September 12, 2003

Generating XML in Ruby

I have been running my weblog on Movable Type for about a month now and I have to say I am really impressed.  For a collection of scripts put together, Movable Type is an impressive piece of software, both powerful and intuitive.  I expected it to be a challenging installation, especially since I am not running my blog on my home machine but at an ISP, but it turned out to be remarkably painless.

Having said that, I have one big complaint:  no support for referrer logs.  I couldn't find any way to have quickly access to my referrer log anywhere in the Movable Type distribution.  A quick Google query turned up several packages implemented in various languages.  I tried a lot of them but I could never quite reach the result I was looking for, so I decided to write my own.

My ISP conveniently stores the logs for my Web site every night in a well-defined directory, following a standard naming notation for each day.  I decided it would be easier to calculate my log referrer from these logs instead of embedding scripting information in my main index file, since the updates don't really need to be more frequent than once a day.

Finally, I had to choose a language.  Since I opted for the static approach, I am not limited to the languages that my ISP supports for CGI programming (PHP and Perl).  The obvious choice was Ruby, which excels at this kind of treatment with its native support for regular expressions, invocation of external commands and offers an object-oriented language from the ground up giving me extreme flexibility in my attempt to write a utility that will be easy to extend for my future log parsing needs.

Since I was going to have to generate HTML, I thought I would port a small Java class that I have been using to generate XML in EJBGen called XMLStringBuffer.  The idea is simply to not have to worry about indentation and closing the tags.  With this class, generating XML is as simple as:

XMLStringBuffer xsb = new XMLStringBuffer();
xsb.addRequired("last-name", m_lastName);
xsb.addOptional("first-name", m_firstName);

Note that I don't really need to specify the closing tag in the pop() call, but it makes debugging easier since the XMLStringBuffer maintains an internal stack of the tags and can therefore tell me right away if my push/pop get out of synch.

It quickly occurred to me that I could make this class even fancier in Ruby thanks to two features that are sadly absent from Java:  closures and method_missing (really dynamic typing).

The idea is to use closures to simulate indentation, and method_missing to make the XML class allow invocations on any method.  If the said method is unknown, it is simply turned into an XML tag.

Here is a piece of code that will make it all clearer:

xml =

xml.html {
  xml.head {
  xml.body {
    xml.table { {{ "valign" => "top"}, "Content1"){
        } {

As you can see, each new closure (pairs of { }) starts a new tag and will cause an indentation and the proper tag to be closed when the block is exited.  Note also that every tag can be passed a Hash that will be turned into attributes if found.  You can also specify the content of the tag either inline or later in the closure with the append() method.  The generated XML is as follows:

        <td valign="top">Content1</td>

The XML class is about forty lines, including comments.

In a next entry, I will give more details about the logging utility itself.

Posted by cedric at September 12, 2003 04:19 PM

Hi - I was looking for some political sites with articles on the recent US election and found your nice site. The comments from others on here are pretty good so I just thought I'd add my thoughts also!

Elaine Cooper

Posted by: jennifer anniston zone diet success at November 4, 2004 01:11 PM

Cedric, This looks really cool. Any chance you could post those forty lines (ie the source)?

Posted by: Darrin at August 22, 2005 08:15 AM

I like the looks of the code. I found what would appear to be the code here:
(The comment handler won't let me post it as a link.)

The trouble is, I can't seem to find a way to download it. When I clicked the download button, it said I needed to install a BitTorrent client. So I did. But refreshing the page didn't cause any negotiation with the client, and there isn't any link I can find that I can manually paste into the client.

So I'm stuck. Maybe someone else who finds this will have better luck. (eric [dot] armstrong [at] sun [dot] com)

Posted by: Eric Armstrong at January 1, 2009 10:46 PM

Here's another site that purports to let you download the code. I don't see any link on it, though. Perhaps I need to log in? (If that's a requirement for downloading, as opposed to posting, it would be nice if these sites said so.)

Posted by: Eric Armstrong at January 1, 2009 10:52 PM

Ok. Never mind. That last site is the good one. Nothing to download. It just shows you the darn snippet, so you can copy it:

Posted by: Eric Armstrong at January 1, 2009 10:58 PM
Post a comment

Remember personal info?