[LRUG] DSLs for newbies: HTML generation (discuss)

Daniel Lucraft danlucraft at me.com
Thu Dec 10 06:25:41 PST 2009


As you can see from the suggested reading material, it's not a dumb idea at all :). Your implementation is impressively concise. The only style point I would make is to ask you not to use @@class_variables. Use class instance variables instead. (And in this case a constant might make more sense anyway.)

I would love to use something like this, but of course it's a heck of a barrier to hiring new markup/css devs.

Dan

___________________________________
Daniel Lucraft

twitter.com/danlucraft
danlucraft.com/blog




On 10 Dec 2009, at 14:19, Roland Swingler wrote:

> or erector
> 
> http://erector.rubyforge.org
> 
> On Thu, Dec 10, 2009 at 2:17 PM, Glenn Gillen <glenn at rubypond.com> wrote:
>> Or Markaby:
>> 
>> http://markaby.rubyforge.org/
>> 
>> On 10 Dec 2009, at 13:56, Alex Graul wrote:
>> 
>>> That's very close to the syntax of Builder, see http://builder.rubyforge.org/
>>> 
>>> Cheers,
>>> Alex
>>> 
>>> On 10 Dec 2009, at 13:49, Daniel Barlow wrote:
>>> 
>>>> I'm playing with Ruby for the first time (pretty much) and having seen haml I thought it would be fun to play with alternate syntaxes.  This one has much less in the way of funny characters (% and #) and isn't whitespace-sensitive
>>>> 
>>>> It's probably also a really dumb idea.  Like I say, first time Ruby programmer.  Anyway, here's a motivating example of its use
>>>> 
>>>> h=HTML.new
>>>> def h.content
>>>> html do
>>>>   head { title(:id=>123) {"My page title" }}
>>>>   body do
>>>>     div do
>>>>       h1(:class => "fancy_formatted") {"hello world"}
>>>>       text "some stuff","more stuff"
>>>>       ul {
>>>>         %w(red orange yellow green blue indigo violent).map {|name|
>>>>           li { text name }
>>>>         }
>>>>       }
>>>>     end
>>>>   end
>>>> end
>>>> end
>>>> h.output
>>>> 
>>>> It's all valid Ruby code.  There is a method (implemented with method_missing) for each HTML element: when called it expects HTML arguments as attributes and a block of element content: it outputs the markup for the start-tag/end-tag and calls the block.
>>>> 
>>>> Inside the block you can call more element-making methods, and/or you can call #text (as shown) to output plain text, and/or you can return some (preferably string) value which will also be output as if by #text
>>>> 
>>>> So,
>>>> - a neat hack?
>>>> - an offence against (your choice of) god?
>>>> - dull and unoriginal and every other newbie did exactly the same thing when learning?
>>>> - really ugly ruby style?
>>>> 
>>>> All criticism welcome.  I'm a Lisp programmer in my day job, so I've almost certainly heard worse.
>>>> 
>>>> Oh, the implementation?  The HTML it generates is not entirely valid (attribute quoting and empty elements are two obvious omissions: introducing all that whitespace, I hazily remember from reading SGML specs back in the day, is probably also wrong) and indenting is hacky, but you get the gist.  It's more about proof-of-concept and playing with the DSL syntax at this stage than production-quality output
>>>> 
>>>> Is Hash.map supposed to work like that, or is it accidental?  It's dashed useful, that I will say
>>>> 
>>>> ---cut here---
>>>> class HTML
>>>> # this is a partial list for testing, and obviously needs to
>>>> # be extending to all tags in whatever version of HTML you want
>>>> # to produce
>>>> @@allowed_tags=%w(html head title body h1 h2 h3 h4 h5 h6
>>>>                   p div span ul li).map {|n| n.to_sym}
>>>> 
>>>> def texts(stuff)
>>>>   stuff and
>>>>     stuff.each {|x| x and @content << ("\n"+(" " * @indent)+x) }
>>>>   nil
>>>> end
>>>> 
>>>> def text(*stuff)
>>>>   texts stuff
>>>> end
>>>> 
>>>> def method_missing(name,*args,&body)
>>>>   if @@allowed_tags.member?(name)
>>>>     attributes = args[0] || [];
>>>>     text "<#{name}"+attributes.map {|k,v| " "+k.to_s+"="+v.to_s }.to_s + ">"
>>>>     @indent=@indent+4;
>>>>     texts body.call
>>>>     @indent=@indent-4;
>>>>     text "</#{name}>"
>>>>   else
>>>>     super # not on our list, let it raise UndefinedMethodError
>>>>   end
>>>> end
>>>> 
>>>> def output
>>>>   @content=[]
>>>>   @indent=0
>>>>   content
>>>>   print @content
>>>>   puts
>>>> end
>>>> end
>>>> ---cut here---
>>>> _______________________________________________
>>>> Chat mailing list
>>>> Chat at lists.lrug.org
>>>> http://lists.lrug.org/listinfo.cgi/chat-lrug.org
>>> 
>>> _______________________________________________
>>> Chat mailing list
>>> Chat at lists.lrug.org
>>> http://lists.lrug.org/listinfo.cgi/chat-lrug.org
>> 
>> _______________________________________________
>> Chat mailing list
>> Chat at lists.lrug.org
>> http://lists.lrug.org/listinfo.cgi/chat-lrug.org
>> 
> _______________________________________________
> Chat mailing list
> Chat at lists.lrug.org
> http://lists.lrug.org/listinfo.cgi/chat-lrug.org




More information about the Chat mailing list