Menu

Creating XML with Ruby and Builder

January 4, 2006

Michael Fitzgerald

Since the advent of Ruby on Rails last year, interest in the Ruby programming language seems to have grown steadily. Rails has helped the masses see what Ruby is: an elegant, easy-to-learn, and fun-to-use language, suitable even for industrial-strength applications. Ruby has been around for as long as Java, and is finally getting the attention it deserves.

Ruby certainly has my attention! I am finding myself reaching for Ruby instead of Java these days because it allows me to put together code more quickly—read same job, fewer keystrokes. I doubt that I'd actually give up Java for Ruby entirely, but my Java compiler has been collecting a little dust in recent months. Keep reading and you'll see why.

Builder is a case in point. It's a lightweight XML builder that originally came from the the Rails project. It is now a separate Ruby library that you can download from RubyForge.

This article will walk you through how to install and then create XML documents with Builder, independent of Rails. It won't, of course, cover all the features of Builder, but it will cover enough to get some wind under your wings. Online documentation is available for Builder, if you want all the details.

First You Need to Install Ruby and Builder

This section is for those who don't have Ruby and Builder installed already. Let's start with Ruby. First, click over to the Ruby download page. There you can download the current, stable release, a nightly snapshot, and various Windows releases. If you are on Windows, the easiest way to install Ruby is to use the One-click Ruby Installer (uses version 1.8.2). For other platforms, just use version 1.8.3, a tar'ed and gzip'ed file. (By the way, version 1.8.2 is recommended for Rails, and version 1.8.4 is in preview release.) Test your installation by typing ruby -v at the command line. If Ruby talks back, you're okay; if the OS talks back, you are probably not okay.

By far, the easiest way to install Builder is with RubyGems, a nifty package management program for Ruby. I can't go into much detail here, but I can offer a few pointers. (See the documentation for more details.) Download RubyGems and then run ruby setup.rb. With RubyGems installed, type the following in a shell:

gem install builder

If all goes well, you should see the following response:

Attempting local installation of 'builder'

Local gem file not found: builder*.gem

Attempting remote installation of 'builder'

Updating Gem source index for: http://gems.rubyforge.org

Successfully installed builder-1.2.4

Installing RDoc documentation for builder-1.2.4...

With Ruby and Builder in place, you are ready to go to work.

Let's Have a Look

To get off the ground, I'll show you a few things about Builder in Interactive Ruby or irb. In a shell, invoke irb, turning off standard prompts for readability:

irb --simple-prompt

Now type the following statements (in bold) in irb to create a little XML.

>> require 'builder'

=> ... 

>> x = Builder::XmlMarkup.new(:target => $stdout, :indent => 1)

<inspect/>

=> #<IO:0x279e7e8>

The line starting with require loads (or tries to load) the library named builder. (Normally, if a library is found, this statement should return true.) The next line creates the object x by invoking the new method in XmlMarkup. The :target => stdout argument indicates that the output will be written on standard output, and :indent => 1 means that the XML output will be indented by one space.

By the way, when a name such as :target is preceded by a colon, it means that it is a symbol, or better, an object of the Ruby Symbol class. It stands for the name of the object, whereas, without the colon, it represents the value of the object.

You can use the instance (receiver) x to invoke other methods, such as instruct!:

>> x.instruct!

<?xml version="1.0" encoding="UTF-8"?>

=> #<IO:0x279e7e8>

This generates an XML declaration with a few default pseudo-attributes. The exclamation following the method name indicates generally that the method will modify the receiver in place, or return nil if no changes were made. The next line generates an XML comment:

>> x.comment! "greetings"

<!-- greetings -->

=> #<IO:0x279e7e8>

Notice that the method inserts a space before and after the comment text.

Here is how to create an element. The name following the receiver is also the name of the element and is case-sensitive:

>> x.Hello "World!"

<Hello>World!</Hello>

=> #<IO:0x279e7e8>

Here is a way to write an attribute on the Hello element:

>> x.Hello("World!", "type" => "global")

<Hello type="global">World!</Hello> #<IO:0x279e7e8>

The first argument is the content of the element, and the second renders the attribute type with a value of global.

