[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 startElement
and 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
looked like
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 CGI
module does this; check the first-edition text
here
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
html
and body
and p
are defined,
but some aren’t; they’re caught by missing_method
, which
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
define_method
and send
voodoo, but you’d rather
not).
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:
A per-doc Namespaces
thingie that you (optionally) use to declare namespace prefixes.
An 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.
An element
method that takes a namespace URI, a
local-part, an AttList
; and a body that fills in the
content.
A text
method that takes care of escaping and
output.
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.