Stupid Human Tricks with Ruby on Rails’ RXML, aka XML Builder
Man, I miss the old “Late Night with David Letterman” of the 80’s sometimes…
Alright, to the point. Ruby on Rails makes it extremely easy to generate nicely formed XML from your application, especially if you are simple doing something like the following:
def show
@topic = Topic.find(params[:id])
respond_to do |format|
format.html
format.xml { render
ml => @topic.to_xml }
end
end
If the request is for XML version of the topic, this will take all the attributes of a topic and make them into well formed XML. Nifty, but this doesn’t provide fine-grained control of the XML output which is what I need. RXML templates to the rescue:
def show
@topic = Topic.find(params[:id])
respond_to do |format|
format.html
format.xml
end
end
This is deceptively simpler than the previous code. What is implied is that there are two templates for the action named show, one called show.rhtml and the other called show.rxml. What does show.rxml look like? We’ll get to that in a sec. In my case, I want to serve a template with a different name than “show.rxml”. Here’s my show action:
def show
@topic = Topic.find(params[:id])
respond_to do |format|
format.html
format.xml { render :action => 'oai_record.rxml', :layout => false, :content_type => 'text/xml' }
end
end
Not too hard. OAI stands for Open Archives Initiative and essentially is a standard that I want my topic’s XML to adhere to. This standard is a bit trickier than what you normally see as an example of how to use RXML. I thought I would step through how I made it do what I wanted to provide future Rails/RXML explorers some insight.
RXML in RoR uses a Ruby library called Builder (see http://builder.rubyforge.org/ for documentation). It can be extremely simple to use. In your RXML template you are given a magic xml object to operate on, like so:
This will result in this being rendered:
You can also do nested XML tags via blocks:
xml.a_nested_element_name(”nested value”) do
end
Which looks like this:
<an_element_name an_xml_attribute="attribute value" >
<a_nested_element_name>nested value</a_nested_element_name>
</an_element_name>
This is pretty darn useful in itself. You have element nesting and attributes and you are working in Ruby. Wicked. Builder uses a “method_missing” trick to take what should be a method call on the xml object as what the element name should be.
What if my element name includes a colon to describe a XML namespace? If you take a look at the documentation for Builder you will see that method is interpreted as a ruby symbol by Builder and thus it will blowup. Oh no! Enter the Builder tag! method like so:
This takes the same arguments as the method missing version, but it’s first argument is the name of the element/tag, the second is the value, etc. It also handles element attributes and nesting in the same manner, for example:
xml.tag!("OAI-PMH", "xmlns:xsi".to_sym => "http://www.w3.org/2001/XMLSchema-instance", "xmlns".to_sym => "http://www.openarchives.or\g/OAI/2.0/", "xsi:schemaLocation".to_sym => "http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd") do
...
end
Will output this:
<OAI-PMH xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.openarchives.org/OAI/2.0/" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/ http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
...
</OAI-PMH>
Notice the “xmlns:xsi”.to_sym? Without the to_sym method, “xmlns:xsi” would be interpreted as a string and thus break ( remember the :symbol_for_attribute_name => “attribute value” syntax?)
What if you want to specify attributes and a value for an element?
Notice that the value of the element argument comes before anything that is defined with a symbol. The same goes for the tag! version, except it’s positioned over one more to the right. So it would look like this:
Those were the killers for me. I also found xml.instruct! useful for the beginning xml declaration. xml.text! looks handy, too.
Hope this post saves you some time.
Cheers,
Walter


December 5th, 2006 at 2:44 pm
One last thing that I forgot to mention and that I used is the “||” operator for values of the XML elements if the first value isn’t found. For example:
xml.tag!(”dc:description”, params[:short_summary] || @topic.short_summary)
December 5th, 2006 at 2:46 pm
Here’s another tutorial about builder:
http://www.xml.com/pub/a/2006/01/04/creating-xml-with-ruby-and-builder.html
Cheers,
Walter
September 16th, 2008 at 8:57 am
[...] Thanks to http://blog.katipo.co.nz/?p=29 for the tips on Builder, especially the bit about using tag! to put in names which would translate to Ruby keywords (e.g. start-date where the – makes it start – date otherwise) [...]