[RAD stands for Ruby Ape Diaries, of which this is part X.] If you’re writing Web apps, and even if you’re one of the few who isn’t, you’re probably going to have write code to generate markup, HTML or XML. Historically, programmers have found this difficult, and thus there are lots of libraries that try to abstract markup away behind code (for example, my own Genx). There are tricky issues and trade-offs here, and Ruby throws them into pretty sharp focus.
The Simple Way ·
Generating XML isn’t that tough, you just use print statements and
make sure to escape any magic syntax characters, and make really sure you’re
handling the Unicode issues. Alternatively, you can use
a library like Genx with primitives like
addAttribute and so on.
But this is pretty low-level, and it there’s an obvious tension between the (hierarchical) structure of XML and the (linear) structure of your program.
Templating · Near as I can tell, most of the XML that’s programmatically generated out there in the real world is template-driven. PHP is probably the leader in terms of volume, if only because there are just way too many Java templating systems.
In Ruby-land, Rails’ RHTML seems to be the winner.
Content Blocks ·
Of course, it takes about ten seconds for an XML-savvy programmer to look
at Ruby’s block structure and have the little light go on over her head saying
“Hey, I’ll have a method that takes care of the start tags and end tags, and
the block fills in the content.” For the Ape, I accidentally invented one of
these myself for doing HTML
<li> structures, the first cut
def lip print "<li><p>" yield puts "</p></li>" end
But then I realized you might want to have multiple paragraphs in your list item, and I needed a hook to put in the links to the client/server dialogs, and it grew.
If you read the Pickaxe book, they point out that the
module does this; check the first-edition text
and search for “Creating Forms”.
The Second Edition Pickaxe goes on to say “Although quite interesting, this method of generating HTML is fairly laborious and probably isn’t used much in practice. Most people seem to write the HTML directly, use a templating system, or use an application framework...”
I don’t know, I thought the example code looked OK. For the ultimate expression of this kind of thinking, check out _why’s Markaby; here’s an example:
html do head do title action_name stylesheet_link_tag 'scaffold' end body do p flash[:notice], :style => "color: green" self << @content_for_layout end end
Hmm... there are a couple of non-obvious things going on. The text explains:
As you can see all the normal helpers and variables are present. There is one caveat: in default Markaby, helper methods are automatically output when called. So, in the above, the stylesheet_link_tag gets output. (To turn that off, use @output_helpers = false.)
Do you understand that? I don’t. A few more months of Rubying, though, and I probably would.
Markaby is clever; some of those methods like
p are defined,
but some aren’t; they’re caught by
generates tags based on the method name.
It bothers me though; some of the element methods have built-in
semantics and some don’t. And you can give XML elements names that will
cause you headaches in Ruby, like
<first-step> (yes, you
can in fact have methods with names like that using
send voodoo, but you’d rather
RGenx? · So it occurs that it wouldn’t be that hard to do a completely generalized and kind of elegant XML generator in Ruby. Off the top of my head, I’m thinking of something like this:
Namespaces thingie that you (optionally) use to declare namespace prefixes.
AttList class that builds a hash of attributes you want
on some element; you add attributes to it by providing their namespace URI,
local-part, and value; it takes care of escaping the value.
element method that takes a namespace URI, a
AttList; and a body that fills in the
text method that takes care of escaping and
Like Genx, you’d want to set a target for the output so that the magic of << would let you build things in memory or send ’em down a pipe.
I think that’s all you’d need. In Genx I added all sorts of black magic to allow changing namespace prefixes mid-stream and so on, but that seems very un-Rubyish.
But, does the world need such a thing? Probably not.