Filters
absattrbatchcapitalizedefaultescapeevalfloatfoldforallgroupbyintjoinlastlengthlistlowermapmaxmd5minnthrandomrangerejectreplacereverseroundselectslicesortsplitstriptagstrlensublistsubstringsumtitletruncatetrimupperurlizewordcountwordwrapxmlattr

Statements

{%- set script = "<script></script>" -%}
{%- autoescape true -%}
{{ script }}
{% endautoescape -%}
{%- autoescape false -%}
{{ script }}
{% endautoescape -%}
&#60;script&#62;&#60;/script&#62;
<script></script>

Loop over each item in a sequence.

Inside of a for-loop block, you can access some special variables:

loop.index
The current iteration of the loop. (1 indexed)
loop.index0
The current iteration of the loop. (0 indexed)
loop.revindex
The number of iterations from the end of the loop (1 indexed)
loop.revindex0
The number of iterations from the end of the loop (0 indexed)
loop.first
True if first iteration.
loop.last
True if last iteration.
loop.length
The number of items in the sequence.
loop.cycle
A helper function to cycle between a list of sequences.
{{ { links : [ [ "http://yahoo.co.jp", "yahoo japan" ],
	       [ "http://google.co.jp", "google japan" ] ]
   , obj: { name: "alice", age: 42, sex: "F" }
   } }}
{%- for item in [ 0, 1, 2, 3, 4, 5 ] -%}
  - {{ item }}: {{ loop.cycle (["even","odd"]) }}
{% endfor -%}
- 0: even
- 1: odd
- 2: even
- 3: odd
- 4: even
- 5: odd

Macro are useful to put often used idioms into reusable component.

Note that macros are hoisted, and their scope is the toplevel scope. It means that you define two macro with the same name, the second will erase the first, even in the macro invocation occuring before the second definition.

{% macro li (content) %}<li>{{ content }}</li>{% endmacro -%}
<ol>
  {{ li ('Item1') }}
  {{ li ('Item2') }}
  {{ li ('Item3') }}
</ol>
<ol>
  <li>Item1</li>
  <li>Item2</li>
  <li>Item3</li>
</ol>

Use call statement to pass a macro to another macro.

{%- macro present (name) -%}
Hello, I am {{ name }}.{{ caller() }}
{%- endmacro -%}
{{ present ("Bob") }}
{% call present ("Alice") %} I'm a programmer.{% endcall %}
Hello, I am Bob.
Hello, I am Alice. I'm a programmer.

Function can be seen as macros that return a value instead of printing it.

{% function incr (i) %}{{i+1}}{% endfunction -%}
incr(1) = {{ incr (1) }}
incr(-1) = {{ incr (-1) }}

{% function lt1 (i) %}{{ i < 1 }}{% endfunction -%}
lt1(1) = {{ lt1 (1) }}
lt1(-1) = {{ lt1 (-1) }}

{% function obj (x, y) %}{{ {a:x,b:y} }}{% endfunction -%}

obj(1,2).a = {{ obj(1,2).a }}
obj(1,2).b = {{ obj(1,2).b }}

obj('a',2.5).a = {{ obj('a',2.5).a }}
obj('a',2.5).b = {{ obj('a',2.5).b }}

{% function inv (reverse=false, x, y) -%}
  {%- if reverse %}{{ {a:y,b:x} }}{% else %}{{ {a:x,b:y} }}{% endif -%}
{%- endfunction -%}

inv('a','b').a = {{ (inv('a','b')).a }}
inv(reverse=true,'a','b').a = {{ (inv(reverse=true,'a','b')).a }}

{% function add_val (val=0, x) -%}
  {{ [ x, (x + val) ] }}
{%- endfunction -%}
{% for x in [-1,0,1,2] | map (add_val, val=-2) -%}
  add_val(val=-2,{{x[0]}}) = {{x[1]}}
{% endfor -%}
incr(1) = 2
incr(-1) = 0

lt1(1) = false
lt1(-1) = true

obj(1,2).a = 1
obj(1,2).b = 2

obj('a',2.5).a = a
obj('a',2.5).b = 2.5

inv('a','b').a = a
inv(reverse=true,'a','b').a = b

add_val(val=-2,-1) = -3
add_val(val=-2,0) = -2
add_val(val=-2,1) = -1
add_val(val=-2,2) = 0

The if statement allows us to check if an expression is true or false, and execute different code according to the result.

0, null, '', "", [], or {} are considered as false when used in a if/elif test. The rest is true.

{{ { hoge : "baz" } }}
{%- if hoge == "foo" -%}
hoge == "foo"
{%- elseif hoge == "bar" -%}
hoge == "bar"
{%- elif hoge == "baz" -%}
hoge == "baz"
{%- else -%}
But... What is hoge?
{%- endif %}
hoge == "baz"

