Down with HTML + Code Markup!

I'm looking at you, ERb...

div_tag-1.jpg

I just did something in a Rails project that made me very happy -- I wrote a lot of helper methods. I had a whole mess of ERb managing an index page with pagination, sorting, quick search, and so on, all of which was going to need to be more or less duplicated for several controllers.

This promised to be annoying, so I started to push some of the HTML/ERb code up to helper methods with an eye toward reusing them. Eventually, I was able to convert the whole thing to a series of helper methods -- the view page is just a single line calling the top-level helper methods. And I smiled.

A little history. Once upon a time, I wrote my first Java Servlet application, which I think was a news and comments forum. Servlets were brand-new, and JSP's were either not quite out yet, or were unsuitable for some reason. I wrote the entire view layer using a library I wrote that encapsulated HTML tags, like HTMLAnchorTag.new().setReference("http://whatever").append("Click Here").toString();

It was, I admit, absurdly verbose -- you should have seen it when CSS styles were involved... (I had an SQL generating package that was somewhat better), but I liked the the code it produced. In particular, the library was very good for highly structured web pages -- I tended to have aggregated HTML classes or methods correspond to specific objects that were displayed on the pages.

Anyway, in the fullness of time, I switched back to the JSP/Velocity/FreeMarker/ERb/ASP/PHP style where the HTML is interspersed with the display logic, because, let's face it, it's way less verbose than declaring each HTML tag as a Java object. But I've never felt like it's the best way to specify a web view layer.

For one thing, the style encourages long, unfactored code, with logic and markup intertangled. For another, you get really ugly constructs when you have to insert markup inside HTML:

<div id="<% dom_id(@thing) %>">

That %>"> structure drives me batty -- I always think it's a typo.

Plus I never know how to indent things -- the HTML structure and the logical structure clash and the markup tags interrupt the layout as well:

<table>
<tr>
<th>Header</th>
<th>Header></th>
</tr>
<% @items.each do |item| %>
<tr>
<td></td>
</tr>
<% end>

It always bugs me that the different <tr> tags are not lined up. And I never know whether to indent further ERb lines to line up with the <% tag or the actual start of the Ruby code. Petty, sure, but I've always felt that problems laying code out indicate a problem in the logical structure of the code.

So, I'm trying the current experiment, to put as much of the view layer as possible in Ruby code, helpers, and as little as possible as raw HTML and ERb. So far I like it.

Benefits

  • My refactoring impulse kicks in, and I wind up with nice, reusable ten-line helper methods for common HTML structures, such as tables based on ActiveRecord objects
  • Each chunk of code has a logical label -- the name of it's helper method, allowing the code to signal intent.
  • Using content_tag means no more missed end tags.

Costs

  • If you do it wrong, the helper code can be a little tricky to read.
  • It's also potentially less accessible to a CSS designer who doesn't know Ruby.
  • Rails doesn't fully support the style. It'd be nice to have something like render :helper => helper_method(@item) to make it easy to call a one-line helper from the controller. As it is, it's easier to create a one-line .erb file.
  • Also, Rails block helpers like form_for don't really translate well to being called from helper methods, since they typically inject output directly into the ERb stream, rather than returning a string. This makes it awkward to call those helpers from inside other helpers, but it's possible I just haven't found the right magic words yet.

A lot of Rails developers have been trying to find better view structures -- Jay Field's presenter pattern is in the new Advanced Rails Recipes, Marcel Molina was working on something for more object-oriented views -- but nobody seems to have hit on the sweet spot yet. Still looking.


Please check out my book, Professional Ruby on Rails.