The last of the irb examples shows you how to place element content within elements. The date element contains three child elements, year, month, and day. The children are created within braces ({}).

>> x.date {

?> x.year "2006"

>> x.month "01"

>> x.day "01"

>> }

<date>

 <year>2006>year>

 <month>01>month>

 <day>01>day>

<date>

=> #>IO:0x279e7e8>

>>

These irb examples cover some of the rudimentary features of writing XML with Builder. The remaining examples will demonstrate Builder with a bit more complexity.

Writing a Hash as XML Markup

Using Builder, the program favs.rb creates a hash called favorites and then writes it out as XML:

#!/usr/bin/ruby



require 'builder'



favorites = {

  'candy' => 'Neccos', 'novel' => 'Empire of the Sun', 'holiday' => 'Easter'

}



xml = Builder::XmlMarkup.new( :target => $stdout, :indent => 2 )



xml.instruct! :xml, :version => "1.1", :encoding => "US-ASCII"



xml.favorites do 

 favorites.each do | name, choice |

  xml.favorite( choice, :item => name )

 end

end

Run it with ruby favs.rb, or just as favs, and the output will be written as:

<?xml version="1.1" encoding="US-ASCII"?>

<favorites>

  <favorite item="candy">Neccos<favorite>

  <favorite item="holiday">Easter<favorite>

  <favorite item="novel">Empire of the Sun<favorite>

<favorites>

The top line of the code hints at where to find the Ruby interpreter (#!/usr/bin/ruby). The Builder library is loaded, and the favorites hash is defined with three name/value pairs. The Builder object xml is instantiated. The instruct! method creates an XML declaration with an explicit target and pseudo-attributes (although the document can pass as XML 1.0, I changed version to 1.1 in the method just to show how it is done).

A block is invoked on favorites (note that you can replace do-end with braces). For each pair in the hash, an element is written using the hash name as an attribute value, and the hash value (read in as the argument choice) as content for the element. As you can guess, using Builder in this way could help conveniently export large numbers of pairs to XML.

Creating a Valid XHTML Document

Finally, I'll touch on a few more methods and techniques for writing a valid XHTML document with Builder. Here is a program (xhtml.rb) that creates the document:

#!/usr/bin/ruby



require 'builder'



x = Builder::XmlMarkup.new(:target => $stdout, :indent => 1)



x.instruct!



x.declare! :DOCTYPE, :html, :PUBLIC, "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"



x.html( "xmlns" => "http://www.w3.org/1999/xhtml" ) { 



 x.head { 



  x.title "XHTML 1.0 example"  



  x.style( "type"=>text/css" ) { x.text! "h1 {font-family:tahoma,sans-serif;font-size:18pt;color:blue} body{font-family:georgia,serif}" 



  }



 }



 x.body {



  x.h1 "Hello from Builder"



  x.p "A Ruby library that facilitates the programatic generation of XML."



  x.p { |y| y <<"Methods of interest from <code<Builder::XmlMarkup</code> }



  x.ul {

    x.li "cdata!"

    x.li "comment!"

    x.li "declare!"

    x.li "instruct!"

    x.li "new"

  }



  x.p{ |y| y << "Methods of interest from "; x.code "Builder::XmlBase"; y << ":" }



  x.ul {

    x.li "<<"

    x.li "new"

    x.li "text!"

  }



 }



}

The declare! method produces a document type declaration for the strict XHTML 1.0 document type definition using symbols and strings. The style element specifies a type attribute and then uses the text! method to write a little CSS. This is an alternative to placing content in the first argument, as shown in this snippet:

x.style("h1 {font-family:tahoma,sans-serif;font-size:18pt;color:blue} body{font-

family:georgia,serif}", "type"=>"text/css" )

Probably the most interesting code are those lines that create paragraphs that have mixed content (embedded code elements with text). Both statements use the block argument |y| and then append text to it using the << method. The first one embeds tags in the text; the second creates the tag with code, separating statements with semicolons.

Builder is one of the most easy-to-use toolsets I've found to generate XML markup on the fly, in any language. Though I've only scratched the surface with some of its possible uses, I think, if you have been playing with the code along the way, you've got the basics down and you're ready to put Builder to work.