→ Mustache 0.9.0

26 March 2010

I'm very happy to announce Mustache 0.9.0, probably the most major release so far. Sinatra 1.0, new parser, bugfixes, and more!

Grab it:

gem install mustache

Or check http://defunkt.github.com/mustache/ for other implementations.

Sinatra 1.0

The release of Sinatra 1.0 changed the way templates are rendered. As such, previous versions of Mustache use an old API and won't work with it.

If you're upgrading from Mustache 0.7.0 or lower please note that the settings have changed very slightly. This diff is an example of one app I upgraded to Sinatra 1.0 + Mustache 0.9 -- pretty painless, and the settings all have better names (finally).

Anyway, Mustache 0.9.0 works great with Sinatra 1.0 and even provides some big improvements:

Caching

The first time a Mustache template is rendered it is compiled into pure Ruby and cached, even if there's no associated view class. Subsequent calls use the cached version which completely bypasses the parsing and generating stages.

Overriding Content in Layouts

Previously there was no easy way to change the layout from a view class. Take this layout.mustache:

And these views:

class Layout < Mustache
  def title
    "layout"
  end
end

class Index < Layout
  def title
    "index"
  end
end

You'd expect it to work, and you'd hope it would work, but it would not. Now it does. Just make sure your views inherit from your layout and everything will be dandy.

Syntax Errors

More syntax errors are caught and the error messages are much more helpful thanks to the new parser (more on that below).

For example, here's a broken template:

And here's what we get trying to tokenize it:

Or take this broken template:

Again, helpful output:

Tag names are a bit more strict as they more closely resemble allowed Ruby method names, with a few exceptions. If you have a big problem with this please file an issue.

New Parser

Magnus Holm has once again torn Mustache's parser apart and rebuilt it into something beautiful. This time it's an entirely new scanner and generator.

Mustache used to loop through a template looking for regular expressions and building up a compiled Ruby string along the way. As a result we had some obscure bugs in nested sections and whatnot. The new parser uses Ruby's StringScanner to intelligently build an array of tokens from a string template.

Using the mustache command line utility we can see exactly what Mustache::Parser returns for a given template:

Pretty neat. Internally this token stream is handed to Mustache::Generator who compiles it into a Ruby string:

$ mustache -c hi.mustache
"#{if v = ctx[:greet?]
    if v == true
      "Hello #{CGI.escapeHTML(ctx[:name].to_s)}!"
    else
      v = [v] unless v.is_a?(Array) # shortcut when passed non-array
      v.map { |h|
        ctx.push(h)
        r = "Hello #{CGI.escapeHTML(ctx[:name].to_s)}!"
        ctx.pop
        r }.join
    end
  end}"

This compiled string is the actual code run in your app to produce HTML output. Fun to look at but mostly useless unless you're familiar with Mustache's internals.

Bugfixes

Recursive partials, nested sections with names matching outer sections, and other bugs are now fixed. Many bugs were fixed by the new parser.

Speedup

When we started to allow Ruby objects to server as section contexts, the implementation slowed down Mustache dramatically. That's been fixed and things are (when compiled and cached) once again speedy.

Mustache 1.0

This release would be 1.0 but there's no Rails integration. That's next on the list - see you then.