Christopher Vollick and I were recently discussing ways to get more compile-time guarentees out of our web templates. We both love Mustache for its simplicity and its thin-view philosophy, so that was a natural place to start.
I decided to try writing a tool to convert Mustache templates to Haskell code, which I have done.
This allows the compiler to check a number of things for us including: ensuring we do not use any keys in the templates that are not defined in the model, ensuring that we do not use keys in the template in ways that are inconsistent with the datatype in the model, and ensuring that our partials and sections expect the context they will end up getting passed due to the structure of the templates.
It also gives us a speed boost. Comparing to the other popular Haskell Mustache implementation my current benchmarks show a significant speedup. This is to be expected, since GHC can perform optimisations that a runtime interpolator just can’t. Also, in order to be able to use arbitrary Haskell Records as context, runtime implementations have to perform runtime checks using a union type or the Typeable class. My implementation can do all these checks at compile time.
People familiar with Mustache may be wondering how I handle recursive partials, since this is a feature Mustache touts as being possible because the templates are not compiled. I make a compromise in my code: partials are rendered out as toplevel functions, just like any other file, and as such they only inherit the context directly above them, instead of inheriting the enitre scope all the way to the top. I rarely use values from higher scopes in my partials anyway, so I don’t think this is a very severe limitation.
Values that are displayed in the template must have an instance of Pretty from wl-pprint and values that are used in sections or inverse sections must either be lists of records (which get used as the context for the section body) or any Monoid instance. If the value (==mempty), it is considered falsey. Note that if you need values to be able to be optional, but want the natural mempty of your datatype to be truthy, you can acheive this by wrapping in a Maybe.
The requirement to be a Monoid is relaxed for Bool and a large list of numeric types, which get wrapped in the Any and Sum newtype wrappers by the code generator when they are detected in a record. This allows False and 0 to be treated as falsy for the majority of types where this would be interesting without needing to define orphan Monoid instances or wrap everything up in newtypes yourself.
There is example code on GitHub. Check it out, play with it, and let me know what you think!
I have added a lot of new tags and some corrections to the table.
A few people have asked for something like this. A table of Blogger Classic template tags and their new equivalent. This is not entirely possible because it is not a 1:1 relationship, but I will do my best. This is a post for those who were somewhat literate in the old Blogger and want to upgrade. Another post to check out would be Johan’s original post. Great stuff.
If you have a template tag from Blogger Classic that you don’t see here, post it in a comment and I’ll try to add it. This is a work in progress.
<$BlogPageTitle$> |
<data:blog.pageTitle/> |
<$BlogMetaData$> |
<b:include data=’blog’ name=’all-head-content’/> |
<style type="text/css"> |
<b:skin><![CDATA[ |
</style> |
]]></b:skin> |
<$BlogURL$> |
<data:blog.homepageUrl/> |
<$BlogDescription$> |
|
<Blogger> |
<b:section class=’posts’ id=’posts’ showaddelement=’yes’ growth=’vertical’>
<b:widget id=’PostWidget’ locked=’false’ title=’Posts’ type=’Blog’>
<b:includable id=’main’>
<b:loop values=’data:posts’ var=’post’> |
</Blogger> |
</b:loop>
</b:includable>
</b:widget>
</b:section> |
<$BlogItemNumber$> |
<data:post.id; |
<BlogDateHeader> |
<b:if cond=’data:post.dateHeader’> |
</BlogDateHeader> |
</b:if> |
<$BlogDateHeaderDate$> |
<data:post.dateHeader/> |
<$BlogItemPermalinkUrl$> |
<data:post.url/> |
<BlogItemTitle> |
<b:if cond=’data:post.title’> |
</BlogItemTitle> |
</b:if> |
<$BlogItemTitle$> |
<data:post.title/> |
<$BlogItemBody$> |
<data:post.body/> |
<$BlogItemAuthorURL$> |
<data:blog.homepageUrl/> |
<$BlogItemAuthor$> |
<data:post.author/> |
<$BlogItemDateTime$> |
<data:post.timestamp/> |
<BlogItemCommentsEnabled> |
<b:if cond=’data:post.allowComments’> |
</BlogItemCommentsEnabled> |
</b:if> |
<$BlogItemCommentCount$> |
<data:post.numComments/> |
<$BlogItemControl$> |
<span class=’control’>
<b:if cond=’data:post.emailPostUrl’>
<span class=’item-action’>
<a expr:href=’data:post.emailPostUrl’ title=’Email Post’>
<span class=’email-post-icon’> </span>
</a>
</span>
</b:if>
<b:include data=’post’ name=’postQuickEdit’/>
</span> |
<$BlogEncoding$> |
<data:blog.encoding/> |
<$BlogTitle$> |
<data:blog.title/> |
<$BlogItemAuthorNickname$> |
<data:post.author/> |
<$BlogID$> |
|
<$BlogItemUrl$> |
<data:post.link/> |
<ItemPage> |
<b:if cond=’data:blog.pageType == "item"’> |
</ItemPage> |
</b:if> |
<MainOrArchivePage> |
<b:if cond=’data:blog.pageType != "item"’> |
</MainOrArchivePage> |
</b:if> |
<MainPage> |
<b:if cond=’data:blog.pageType == "main"’> |
</MainPage> |
</b:if> |
<ArchivePage> |
<b:if cond=’data:blog.pageType == "archive"’> |
</ArchivePage> |
</b:if> |
<$BlogItemCreate$> |
<a expr:href=’data:post.addCommentUrl’>Post a Comment</a> |
<BlogItemComments> |
<b:loop values=’data:post.comments’ var=’comment’> |
</BlogItemComments> |
</b:loop> |
<$BlogCommentNumber$> |
<data:comment.id; |
<$BlogCommentDateTime$> |
<data:comment.timestamp/> |
<$BlogCommentAuthor$> |
<address style="display:inline;font-style:normal;" class="author vcard">
<b:if cond=’data:comment.authorUrl != ""’>
<a class="url fn" expr:href=’data:comment.authorUrl’><data:comment.author/></a>
<b:else/>
<span class="fn"><data:comment.author/></span>
</b:if>
</address> |
<$BlogCommentBody$> |
<data:comment.body/> |
<$BlogCommentDeleteIcon$> |
<b:include data=’comment’ name=’commentDeleteIcon’/> |
<$BlogCommentPermalinkURL$> |
#c<data:comment.id; |
For how to use this syntax inside tag attributes, please see Johan at Ecmanaut. I would actually recommend that you read that whole post.
Now having translated most of my hacks to the new Blogger, it is time to unveil the templates. I myself will likely be switching soon, and my template will be based on the code here published. The features are similar to the first set, but optomised for the new Blogger.
To install one of these templates, just cut-n-paste the code into the ‘Edit HTML’ section of your blog and save. Confirm deletion of widgets (Yes, unfortunately, you lose you widgets. You can make them again).
Templates
Please see my Singpolyma Templates for the New Blogger
This is the first Blogger BETA custom template based on my Singpolyma Templates. This will not be a final version because it still has some slight bugs, but I would like feedback from users before I make any changes. You can preview the new template at my BETA blog. The spirit and look of the original basic template has been ported to the new widget system, and all fonts and colours can also be edited from the new panel for that purpose. Minor known issues are:
- Not even close to XHTML compliant. This is not my fault. There are significant JavaScript sections on Blogger BETA that are malformed XML and thus break any sort of compliance. I’ve written support, but I doubt that will do any good. We need to find a way to put serious pressure on them to correct this. The new template system is in XML, make XML-well-formed output (and perhaps even XHTML-compliant output) has never been easier, and yet so far away. The changes they would have to make are minor (like, adding commented CDATA sections around the JavaScript). I don’t see a thing about it on Known Issues.
- Ditto to peek-a-boo comments. This is a limitation in the BETA as far as I can see… I really hope they can be persuaded to fix this.
- The pre-included blogroll is XOXO compliant, but has fewer features as the manually-created ones did (ie, no option for feed data).
- No link to main comments feed (it doesn’t seem to be generating, I believe this is a known BETA issue…)
The profile section supports both single and group blogs, unlike the original which assumed a single-authorship blog. I could also add previous/next links on item pages and a labels sidebar widget if people want it. Versions with my other hacks (as before) will be forthcoming.
To install this template on your blog, cut-n-paste the template from here into the ‘Edit HTML’ view on your BETA blog, click ‘Save’, and if you are asked to confirm the deletion of existing widgets click the affirmative option. Then you can add/edit widgets or change the colours!
For templates for the new Blogger see the updated post.
Due to some of the feedback I’ve been getting from hack implementors recently I have decided to roll out my own set of templates for Blogger, implementing various and sundry features that different persons have expressed interest in. These templates are all loosely based on the Blogger Minima concept, with some inspiration from my blog also. They have been written from scratch, however, and all have the following features:
So, if you’re looking for a new template, or just want some of these features or some of those that follow, this is for you.
If anyone wants another version of this template created (ie, some combination of the hacks, or for another hack) feel free to contact me. If anyone has any ideas or feedback at all concerning the usefulness of / look of these templates, again, I have an open ear. I would ask that you keep your comments positive. ‘This Sucks!’ is not all that helpful to me, even if that’s how you feel 😉
Notes to advanced users: Hopefully these templates will also be useful to you in examining my code, etc. To remove peek-a-boo from comments or backlinks in any of the above templates, simply remove the onclick handler from the appropriate link. It should also be mentioned that these templates all support both versions of blogger comment permalinks. If you have any questions about the features on these templates I’m just an email/comment away!