Logic-less Template Redux
A template is a transform on a context to produce a presentation of that context.
In the beginning, there was printf. Printf takes format strings (templates) and a list of arguments (context), and applies the format to those arguments to produce a presentation:
>>> for item, price in {"Rice": 3.50, "Beans": 5.25, "Onions": 0.29}.items():
... print "%-10s %0.2f" % (item, price)
...
Onions 0.29
Rice 3.50
Beans 5.25
Printf lacks logic. If you want to make a shopping list only show items on sale, you must determine whether an item is on sale in the code surrounding your print statements. This is fine, because printf is always used in code.
On the web, we need to produce documents. With code and printf, this is possible. In the bad old days, when we ignored the wisdom of the ancients, we wrote PHP scripts (templates) which operated on HTTP requests and environment variables (context) to produced a webpage. PHP as a set of instructions for context transform is a powerful (if flawed), fully featured, turing complete language, which allows for any logic to be embedded in the template. Early tutorials often showed database connections being made and more data being pulled into the context (based on parameters from the request).
This ended up being a bad idea for a lot of reasons:
- It can perform poorly, because it is it difficult to share expensive or scarce resources (database cursors, cache connections, module imports) across requests
- It is bad for security, because your entire application and all of its infrastructure knowledge (and private user details) is in the template, making it possible they might be displayed in the event of unforeseen errors
- It is bad culturally, because a language as nuanced and complex as PHP (despite its low initial learning curve) is easy to use poorly, and designers and front-end developers don't need the extra cognitive load
Common system design started to change, and a wall was erected between the logic in the controller producing the context and the logic in the template which produced the output: controller logic should be limited to producing presentation-neutral contexts, and template logic should be limited to presenting that context. This is such a consensus that modern PHP applications are structured completely differently from my caricature of the past and themselves use template languages which are simpler and more secure.
To borrow (again) from Alex Martelli, if making less logic possible in the template was such an improvement, it's a natural fallacy to think that eliminating logic altogether would make everything just perfect. Since this is an implicit claim fundamental to the philosophy of logic-less systems, it's worth examining a bit more closely. First, lets establish what that actually means. Mustache's documentation loosely defines what is meant by logic-less with these words:
We call it "logic-less" because there are no if statements, else clauses, or for loops. Instead there are only tags.
Lets investigate these claims and their impact on Mustache.
Important in Mustache is the concept of "sections", which narrow down the wider context to deal with whatever was mentioned in the opening tag. Sections behave differently depending on what the tag was: if it was an object, the section now has that object's namespace as its context. If it was a list, the section repeats per list item, each item's namespace being the context for the section on each iteration, and if it is a function then that function is called with the section as its input.
Unsurprisingly, sections will not render if the key does not exist or the list is empty. To be able to do something in this case, there are inverted sections, which will render if the key was falsy. Though the semantics of sections are limited, they actually do achieve most of the required semantics of if statements, else clauses, and for loops which Mustache otherwise lacks. So, is the logic-less boast empty sleight of hand?
Not exactly. Inverted sections are only if-else replacements if the test condition for your else was essentially boolean (empty or not empty). If you are looping over your list of users and want to show VIPs in a different color, you will need to express that state ahead of time in some boolean way. So far though, this doesn't violate the wall of separation; such an is_vip
attribute on a user is a valid presentation neutral bit of data.
The problem arises when you have presentations which can differ based on the structure of the data in the context or even the context itself. If you want to display a list of 20 photos 4 per row, you're forced to violate the wall; either you pass in a list of photos as a matrix, embedding details of the presentation in the structure of the context, or you go through items in the context adding superficially presentation neutral data whose only purpose is to overcome these limitations. If, instead of VIP status, you wanted to alter user color based on their number of twitter followers, you'd have to embed those design considerations in the context, and change them there in the future when the design requirements change.
The original Django Template system was a pioneer of limited logic templating, but despite its effort to provide enough in the way of logic, it ran aground in areas. As an example, it eschewed traditional if/else logic for a bevvy of if tags likeif
, ifequal
, and ifnotequal
, which allowed the template to have a limited but mostly functional conditional system. It was argued that this limitation was simpler for designers, and the required if/else semantics were still possible by nesting these tags. Eventually, the unwieldy verbosity of endlessly nesting if sections and the limitations led to the development of much more standard if/else tags with programming language style boolean operators.
For a little one man operation like this, I can just about grin and bear intrusions of display logic into the controller, but what I can't bear is the inability to actually achieve display logic in the template. After quite a bit of use and reflection, I've decided that the skin-deep logic-less claim of Moustache serves mostly to frustrate rather than to guide towards good practice. Since it doesn't actually limit logic itself, but rather the ergonomics in the application of that logic, and, often, its locality, the claimed benefits must be called into question.