Mozsearch uses the liquid crate which is a rust implementation of the liquid templating language. Documentation for the JS and the canonical Ruby implementations are available and are quite good, but note that liquid-rust currently does not implement all of the blocks/tags/filters that are documented at those implementations. liquid was chosen from the limited pool of rust templating engines that also had JS implementations available so that we could use the same logic on both the server and client if needed.
Tags look like {% foo %}
. Outputs look like {{ some_var }}
. liquid-rust
adds an additional concept of "blocks" which are tags which can have other tags
conceptually nested inside them, like {% for %}
which gets paired with
{% endfor %}
. Outputs can also include filters which use a pipe-like syntax
that looks like {{ my_mixed_case_string || downcase }}
.
Under the canonical ruby semantics, the only things that are falsy are false
and nil
. Empty strings, empty arrays, and empty objects are all truthy! If
you would like sane results, you need to compare against the magic literals of
empty
and/or blank
.
There is logic in the core method value_eq which checks if the LHS or RHS is an explicit "state" and in that case uses the "query_state" method to check the semantics of the value for the given state; for example the str ValueView has this logic. This provides the mechanism for "truthy" (used by the if block's existence condition) as well as for the "default" filter's logic which checks for falsy or blank, not just falsy.
The difference between empty
and blank
is that blank
will trim a string before
checking if it's empty. So "" == empty
and "" == blank
but for " "
we find
that " " != empty
and " " == blank
.
Want to check if a string is exactly ""
? Then use my_var == empty
. Do you
also want to act like it's empty if it's only whitespace? Then use
my_var == blank
.
Use empty
. Do my_array_val == empty
.
Want to check if an object dictionary has no keys/values? Use empty
. Do
my_obj == empty
. Be aware that if you try and access a nonexistent property
of an object you will get an IndexError, so in many cases you actually want to
be using the contains
operator instead; see the next section for more info.
liquid-rust currently does not have support for "EmptyDrop" as documented at https://shopify.github.io/liquid/basics/types/ so if you try and access a property that does not exist, you will get an IndexError. If EmptyDrop support existed, in theory it would just return something that is equal to empty, but the ruby implementation doesn't seem to explicitly test for this behavior, so it may just be an implementation artifact.
In any event, you can use the "contains" operator to do my_obj contains "key"
to check if there's a key property. The canonical documentation for contains at
https://shopify.github.io/liquid/basics/operators/ is a little confusing because
the way it discusses object makes it sound like you can't do this, but I think
it's just saying it can only test string equality / check for keys and is not
capable of doing structural equality tests.
In a conditional you can do things like my_array contains "foo"
in an if tag
like {% if my_array contains "foo" %}
. As noted above about objects, this
also works for objects, so my_obj contains "foo"
should return true for an
underlying object that looks like { "foo": ... }
in JSON.
You can't use parentheses or otherwise nest things. It's like the rightmost pair is fully nested in parentheses. Don't even think about asking about short-circuiting.
my_array_val[0]
is the first item in the array. Usually you would use a
for block tag, not directly subscript
things. Subscripting makes sense for tuple types, such as when iterating over
an object/map.
Putting a -
character on the inside of a tag indicates to strip the whitespace
on that side of the tag. So {{-
/-}}
can be used instead of {{
/}}
and
{%-
/-%}
can be used instead of {%
/%}
. Note that you can make the
decision independently for the opening and closing tags.
This list is derived from examining the source's blocks, tags, and filters modules for the stdlib.
- Blocks:
- capture
- case
- comment
- for
- if
- ifchanged (only outputs its rendered contents if they've changed since the last outputted value, starting from the base case)
- raw
- tablerow
- unless
- Tags
- assign
- break
- continue
- cycle
- decrement
- include (note: "render" is not supported right now!!)
- increment
- Filters
- abs
- append
- at_least
- at_most
- capitalize
- ceil
- compact
- concat
- date
- default
- divided_by
- downcase
- escape
- escape_once
- first
- floor
- join
- last
- lstrip
- map
- minus
- modulo
- newline_to_br
- plus
- prepend
- reverse
- remove
- remove_first
- replace
- replace_first
- round
- rstrip
- size: Number of elements in an array or object, length for strings.
- slice
- split
- strip
- strip_newlines
- sort
- sort_natural
- strip_html
- times
- truncate
- truncate_words
- uniq
- upcase
- url_decode
- url_encode
- where
- Filters
- compact_pathlike: Remove excess whitespace in a path-like string. Compacts
" foo / bar/ baz "
to"foo/bar/baz"
. - ensure_bug_url: If we're given something that's clearly a link, pass it through as-is, but if it's not a bug, format it into a proper bug tracker link, which by default is (or may still be hardcoded to be) BMO.
- fileext: Extracts the file extension from a path string, defaulting to the empty string if there is no file extension. (Note that this does not use the "default" mechanism!)
- json: Render the given value to JSON
strip_prefix_or_empty
: Takes an argument which is a prefix to attempt to remove. If the string started with the prefix, the prefix-stripped string is returned. If the string did not start with the prefix, an empty string is returned. We probably could also have just returned the false value but idiomatically it's probably better to have explicit checks againstempty
everywhere to reduce confusion.
- compact_pathlike: Remove excess whitespace in a path-like string. Compacts