Skip to content

Instantly share code, notes, and snippets.

@amitsnyderman
Last active May 10, 2018 21:57
Show Gist options
  • Select an option

  • Save amitsnyderman/99f7252801b517b9ae30d09de1100098 to your computer and use it in GitHub Desktop.

Select an option

Save amitsnyderman/99f7252801b517b9ae30d09de1100098 to your computer and use it in GitHub Desktop.
Notes for @ksylormiller on Mustache rendering in Etsyweb

About five years ago Etsy added support for Mustache as a template language. Mustache_Engine was chosen as the renderer, but required a bit of customization for our environment:

  • Translation support – Prior to message catalogs, continued support for <msg /> tags was necessary. Rather than re-implement translation support, the implementation uses a two-part render cycle; the first pass leverages the Smarty renderer for only translations, and the second pass renders the language-specific Mustache. An implementation of Mustache_Loader was written for this purpose.
  • Deterministic runtime compilation – Out of the box templates are compiled on demand. To avoid the performance hit in production, templates are built during deploys for all languages and never compiled at runtime. Where and how compilation cache artifacts are persisted are implemented in subclasses of Mustache_Engine.

Manager

The primary interface for rendering Mustache templates. At its most basic, exposes a render(string $template, array $data) method.

Tpl_Mustache_Manager – Receives an instance of Mustache_Engine and Tpl and sets up the Mustache_Loader relationship. Also the layer at which UiToolkit_Transformer lives.

Engine

Why write our own mustache engine? The Mustache_Engine only actually does one thing: cache. The rest of the work (i.e. compilation and rendering) is delegated to other sibling classes (i.e. Mustache_Parser, Mustache_Tokenizer, Mustache_Compiler).

The problem with the design of the original Mustache_Engine is that it assumes that caching happens after the Mustache_Loader, instead of applying caching around the loader. We get two benefits by writing our own engine:

  • More efficient file loading
  • Predictable cache paradigm

When we speak of cache'ing, there are two varieties:

  1. File cache – compiled Mustache source and written to disk (optional)
  2. Object cache – instance of compiled Mustache source

How the object cache is populated depends on the implementation of loadForObjectCache; whether a file cache is employed is conditional on the implementation.

Shared across implementations is Tpl_Mustache_Engine, an abstract subclass of the Mustache_Engine library.

Implementations of Tpl_Mustache_Engine include:

  • Tpl_Mustache_Engine_Dynamic – Engine used for compiling Mustache templates on-the-fly. Does NOT write to a file cache. Uses eval to evaluate compiled source in memory. Should NOT be used for production code.
  • Tpl_Mustache_Engine_Compile – Engine used only in the context of pre-compiling Mustache templates. Writes and loads compiled Mustache source to and from the filesystem.
  • Tpl_Mustache_Engine_ReadOnly – Engine used for loading pre-compiled Mustache templates. Assumes all templates have been pre-compiled in advance of being used.

Use the Tpl_Mustache_Engine_Factory to instantiate an implementation of one of the engines with correct defaults applied.

There is decent unit test coverage in Tpl_Mustache_EngineTest which codifies and verifies these environment-specific concerns.

Loader

Tpl_Mustache_Loader_SmartyLoader – Loader implementation which delegates to Tpl for reading the localized template off the filesystem or Smarty cache.

Compilation

For deployment to production, templates are pre-compiled during the deploy process.

The main script that handles that is bin/compile_mustache. To parallelize the distinct language builds, it is kicked off by bin/compile_mustache_parallel. The compilation work is all done by the MustacheCompiler_Compiler which traverses the filesystem, compiles the templates and writes the compiled cache to disk utilizing Tpl_Mustache_Manager and Tpl_Mustache_Engine_Compile.

Lint

Not sure when this happens (e.g. git post-commit, deploy, try, etc) but there is a linting script in bin/mustache_lint which calls on MustacheCompiler_Lint_Runner to lint for evidence of bad syntax, inaccessible markup, and Smarty in Mustache templates. Note that MustacheCompiler_Lint_CompileSniff uses Tpl_Mustache_Manager and Tpl_Mustache_Engine_Compile to capture failed Mustache compiles before running the sniffs

Miscellaneous

  • {{.}} vs {{{.}}} – Part of the original Edge Side Rendering project disabled the default escape behavior. Sadly this implementation detail has propogated into our current Neu implementation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment