entries friends calendar user info My Website Previous Previous Next Next
Anthony Bailey's blog - Slim Builder - an Untangled Template View
anthonybailey
[info]anthonybailey
Add to Memories
Tell a Friend
Slim Builder - an Untangled Template View

I'd like to draw attention to a pleasant way to implement the view in MVC web apps that is emerging in suitably reflective and dynamic frameworks. It doesn't seem to have a name yet - I'd suggest "slim builder" and/or "untangled template view".

The technique combines the strengths of two traditionally distinct approaches - builders, and server page templates - and thereby solves some of their usual problems. I'll illustrate it through an example drawn from Rails, although you don't need to know the details of this framework or the underlying Ruby language to get the point.

If you do know Rails and its canon then the example will be very familiar: it's the bookstore index page from the "depot" tutorial in Agile Web Development with Rails, as of the end of section B.

(As context for the rest of you, here's how the page renders once its style is wired up. It's simple, but will suffice for our purposes. The approaches described all extend smoothly to more complicated layouts, Ajax updates, and so forth.)

Templates - mark-up and code entangled in a server page

The traditional template view works by embedding executable code fragments into concrete mark-up. The default Rails view system uses rhtml templates processed by ERb.

Here's how we'd implement the store page view. In the tutorial - as in real Rails apps - the page gets split into different modular parts (layouts, partials, etc.) For the sake of this example I'll inline everything into one template:

<?xhtml version="1.0" encoding="UTF-8"?>
<html>
<head>
  <title>Pragprog Books Online Store</title>
  <%= stylesheet_link_tag "depot", :media => "all" %>
</head>
<body id="store">
  <div id="banner">
    <img src="/images/logo.png" alt="Logo"/>
    <%= @page_title || "Pragmatic Bookshelf" %>
  </div>
  <div id="columns">
    <div id="side">
      <a href="http://www....">Home</a><br />
      <a href="http://www..../faq">Questions</a><br />
      <a href="http://www..../news">News</a><br />
      <a href="http://www..../contact">Contact</a><br />
    </div>
    <div id="main">
      <h1>Your Pragmatic Store</h1>
      <% for product in @products -%>
        <div class="entry">
          <img src="<%= product.image_url %>" alt="Cover" />
          <h3><%= h(product.title) %></h3>
          <%= product.description %>
          <span class="price"><%= number_to_currency(product.price) %></span>
          <%= button_to "Add to Cart", :action => :add_to_cart, :id => product %>
        </div>
      <% end %>
    </div>
  </div>
</body>
</html>

To my eyes, this kind of JSP-inspired template is rather ugly - it is the dirtiest plate in the default Rails stack. Angle-bracketed web mark-up is not pretty to start with, and mixing code into it under the protection of even more punctuation does not help matters, forming uglies such as <%, <%=, and -%>. Further, the resulting template is neither fish nor flesh; it isn't validating mark-up, nor is it directly compilable code. Two worlds have been messily ent-angled.

To be fair, if you need to directly mix the two, then having the mark-up around the code is probably a better choice than the converse. Templates rightly ousted sequences of print statements that cobbled together strings of explicit mark-up. Traditional entangled server page templates have a sufficient similarity to straight HTML that tag-aware tools will do a decent job of pretty-printing and coloring them. Similarly tag-aware but non-programming web folk can work with the template directly, especially when helper objects have been used to minimize the size of the scary code fragments and tuck the details safely away.

Still, it's messy. Please do better.

Builders - code that casts a mark-up shadow

When I first ran across the Template View pattern in Martin Fowler's Patterns of Enterprise Application Architecture I imagined it to cover exactly the case above and no more; template views were server pages.

But a refinement of the "cobbling" procedure described above brings us to the builder, which inverts the server page relationship between the tags and the code. By encapsulating all the opening and closing of tags within helper methods, we can hide the actual mark-up whilst persisting its structure in that of the program that knows how to build it, so that the code itself looks something like a template. Fowler's account of the view template shows he considers builders to be a valid variant; the veteran builder from those days was Perl's CGI.pm, which is referenced (albeit briefly) in the pattern.

The CGI.pm builder brews tag soup by recursively nesting expressions each of which evaluate to strings. A set of helper methods that are named after the tags they build take a hash of options followed by further string-returning expressions to concatenate together and enclose. Glossing over a few Perl/Rails gaps, here's how we can get CGI.pm to build up and print an expression that evaluates to the store page.

#!/usr/local/bin/perl
use CGI ':standard';

