From a C++ point of view, i.e. the statically typed world with no “dynamic” features that deserved the name, I guess you would all agree that languages like Groovy or Ruby are truly something completely different. Having strong C++ roots myself, my first Grails project gave me lots of eye openers on some nice “dynamic” possibilities. One of the pretty cool things I encountered there was the MarkupBuilder. With it you can just write XML as if it where normal Groovy Code. Simple and just downright awesome.
The other day in yet another C++ project I was again faced with the task to generate some XML from text file. And, sure enough, my thoughts wandered to the good days in the Grails project where I could just instantiate the MarkupBuilder… But wait! I remembered that a colleague had already done some scripting stuff with Ruby, so the language was already kind of introduced into the project. And despite the fact that it was a new language for him he did some heavy lifting with it in just no time (That sure does not come as a big surprise all you Ruby folks out there).
So if Ruby is such a cool language there must be something like a markup builder in it, right? Yes there is, well, sort of. Unfortunately, it’s not part of the language package and you first have to install a thing called gems to even install the XML builder package. Being in a project with tight guidelines when it comes to external dependencies and counting in the fact that we had no patience to first having to learn what Ruby gems even are, my colleague and I decided to hack our own small XML builder (and of course, just for the fun of it). I mean hey, it’s Ruby, everything is supposed to be easy in Ruby.
Damn right it is! Here is what we came up with in what was maybe an hour or so:
class XmlGen def initialize @xmlString = "" @indentStack = Array.new end def method_missing(tagId, attr = {}) argList = attr.map { |key, value| "#{key}=\"#{value}\"" }.reverse.join(' ') @xmlString << @indentStack.join('') @xmlString << "<" << tagId.to_s << " " << argList if block_given? @xmlString << ">\n" @indentStack.push "\t" yield @indentStack.pop @xmlString << @indentStack.join('') << "</" << tagId.to_s << ">\n" else @xmlString << "/>\n" end self end def to_s @xmlString end end
And here is how you can use it:
xml = XmlGen.new xml.FirstXmlTag { xml.SubTagOne( {'attribute1' => 'value1'} ) { someCollection.each { |item| xml.CollectionTag( {'itemId' => item.id} ) } } }
It’s not perfect, it’s not optimized in any way and it may not even be the Ruby way. But hey, it served our needs perfectly, it was a pretty cool Ruby experience, and it sure is not the last piece of Ruby code in this project.
Great to see ruby newcomers picking up the language so fast!
Some tips:
The tagstack isn’t needed. The local variable tagId doesn’t change after the yield.
Using a hash for the attributes gives a nice interface:
def method_missing(tagId, attr = {})
argList = attr.map {|k, v| “k=’#{v}'” }.join(‘ ‘)
…
Better use << instead of += for building strings as the latter creates unnecessary temporary objects.
Thank you for the tips! Changed the code accordingly. For some reason, though, I had to call “reverse” after attr.map to get the right order of the attributes.
I especially like the hash as interface for the attributes. And for me as C++ guy, operator “<<" related to string building also feels very close to home 😉
It’s always nice when a scripting language can save the day 🙂
There’s a de facto library in the Ruby community which does exactly this called Builder:
http://builder.rubyforge.org
In fact, MarkupBuilder is supposed to be a clone of Ruby’s Builder. 😉