{% if 1 in [1,2,3] -%}
<p>Yes, it works! <code>1</code> is in <code>[1,2,3]</code></p>
{%- endif %}
<p>Yes, it works! <code>1</code> is in <code>[1,2,3]</code></p>

{% filter upper -%}
must be upper
{%- endfilter %}
MUST BE UPPER

{{ { person : { name : "alice", age: 42 } } }}
- person.name: {{ person.name }}
- person.age: {{ person.age }}
- person["name"]: {{ person["name"] }}
- person["age"]: {{ person["age"] }}
- person.name: alice
- person.age: 42
- person["name"]: alice
- person["age"]: 42

{% raw -%}
Not expanded: {{ hoge }}
Not a for loop: {% for x in long_list %}{{x}}{% endfor %}
{%- endraw %}
Not expanded: {{ hoge }}
Not a for loop: {% for x in long_list %}{{x}}{% endfor %}

All blocks introduce a new scope when assigning variables. You can read a variable defined in a parent block, but you can not assign a new value to this variable. A new variable will be created for each assignment. It will shadow the old definition, but as soon as you will leave the block, the old definition

The only exception to that rule are if statements which do not introduce a scope.

See Namespace for variable assignement propagating across scopes.

{%- set hoge = "ok" -%}
{%- set foo, bar = ("foo", "bar") -%}
hoge: {{ hoge }}
foo: {{ foo }}
bar: {{ bar }}

{% set iterated = false -%}
{%- for item in [ 1, 2, 3 ] -%}
  {% set iterated = true %}
{%- endfor -%}
{%- if not iterated %}Did not iterate!{% endif %}
hoge: ok
foo: foo
bar: bar

Did not iterate!

Namespace are used to create variables you can modify from a child block.

{%- set ns = namespace (foo=0, bar='bar') -%}

{%- for i in [1,2,3] -%}
  {%- set ns.foo = ns.foo + i -%}
  {%- set ns.bar = ns.bar + i -%}
{%- endfor -%}

{%- if ns.foo == 6 and ns.bar == 'bar123' -%}
  Namespace works :)
{%- endif %}
Namespace works :)

Use {%- to strip whitespaces before this token.

Use -%} to strip whitespaces after this token.

{%- for i in [1,2,3] -%}
  {{ i }}
{%- endfor %}
123

{% with foo = 10, hoge = 20 -%}
foo: {{ foo }}
hoge: {{ hoge }}
{%- endwith %}
foo: 10
hoge: 20

Tests

Tests are just functions that return a boolean.

Tests can be used with the standard function call syntax, but also with the is keyword in statements or expressions.

- 6 is divisibleby 4: {{ 6 is divisibleby 4 }}
- 6 is divisibleby (4): {% 6 is divisibleby (4) %}
- divisibleby(3,6): {{ divisibleby(3,6) }}
- 3 is iterable: {% 3 is iterable %}
- [1,2,3] is iterable: {% [1,2,3] is iterable %}
- "hoge" is iterable: {{ "hoge" is iterable }}
- null is iterable: {% null is iterable %}
- 6 is divisibleby 4: false
- 6 is divisibleby (4): false
- divisibleby(3,6): true
- 3 is iterable: false
- [1,2,3] is iterable: true
- "hoge" is iterable: true
- null is iterable: true

Operators

Evaluation of boolean operators is sequential, left-to-right.

In e1 && e2, e1 is evaluated first, and if it returns false, e2 is not evaluated at all.

In e1 || e2, e1 is evaluated first, and if it returns true, e2 is not evaluated at all.

Use not and ! operators to negate a boolean.

{{ { foo : "FOO", bar: "BAR" } }}
{% if foo == "BAR" || bar == "BAR" %}{% endif %}
{% if foo == "BAR" or bar == "BAR" %}{% endif %}
{% if foo == "FOO" && bar == "BAR" %}{% endif %}
{% if foo == "FOO" and bar == "BAR" %}{% endif %}
{% if not (foo == "BAR" and bar == "BAR") %}{% endif %}
{% if ! (foo == "BAR" || bar == "FOO") %}{% endif %}
✓
✓
✓
✓
✗
✗

1 + 1 = {{ 1 + 1 }}
1 - 1 = {{ 1 - 1 }}
2 * 3 = {{ 2 * 3 }}
5 / 2 = {{ 5 / 2 }}
2 ** 8 = {{ 2 ** 8 }}
8 % 3 = {{ 8 % 3 }}

"a" + "b" = {{ "a" + "b" }}
{% for i in [1,2] + [3,4] %}{{i}}{% endfor %}
1 + 1 = 2
1 - 1 = 0
2 * 3 = 6
5 / 2 = 2
2 ** 8 = 256.
8 % 3 = 2

"a" + "b" = ab
1234

Built-in filters

abs (num) Return the absolute value of num.

attr (name, obj)

{{ foo | attr ("bar") }} is {{ foo.bar }}.

{{ foo | attr ("bar.baz") }} is {{ foo.bar.baz }}.