print(header,
  start_html("Pragprog Books Online Store"),
             # I couldn't figure out how to supply the stylesheet
  div({-id=>'banner'},
    img({-src=>"http://localhost:3000/images/logo.png", -alt=>"Logo"}),
    $page_title || "Pragmatic Bookshelf"
  ),
  div({-id=>'columns'},
    div({-id=>'side'},
      a({-href=>"http://www...."}, "Home"), br,
      a({-href=>"http://www..../faq"}, "Questions"), br,
      a({-href=>"http://www..../news"}, "News"), br,
      a({-href=>"http://www..../contact"}, "Contact"), br
    ),
    div({-id=>'main'},
      h1("Your Pragmatic Store"),
      join '', map {
        $product = $_;
        div({-id=>entry},
          img({-src=>$product->{image_url}, -alt=>"Cover"},
            h3($product->{title}),
            $product->{description},
            span({-id=>'price'}, number_to_currency($product->{price})),
            button({-action=>"add_to_cart/$product->{id}"}, "Add to Cart")
          )
        )
      } (@products)
    )
  ),
  end_html
);

This builder is very Perlish. It achieves high-level brevity at the cost of some low-level heavy punctuation, as well as dragging its helpers into the global scope. We got to use some semi-functional programming cuteness to iterate over the products within the innards of a huge expression. This is charming if, like me, you have an old affection for the language; but even if you find Perl distasteful I hope you'll agree CGI.pm seems to have hit upon a good idea.

The Ruby version most directly descended from CGI.pm is XmlBuilder, and it's the default builder within Rails. One change in approach is that rather than building a page as one big expression that nests method calls and string concatenation, instead we let the building occur as a side effect and hence write a regular sequential program with guards and iterators.

Here's the rxml template to get Rails to render the store page:

xml.instruct! :xhtml, :version=>"1.0", :encoding=>"UTF-8"
xml.html do
xml.head do
  xml.title("Pragprog Books Online Store")
  xml<<(stylesheet_link_tag "depot", :media => "all")
end
xml.body :id=>"store" do
  xml.div :id=>"banner" do
    xml.img :src=>"/images/logo.png", :alt=>"Logo"
    xml.text!(@page_title || "Pragmatic Bookshelf")
  end
  xml.div :id=> "columns" do
    xml.div :id=>"side" do
      xml.a("Home", :href=>"http://www...."); xml.br
      xml.a("Questions", :href=>"http://www..../faq"); xml.br
      xml.a("News", :href=>"http://www..../news"); xml.br
      xml.a("Contact", :href=>"http://www..../contact"); xml.br
    end
    xml.div :id=>"main" do
      xml.h1("Your Pragmatic Store")
      @products.each do |product|
        xml.div :class=>"entry" do
          xml.img :src=>product.image_url, :alt=>"Cover"
          xml.h3(product.title)
          xml<<(product.description)
          xml.span(number_to_currency(product.price), :class=>"price")
          xml<<(button_to "Add to Cart", :action => :add_to_cart, :id => product)
        end
      end
    end
  end
end
end

This is looking OK, but it isn't nice enough to move me from rhtml - it's still a bit hard to read, just from a different angle. We haven't yet reached a DSL for writing pages - web people probably couldn't work with this directly. My DRY eyes are offended by some remaining duplication. What with introducing the xml builder object to scope its helpers, this is actually more verbose as the server page. And, there's too much punctuation (although I confess I've not suppressed everything I could - I'm leaving some tricks for our big finish...)

Still too massive. Let there be light.

Slim builder untangles the template

What happens if we throw further Ruby magic at the problem so as to slim down the builder?

Markaby happens:

xhtml_strict {
head {
  title "Pragprog Books Online Store" 
  stylesheet_link_tag "depot", :media => "all"
}
body.store! {
  div.banner! {
    img :src=>"/images/logo.png", :alt=>"Logo"
    text @page_title || "Pragmatic Bookshelf"
  }
  div.columns! {
    div.side! {
      a "Home", :href=>"http://www...."; br
      a "Questions", :href=>"http://www..../faq"; br
      a "News", :href=>"http://www..../news"; br
      a "Contact", :href=>"http://www..../contact"; br
    }
    div.main! {
      h1 "Your Pragmatic Store"
      @products.each do |product|
        div.entry {
          img :src=>product.image_url, :alt=>"Cover"
          h3 product.title
          text product.description
          span.price number_to_currency(product.price)
          button_to "Add to Cart", :action => :add_to_cart, :id => product
        }
      end
    }
  }
}
}

Markaby is "mark-up as Ruby." It's real pretty.

Punctuation noise is minimal. As a matter of style we've used curlies rather than "do ... end" for the tag blocks, and managed to suppress almost every bracket. This is about as clean as the structure representation could be whilst remaining executable code. (HAML gets briefer still, including Pythonesque semantic indentation, but it only manages this through going back to the being a template that needs pre-processing - using a better rhtml like this is an interesting alternative, though.)

There's much handling of method_missing so that method invocations can be used to build the basic CSS classes and ids without needing hashes. (Like many builders Markaby is also using further reflection to more cheaply create the tag helper methods that these modify.)

