Logic-less template system. Snabela (pronounced snob-el-aw) is based on Mustache with an intent to fix the issues the author found with it.
Specifically:
- Mustache is geared very specifically at web development, by default everything is HTML escaped. Snabela does not escape anything by default and allows extensible escaping.
- Mustache has no direct way to escape template replacement but has a convoluted mechanism based on changing the syntax of the delimiter on the fly. Snabela does not allow changing the syntax of the template delimiter on the fly and has a simple mechanism for escaping template replacement.
- Mustache supports changing the syntax of the template delimiter on the fly, Snabela does not support this.
- Mustache has many implicit conversions, for example iterating a list and testing boolean values use the same operator. Snabela has specific operators for each type.
- Mustache does not allow arbitrary conversion of a template to a defined format. For example if you want to represent a number as money, you have to either convert it to a string before the replacement or use whatever numeric representation the template engine does. Similarly, in some context a string should be a title or capitalized/lowercased. In Mustache that value must have each version represented.
A typical Snabela template:
Hello @name@ You have just won @value@ dollars! @?in_ca-@ Well, @taxed_value | money@ dollars, after taxes. @/in_ca-@ Email me at foo@@bar.com for more details.
Given the following value:
{ "name": "Chris", "value": 10000, "taxed_value": 10000 - (10000 * 0.4), "in_ca": true }
Will produce the following, assuming there is a transformer named money
:
Hello Chris You have just won 10000 dollars! Well, 6000.00 dollars, after taxes Email me at foo@bar.com for more details.
Snabela takes a Unicode template string and a key-value object and replaces keys
referenced in the template string with the value in the object. Keys are
separated by the @
symbol. The @
can be escaped with two @
symbols. The
key-value object can contain UTF-8 encoded strings, integers, floats, lists, and
key-value objects. Symbols directly after the opening @
allow the key-value
object to be traversed in different ways.
A key identifier must correspond to the regexp: [a-zA-Z_][a-zA-Z0-9_]*
.
A conforming Snabela implementation must, by default, error if a key is accessed in a template but does not exist in the key-value object. Likewise, it must error if a transformer is accessed but does not exist in the context. The implementation must also error if a test operator is used on a value of the incorrect type.
Replacing a key with a value is called template replacement. Template
replacement is began with a @
and ended with a @
. Modifiers may directly
follow the @
, such as ?
, !
, and #
. After those may be any number of
white space characters followed by a key identifier, followed by any number of
white space characters, followed by a closing @
. By default, all white space
is preserved in the template, however this can be modified with the -
modifier, which is always the inner-most modifier.
White space is removed up to the preceding or following new line using the -
modifier. A -
in the opening of a template replacement trims any white space
up to the new line. A -
in the closing template replacement trims any white
space up to and including the first new line.
Template:
@-them@ My name is @me-@ and I have a business proposition for you.
Key-value object:
{ "them": "Sir/Madam", "me": "Arthur Digby Sellers" }
Output:
Sir/Madam My name is Arthur Digby Sellers and I have a business proposition for you.
Like Mustache, Snabela has sections. The identifier to enter a section depends
on the type of section however each section is exited with @/
.
A boolean value in the template can be tested for true
with @?
and false
with @!
.
Template:
Shown. @?person-@ Never shown! @/person@ @!person-@ Always shown! @/person-@
Key-value object:
{ "person": false }
Output:
Shown. Always shown!
The key-value object can have keys which correspond to a list of objects.
Iterating is done with @#
. Each object in the list creates a new, inner,
context which includes the outer contexts but shadows keys with the same name.
Template:
@#parties-@ @name@ has a minimum age of @min_age@. Guest list: @#guest_list-@ @name@ @/guest_list-@ @/parties-@
Hash:
{ "min_age": 18, "guest_list": [], "parties": [ { "name": "End of the world party", "guest_list": [{"name": "me"}, {"name": "myself"}, {"name: "i"}] }, { "name": "End of the world party party", "min_age": 21 }, ] }
Output:
End of the world party has a minimum age of 18. Guest list: me myself i End of the world party party has a minimum age of 21. Guest list:
A list can be tested for if it is empty or not. The value must be a list.
Testing if a list is empty or not is done with @#?
and @#!
respectively.
This does not create a new context inside the list with shadowed variables.
The previous example done such it does not give an empty “Guest list” section would look like:
Template:
@#parties-@ @name@ has a minimum age of @min_age@. @#?guest_list-@ Guest list: @-#guest_list-@ @name@ @-/guest_list-@ @/guest_list-@ @#!guest_list-@ No guests have signed up. @/guest_list-@ @/parties-@
Hash:
{ "min_age": 18, "guest_list": [], "parties": [ { "name": "End of the world party", "guest_list": [{"name": "me"}, {"name": "myself"}, {"name: "i"}] }, { "name": "End of the world party party", "min_age": 21 }, ] }
Output:
End of the world party has a minimum age of 18. Guest list: me myself i End of the world party party has a minimum age of 21. No guests have signed up.
A template can have a comments. Comments start with @%
or @-%
and can
contain any character other than @
. A command ends with @
or -@
.
Any template replacement may include one or more transformers. A transformer is
a function which takes the value of a template and converts can perform any
operation on it. It may also throw an error. Transformers can be used to
encode a value to ensure it is safe to output or ensure a value has a particular
structure. Transformers come after the name of the key, with optional white
space, separated by the |
symbol. Transformers are only valid for key
replacements, not testing a value or iterating a list. An implementation is
allowed to execute a template with a default transformer on all template
replacements. For example, in a web context the template execution might put
all values through an transformer which HTML escapes.
Template:
- @name@ - @company | html@ - @company@
Hash:
{ "name": "Chris", "company": "<b>GitHub</b>" }
Output, presuming a transformer called html
exists which takes any value end turns it
into an HTML-escaped string, the following:
- Chris - <b>GitHub</b> - <b>GitHub</b>