Creating XML with Ruby and Builder
January 4, 2006
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.