The builder is made implicit again by using instance_eval to carry the object's context through to its inner blocks - this avoids scope pollution without incurring a character tax. (XmlBuilder used to turn this trick too, but because it builds general XML the chance of confusion was considered too high and the technique was retired. As an HTML builder Markaby suffers less, and we also get to enforce a restricted builder context so as to control which models can be referenced by the view.)

The code flow has been designed so that natural idioms tend to do the right thing; appropriate encodings are implicitly applied to appropriate string arguments. The inner details can be mysterious, but why would you expect helpful magic to be otherwise?

Talking of doing the right thing by default... Markaby was the only one of the builders I tried that generated completely (i.e. W3C) validating mark-up out the box without any tuning. Further, it also complains if you attempt to build such things as elements with duplicate ids, or tags that aren't allowed to self-close - this validation is a nice benefit of builders. Once armed with a decent Ruby-aware editor working in Markaby can provide plenty of warning of mistakes in either code or tags - the best of both worlds, keeping programmer and web designer happy.

Conclusion

I think this is the right approach for view building. To repeat my proposed terminologies:

  • It's an untangled template view - all the flexibility of your regular server page view template, but less of the mess. We've disentangled the template by collapsing mark-up into the code and outlawed all distressing pointy brackets.
  • And it's a slim builder - all the power of your regular builder but less of the mass. The result is a DSL for authoring web pages.

What are the slimmest builders in other languages and frameworks?

Tags: ,
Current Mood: impressed

Comments
cairmen From: [info]cairmen Date: February 8th, 2007 03:04 pm (UTC) (Link)
Hmm, interesting.

I'm not sure that I'm feelin' the improvement over the messy code-inside-markup version in terms of practical productivity improvements, but I may just be being dense. It's a lot more programming-languagey, but that's not as much of an advantage for me. I'm also not wild about having to learn new ways to write HTML. How does it cope with wierd A List Apart-style CSS hacks to do odd things? (F'r example, how well would it work with the Machinima.com frontpage?)

OTOH, the division into div tags is very clear, which is way cool.

Can you explain a little more of why you would expect this to be helpful in an on-the-ground, day-to-day context?

(And please forgive the wankiness of that last sentence. I'm surprised my next one wasn't something like "toward a dynamic win-win synergy on a going forward basis at this moment in time".)
anthonybailey From: [info]anthonybailey Date: February 9th, 2007 12:52 am (UTC) (Link)
Mostly this is about appealing to a laudable contemporary developer aesthetic. I didn't mean to imply a productivity boost over and above what you get when you make a system more maintainable and beautiful by removing duplication and ugliness, although I did mention some incidental validation / tool support advantages toward the end.

I guess I hope that the slim builder might when necessary at least be as easy for web folk to tweak and edit as a server page is - i.e. it doesn't throw this baby benefit of the traditional tangled template out with the angle brackets. Could be wrong, though.

No style tricks will be harmed in the process of this translation. You don't have to do anything different in the stylesheet where all your CSS continues to live. The only change re CSS is how you specify class and id in the template, and the syntax for this is clean and simple: tag.foo.bar.baz! { "Content" } in your mab will produce <tag class="foo bar" id="baz">Content</tag> in your mark-up.
From: (Anonymous) Date: February 15th, 2007 02:06 pm (UTC) (Link)

Very Clean

That last template does indeed look very clean. I also like the idea of doing everything in the one language - Rake and RJS are testament to the power of that approach.

My only concern is the performance penalty of all these reflective method calls on moderately complex page. I mention this because I hit performance issues using the Rails XmlBuilder in the past. I ended up using rhtml to template the XML output so that static portions of the output markup could be generated without overhead - the difference in speed and memory footprint were staggering.

Interesting article,
-Richie
anthonybailey From: [info]anthonybailey Date: February 16th, 2007 10:18 pm (UTC) (Link)
There is indeed a performance penalty. Running in the development environment and checking the logs (nowhere near as good a test as a properly benchmarked production run, I realize), then on the simple example I presented above, my rxml and mab renders take twice as long as my rhtml ones. (Markaby uses XmlBuilder under the hood.)

In a real app that I converted from rhtml to mab, the mab takes four or five times as long. But the page render latency is still small, and it isn't an app where any real load is expected, so I haven't looked into where the performance can be tweaked.

From: (Anonymous) Date: November 28th, 2007 10:13 pm (UTC) (Link)

naisioxerloro

Hi.
Good design, who make it?
5 comments or Leave a comment
about this journal
License:Public Domain Dedication
Feed:RSS feed
Contact details
Blog - permalink
Tumblelog - Anthony uncut
My views, not Amazon's
tags
page summary
[info]cairmen (no subject) [+1]
Anonymous Very Clean [+1]
Anonymous naisioxerloro