Mustache

is a full Mustache templating implementation for Carp.

A template is rendered via Mustache.template against a context of type (Map String Mustache). The Mustache sumtype carries the supported value kinds: strings, lists, nested maps, and lambdas.

(Mustache.template
  "Hello, {{#users}}{{name}} {{/users}}!"
  &{@"users" (Mustache.Lst [(Box.init (Mustache.Mp {@"name" (Box.init (Mustache.Str @"Ada"))}))
                              (Box.init (Mustache.Mp {@"name" (Box.init (Mustache.Str @"Grace"))}))])})
; => "Hello, Ada Grace !"

Supports comments, section iteration, inverted sections, object contexts, dotted names, partials, inheritance (parent and block tags), and the set-delimiter tag. Templates are parsed into an intermediate node tree before rendering, so nested sections work correctly.

Templates are processed at byte level, which is correct for ASCII and UTF-8 input as long as the delimiters themselves are ASCII.

Lambda

instantiate

(Fn [(Fn [String] String)] Mustache)

creates a Lambda.

Lst

instantiate

(Fn [(Array (Box Mustache))] Mustache)

creates a Lst.

Mp

instantiate

(Fn [(Map String (Box Mustache))] Mustache)

creates a Mp.

Str

instantiate

(Fn [String] Mustache)

creates a Str.

template

defn

(Fn [(Ref String a), (Ref (Map String Mustache) b)] String)

                        (template s values)
                    

templates a Mustache-formatted string s using the map values.

The map must be from String to Mustache. Values can be:

  • Str — a plain string. Empty strings are falsy for section purposes.
  • Lst — an array of boxed Mustache values. Empty arrays are falsy; non-empty arrays cause sections to iterate once per item. Inside a list section, bare items are bound as {{.}} and Mp items provide nested object contexts.
  • Mp — a nested map of boxed Mustache values. Used as a section value, it pushes its entries onto the context for the section body.
  • Lambda — a function applied to the raw (unrendered) section body, or to the empty string when it is used in an interpolation tag. Its return value is parsed as a Mustache template and rendered against the current context, so tags the lambda emits are interpolated. A section lambda's output is parsed with the delimiters active at the section tag, an interpolated one's with the default delimiters. An interpolated lambda is called once per occurrence, and what it renders to is escaped unless the tag is {{{…}}}.

Example:

(Mustache.template
  "this is a super {{ adjective }} library"
  &{@"adjective" (Mustache.Str @"cool")})

Dotted names like {{a.b.c}} walk nested Mp values: the first segment is looked up on the context stack, then each further segment must be a key of the previous segment's map. This works in interpolation, sections, and inverted sections. If any segment is missing or is not a map, the name is treated as absent (renders empty / skips the section). A bare {{.}} remains the implicit iterator.

A {{> name}} partial that stands alone on its line has that line removed, and every non-empty line of name.mustache is indented to the tag's column before it is parsed. The indent applies to the partial's source, not to its rendered output, so newlines inside an interpolated value are not indented.

A {{$name}}default{{/name}} block marks a spot that can be filled in from elsewhere; on its own it renders default. A {{<name}}...{{/name}} parent injects name.mustache like a partial, but the blocks written directly inside it replace the same-named blocks of the injected template. Everything else inside a parent tag is ignored. Overrides stay in scope for partials and further parents encountered while rendering, and when the same name is overridden at several levels the outermost one wins.

Trimming: names inside mustache tags are trimmed, so {{ thing }} looks up "thing". Opening and closing section markers must use consistent names, i.e. {{/thing}} is not a close for {{^ thing }}.

Rendering nests at most 128 levels deep, and one render emits at most 1048576 bytes; each expansion costs a further 16 bytes of that same budget, so at most 65536 subtrees are expanded even when they emit nothing. Past any of these the offending subtree renders as the empty string, the same as a partial naming a missing file. The depth limit stops a partial, parent or lambda that pulls itself back in once; the byte budget stops one that pulls itself back in twice and fans out exponentially, and because it counts bytes rather than expansions it does so whatever the size of the body being repeated. The budget is shared by the whole render, so a template that legitimately emits more than 1048576 bytes — iterating over a very large list, say — is truncated too.