↤ February 13th, 2011
Dear Mustache community, users and implementors:
This is a lengthy call to arms; in short: I need your help. Thank you for reading. If you already know all the background, feel free to skip to the end.
In late 2009, Chris Wanstrath released mustache, a Ruby library with a an intriguing premise: “Logic-less templates.” — I’ve been through many iterations of mixing code and HTML and coming up with mini-languages that would allow productive work and tinkering, but not footgunning. This is a rather sad world and Mustache sounded like a great idea.
I was writing a bunch of CouchApps at the time and thought it’d be nifty to have Mustache in JavaScript. I checked out the code with that little Ruby I know and decided it would be easy enough to port to JavaScript. It didn’t take me long. All thanks to Chris for writing clear code — compare the initial Ruby and JavaScript implementations.
The rest is history, Mustache got ported to many other languages and gained a massive (as it gets for a yawn templating library) following in a very short time. Since 2010, mustache.js is a core part of the new Twitter website which I find is pretty cool.
Shortly after Chris released version 0.1.0 the Ruby community with all their smarts found that it wasn’t “optimal” and went to create a proper parser and compiler, as you do with a CompSci degree. Mustache.rb got a lot “more optimal” but it also grew in size. Optimisation is never uniformly good — it is always a trade-off. The new mustache.rb traded conciseness and simpleness of the code with less wasted CPU cycles. An honourable goal.
For the JavaScript version, I didn’t follow that route. I felt a feature of mustache.js was that all code fit into 300 lines of clear code. I didn’t want to lose that. In addition, a common JavaScript environment (the browser, duh) has special considerations at runtime and less code is always good because of network latency and bandwidth and compile-time and whatever else you can come up with. My catchphrase for this is “By the time the browser has downloaded, parsed and compiled your smart compiler, my dumb parser is done rendering the template.” — It’s true! But it is a one-sided view.
In the past year Nathan Vander Wilt created a smart parser for mustache.js that doesn’t add lines of code. I love it, but I never got around reviewing it; sad. It also fixes a few of the many outstanding issues that I don’t find the time addressing.
Over the past year and a half it also became fairly clear that Mustache, while great, isn’t perfect and that people keep asking for the same features or run into the same limitations over and over again. Some time last summer I decided, it is a good time to think about Mustache 2.0 that would break backwards compatibility and would fix all the version 1.0 problems in one swoop. I imagined that the different Mustache implementors would agree on a spec upfront and then implement it. Sounds all good, no?
Over the summer, Yehuda Katz introduced handlebars.js which started to address some of the problems with mustache.js and it looks pretty good! I’m looking at it for inspiration for Mustache 2.0.
Here is the list of things I want to see fixed or improved in Mustache 2.0. It is by no means comprehensive, I’m counting on you to add what you think is missing. But I also want to be careful to not turn Mustache into the kitchen sink and after I list my favourite features, I’ll state what I think are the design principles that should guide any Mustache 2.0 definition and implementation. But first, the features.
Let me start with a light one; if it turns out this would be a problem getting into 2.0, I’d be willing to let go of this :)
The canonical demonstration of this limitation is the shopping cart. List all items one by one or say the cart is empty. Currently, this is how you do it:
var cart = {
items: [
{title:"One"},
{title:"Two"},
{title:"Three"}
]
};
{{#items}}
{{title}}
{{/items}}
{{^items}}
Your cart is empty, go shopping!
{{/items}}
I find having to use inverted sections (or a function-section for that matter) extremely unsexy and I’d like to see something along these lines:
{{#items}}
{{title}}
{{^items}}
Your cart is empty, go shopping!
{{/items}}
I don’t care what the middle symbol is, but I think this is a lot tighter.
Note that I don’t propose to get rid of inverted sections, I just want to be able to make the list-or-empty-list-message pattern nicer.
Oh boy. This keeps coming back all the time. The canonical reply usually only creates more questions than it answers. I personally can live without it, but the frequency of this being brought up made me realise its omission is a pretty big WTF.
“What Notation?” — Here:
var view = {
foo: {
bar: 1
}
};
{{#foo}}
{{bar}}
{{/foo}}
“Boo! Why not:”
{{foo.bar}}
Yeah, why not?
Mustache has sections and they come with changing contexts. The top level context (the foo
in the previous example) and a context for each recursing object inside the view (one, the bar
in the previous example). Now what if you have some nested JSON structure that is three levels deep, you are displaying the third level and also want to render a value from the middle context? Tough luck!
var view = {
foo: {
bar: {
baz: 1
},
qux: 2
}
};
{{#foo}}
{{#bar}}
{{baz}}
{{qux}} # uh-oh!
{{/bar}}
{{qux}} # this would work, but isn’t what we want
{{/foo}}
There’s multiple ways to handle this:
Munging all contexts into one: When recursing down into sections, the new context is merged with all previous contexts. Early versions of mustache.js did this and it irked my ever since. It means all keys in all levels share a single namespace which is highly annoying and merging all these contexts adds significant overhead.
Restricting access to the current and one other context. The current behaviour of mustache.js alternates between 2.a. access to the current context and if there is a key-miss, look up the key in the root context and 2.b. access the current context and if there is a key-miss, look up the key in the previous context. 2.b. requires contexts to be stacked, since that adds code, I currently prefer 2.a., but neither is a good solution.
Handlebars solved this, but I am still on the fence on if it is pragmatic and hence good or a little clunky:
{{#foo}}
{{#bar}}
{{baz}}
{{../qux}} # ah-ha!
{{/bar}}
{{/foo}}
This is neat as it scales, is familiar and solves the problem. It is just my æsthetic sense getting in the way of liking ../
in my mustaches. If anyone finds a notation for this that scales, is familiar and solves this problem and is pleasing to my eyes, I owe you a coffee or beer. Otherwise, I’d say we adapt the ../
.
The smart parser rewrite avoids parsing templates over and over again. This is an obvious performance increase and Nathan managed to keep it short and tight, so I don’t see any reason why this shouldn’t be in 2.0.
Twitter started working on an internationalisation extension. I haven’t had the time to check out their code and I wonder if higher-order sections could help with that instead of making it a standalone (hello kitchen sink) feature, but even if not, I think this is worth adding or pluginning (see next).
This is a feature that currently exists that I’ve never used and that only few people use (sorry if you are one of the ones using it). With all the new features, and the (see below) requirement to keep things small, something has to go and the changing delimiters feature is first on my list to get the boot.
I’d be happy if there’s some way to easily plug in extensions into Mustache for people who need this and potentially other features like internationalisation.
Handlebars implements helpers. They are a good idea and we should outright steal them :)
When I think about Mustache I keep the following in the back of my head to give me guidance when I encounter decisions.
Keep It Simple Stupid. Mustache’s simplicity is a feature and it is of utmost importance we keep it that way. I’d argue that it is its most important feature.
Remember my catch phrase for a dumb implementation? — “By the time the browser has downloaded, parsed and compiled your smart compiler, my dumb parser is done rendering the template.” — In environments like the browser (others like it elude me at the moment) concise code is a feature. And that is before minification and compression.
One of the cool things about Mustache is that you can share templates between your Python or Ruby or JavaScript (or any other) backend and JavaScript front end. It reduces context switching and double implementations of the same things and I consider it a core feature. The devil is in the details, what one language considers truthy, another considers falsy. Mustache shouldn’t care and templates should not break between implementations. The edge-cases here are manifold, and sometimes this just isn’t feasible, but we should try to keep them minimal.
From the “Current State” section, it should be clear that I can’t do this alone. Not for mustache.js and not for all of mustache. This is my call for help and collaboration between all Mustache users and implementors to address what I outlined above (and add what I missed).
Here is the plan in 4 easy steps:
To this end, I’d like to propose to use the /mustache
GitHub project as an Organization that allows us to collaborate easier now and that makes it easier for users to find the best version of Mustache for any given language more easily — But I think this is ultimately Chris’s call.
Who’s in?
Long live Mustache!
{