Tue 23 Aug 2011
Weapon of Choice
Having the right tool for the job is incredibly important. As an amateur mechanic, I more often than not have to use a sub-optimal set of tools, making my job much more difficult. Fortunately in the software world, particularly in JVM land, we have an abundance of tools and technologies that allow us to solve problems in a variety of ways. With the ever increasing popularity of JVM languages such as Scala, Clojure, JRuby etc. there really are a ton of options.
But who gets to use these languages in production? Are these languages reserved for small, risk tolerant startups who have the luxury of starting on that blank IDE screen? Or can these languages be used to augment an aging system?
During our last Hackathon, my co-worker Adam “**GlitterPants**” Esterline, asked me if I’d like to help him remove a bunch of duplication from the pop up editors that we had been working on for the last quarter. As I love killing off duplication, I of course accepted.
Our weapon of choice was JRuby. Our target was a bunch of XML that we used to describe the structure and rendering of our detail pages and editors. JRuby was a natural fit given its flexible syntax. Our goal was to reduce duplication and complexity, but not at the cost of readability.
The duplication was rampant, which is somewhat typical for XML. We felt that a good first step would be for us to generate the XML from JRuby and have our existing framework handle the XML. This incremental approach had several benefits. First of all writing tests for this code would be brain dead simple. We would assert that our generated XML was semantically equivalent to the existing file rather than actually dealing with the page itself. This would also mean that our JRuby code ran outside of the request cycle. Our code would only generate the XML the first time it was requested which would be upon boot of the JVM. The XML is then cached and the system fetches it from there for subsequent requests. This overall seemed like a useful and safe way to introduce a new JVM language into our production stack.
So how did this all turn out? Well here is a snippet of the orignal User Story editor/detail page as defined by our XML and the corresponding JRuby DSL. It is immediately apparent that the JRuby is much more readable in addition to being more concise. This XML block was actually duplicated in our codebase with the only difference being which JSP would render the fields. So we were able to replace both blocks of XML which looked similar to this:
<section cls="section" resource-key="general" uri="/WEB-INF/jsp/content/item/defaultSectionRenderer.jsp">
<row>
<field property-name="formattedID" resource-key="formattedID">
<field-label/>
<field-renderer/>
</field>
</row>
<row>
<field property-name="name" resource-key="name">
<field-label/>
<field-renderer uri="/WEB-INF/jsp/content/item/edit/textbox.jsp">
<tps:attribute name="maxLength" value="256"/>
</field-renderer>
</field>
</row>
<row>
<field property-name="tags" resource-key="tags">
<field-label />
<field-renderer uri="/WEB-INF/jsp/content/item/edit/artifact/tagRenderer.jsp" />
</field>
</row>
<row>
<field property-name="description" resource-key="description">
<field-label/>
<field-renderer uri="/WEB-INF/jsp/content/item/edit/editor.jsp">
<tps:attribute name="className" value="userStory" />
<tps:attribute name="height" value="190px"/>
</field-renderer>
</field>
</row>
<row>
<field property-name="attachment" resource-key="attachment">
<field-label/>
<field-renderer uri="/WEB-INF/jsp/content/item/edit/statelessEditAttachmentForm.jsp">
<tps:attribute name="className" value="userStory" />
</field-renderer>
</field>
</row>
<row>
<field property-name="owner" resource-key="owner">
<field-label/>
<field-renderer uri="/WEB-INF/jsp/content/item/edit/select.jsp">
<tps:edit-option-map name="optionMap"
value="com.rallydev.domain.option.UserOptionMap"/>
</field-renderer>
</field>
<field property-name="category" resource-key="category">
<field-label/>
<field-renderer uri="/WEB-INF/jsp/content/item/edit/select.jsp">
<tps:edit-option-map name="optionMap"
value="com.rallydev.domain.option.CategoryOptionMap"/>
</field-renderer>
</field>
</row>
<row>
<field property-name="project" resource-key="project">
<field-label/>
<field-renderer uri="/WEB-INF/jsp/content/item/edit/schedulableagileworkproduct/project.jsp">
<tps:edit-option-map name="optionMap"
value="com.rallydev.domain.option.NonClosedProjectOptionMap"/>
</field-renderer>
</field>
<pns:predicate predicate-class="com.f4tech.slm.application.portal.predicate.MultiProjectWorkspacePredicate"/>
</row>
</section>
With this block of JRuby code defining the structure:
section(:general) do
row { fields(:formatted_i_d) }
row { fields(:name) }
row { fields(:tags) }
row { fields(:description) }
row { fields(:attachment) }
row { fields(:owner, :category) }
row(:predicate => :project_row) { fields(:project) }
And this block of code defining how to render it in a given situation:
render(:formatted_i_d) { using_the_default_renderer }
render(:name, :when => :editing) { using("~/edit/textbox.jsp", "maxLength" => 256) }
render(:tags, :when => :editing) { using("~/edit/artifact/tagRenderer.jsp") }
render(:tags, :when => :viewing) { using("~/view/tagRenderer.jsp") }
render(:description, :when => :editing) { using_a_rich_text_editor(@item_type, :height => "190px") }
render(:description, :notes, :when => :viewing) { using("/WEB-INF/jsp/content/tps/richTextCellRenderer.jsp") }
render(:attachment, :when => :editing) { using("~/edit/statelessEditAttachmentForm.jsp", "className" => @item_type) }
render(:attachment, :when => :viewing) { using("~/viewAttachment.jsp") }
render(:owner, :when => :editing) { using_options_from("UserOptionMap") }
render(:owner, :when => :viewing) { using("/WEB-INF/jsp/content/tps/userDisplay.jsp") }
render(:category, :when => :editing) { using_options_from("CategoryOptionMap") }
render(:project, :when => :editing) { using_options_from("NonClosedProjectOptionMap", "~/edit/schedulableagileworkproduct/project.jsp") }
render(:project, :when => :viewing) { using("~/project.jsp") }

You guys are replacing the render xml stuff with JRuby? That’s awesome, nice work. It was always a DSL, so now it’s a DSL in a DSL language instead of in XML.
Those rendering descriptions are so much more readable now. The old XML code makes me want to claw my eyes out… with a rusty spoon.
I love it when hackathon week finishes up and we have demonstrations from everyone. So many smart ideas and cool technologies.
Good job!