Thursday 29 October 2009

Ruby XML Builder prefixes

This is a topic I've come across a few times now, and more recently turned up when someone asked a question in an IRC channel (it was either #ruby or #rails on irc.freenode.net, I can't quite remember which now). The basic problem was that they wanted to output an XML prefix to the tags generated with Builder. Anyone who has used Builder will be familiar with it's syntax:
xml.someTag do 
xml.anotherTag "tag content"
end
and can see that this doesn't work with a tag prefixed with an xml namespace, which includes a : (this being a special character in ruby). So what is the solution? The person asking the question actually got the answer from several people that it wasn't possible, but that didn't seem right. And it turns out that it is perfectly possible, it just requires a slightly more verbose syntax. Instead of
xml.someTag
you need to do
xml.tag! "somePrefix:someTag"
where the tag! function on an XML Builder object takes a string representing the entire tag and outputs it as is.

It turns out the consideration of the Builder creators didn't stop there. They have functions to allow for the full range of standard XML to be created. Need a CDATA field? use xml.cdata!, need to add, comments? Use xml.comment!, need to create a node with mixed text and child nodes? Use xml.text! like so:
xml.myNode do
xml.myChildNode "awesome"
xml.text! "More awesome"
end
All of these are supported with functions with a ! at the end, which keeps them nice and separate from tags that you would typically create.

And to really add to the point that these things were considered by the Builder creators, they even have a simpler form for using prefixes in XML now. You simply add a space between the prefix and the : to create an expression like:
xml.myPrefix :myElement, "look at this!"

or in more common ruby language, if you pass a symbol as the first argument when creating a tag, it will take the tag as a namespace prefix and the symbol as the actual tag name.

So yes, it is possible to add prefixes with Builder, and more than possible, it's simple! There's no excuse for saying it can't be done.