{{ { list: [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100] } }}
Batch of length 4 padded with "X":
{% for row in batch (4, list, fill_with="X") -%}
|{% for x in row %} {{ x }} |{% endfor %}
{% endfor -%}
Batch of length 4 padded with "X":
| 10 | 20 | 30 | 40 |
| 50 | 60 | 70 | 80 |
| 90 | 100 | X | X |

{{ 'this phrase should be capitalized!' | capitalize }}
This phrase should be capitalized!

default (default_value, value)

Return value if it is defined from null, and default otherwise.

escape (string). Replaces &, ", < and > with their corresponding HTML entities.

{% set msg = 'Hello, world!' -%}
{% set src = 'From eval!' -%}

Only the last expression is returned:
- "{{ eval("{{ msg }} {{ src }}") }}"
- "{{ eval("{{ msg + ' ' + src }}") }}"

Be careful with trailing whitespaces:
- "{{ eval("{{ msg + ' ' + src }} ") }}"
Only the last expression is returned:
- "From eval!"
- "Hello, world! From eval!"

Be careful with trailing whitespaces:
- " "

groupby (fn, seq)

{{ { persons : [ { name : "alice", age: 42, sex : "F" },
                 { name : "bob", age: 34, sex : "M" },
                 { name : "carol", age: 29, sex : "F" } ] }
}}
{%- for g in persons | groupby (attr('sex')) | sort (attribute='grouper') -%}
- Gender: {{ g.grouper }}
  {%- for p in g.list | sort (attribute='name') %}
  - {{ p.name }}
  {%- endfor %}
{% endfor -%}
- Gender: F
  - alice
  - carol
- Gender: M
  - bob

join (sep, seq)

{{ join (", ", [1,2,3,4,5,6,7,8]) }}
1, 2, 3, 4, 5, 6, 7, 8

last (seq). Return the last element of sequence seq.

length (seq). Return the number of elements in sequence seq.

{{ max([10, 30, 20]) }}
30

{{ min ([10, 20, 5, 30]) }}
5

range (start, stop)

- range(0,3): {% for x in range(0,3) %}{{x}}{% endfor %}
- range(3,0): {% for x in range(3,0) %}{{x}}{% endfor %}
- range("a","e"): {% for x in range("a","e") %}{{x}}{% endfor %}
- range(0,3): 0123
- range(3,0): 3210
- range("a","e"): abcde

round (method, val). method can be "floor" or "ceil".

- 1.5 | round ("floor"): {{ 1.5 | round ("floor") }}
- 1.5 | round ("ceil"): {{ 1.5 | round("ceil") }}
- 1.5 | round ("floor"): 1.
- 1.5 | round ("ceil"): 2.

{{ { list: [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100] } }}
List sliced in 4:
{% for s in slice (4, list) -%}
- [{% for x in s %} {{ x }}{% endfor %} ]
{% endfor -%}
List sliced in 4:
- [ 10 20 30 ]
- [ 40 50 60 ]
- [ 70 80 ]
- [ 90 100 ]

sort (seq). Support the following optionnal keywords:

reverse
Sort using descending order.
attribute
Compare items using a given attribute. Support dotted notation.
compare
Provide a comparison function to be used instead of built-in.

{{ { data: [ ["A", 0], ["Z", 2], ["b", 1] ] } }}
{%- function ic_fst_cmp (a, b) -%}
  {{ compare (lower (a[0]), lower(b[0])) }}
{%- endfunction -%}

{%- set data = [("A", 0), ("Z", 2), ("b", 1)] -%}

sort(data):
 {% for x in data | sort %} {{ x[0] }}{% endfor %}
sort(data, compare=if_fst_cmp):
 {% for x in data | sort (compare=ic_fst_cmp) %} {{ x[0] }}{% endfor %}
sort(data):
  A Z b
sort(data, compare=if_fst_cmp):
  A b Z

strlen (string). Number of UTF-8 characters.

- strlen("hogehoge"): {{ strlen("hogehoge") }}
- strlen("日本語"): {{ strlen("日本語") }}
- strlen("hogehoge"): 8
- strlen("日本語"): 3

- substring (3, 2, "hogehoge"): "{{ substring (3, 2, "hogehoge") }}"
- substring (0, 2, "日本語"): "{{ substring (0, 2, "日本語") }}"
- substring (3, 2, "hogehoge"): "eh"
- substring (0, 2, "日本語"): "日本"

{{ [ 0, 1, 2, 3, -1 ] | sum }}
5

{{ 'this phrase should be in title case!' | title }}
This Phrase Should Be In Title Case!

upper test: {{ "must be upper" | upper }}
upper test: MUST BE UPPER

Word count for "hoge hage hige" = {{ "hoge hage hige" | wordcount }}
Word count for "hoge hage" = {{ "hoge hage" | wordcount }}
Word count for "hoge hage hige" = 3
Word count for "hoge hage" = 2