Skip to content
Brian Zou edited this page Mar 23, 2019 · 6 revisions

Introduce

hunt framework view module is runtime render template engine, easy to use, like twig / jinja2.

How to use

render string

example:

my-template.html:

<div>Hello, {{ name }}!</div>

DLang code:

@Action
string test()
{
    view.assign("name", "Brian");

    return view.render("my-template");
}

result:

<div>Hello, Brian!</div>

Syntax

Default delimiters:
  • Statements {% ... %}
  • Expressions {{ ... }}
  • Comments {# ... #}
  • Line statements #, comments ##
Variables:
  • Variable itself: foo
  • Variable's field: foo.bar or foo['bar']
  • Variables array: foos[2]
Expressions:
  • Integer number: 42
  • Floating number 42.2
  • String: "Some string", 'Another string'
  • Boolean: true/false, True/False
  • Array: [1, 'string', false]
  • List: (1, 'string', false) note: internal list representation still is array
  • Dictionary: {'a': 10, 'b': true} or {a: 10, b: true} note: keys in dictionary can only be strings
  • Math Operators: **, *, /, //, %, +, -
  • Logic operators: ( ... ), not, and, or
  • Comparison operators: ==, !=, >=, <=, >, <
  • Other operators: in, is, |, ~, ...(...)
  • Ternary if: ... if ... else ...
Statements:
  • If: if/elif/else/endif
  • For: for/else/endfor
  • Macros: macro/endmacro
  • Call: call/endcall
  • Set: set
  • Filter: filter
  • Extending: extends, block/endblock
  • Import: import
  • Include: include
  • With: with/endwith note: without assignment
Whitespace control:

Space control with - operator allowed only for statements: {%- ... -%}

Functions:

Most of global functions / tests / filters are not implemented at the moment. Must be done later.

  • Implemented functions: range, length/count, namespace
  • Implemented test: defined, undefined, number, list, dict
  • Implemented filters: default/d, escape/e, upper, sort, keys

2. Main differences

Assignment scope behavior:

Unlike original Jinja it is possible to set variables inside a block and have them show up outside of it. This means that the following example will work as expected.

{% set iterated = false %}
{% for item in seq %} ## non-empty sequence
    {{ item }}
    {% set iterated = true %}
{% endfor %}
{% if not iterated %} did not iterate {% endif %} ## will not be printed (iterated == true)

3. Additional features

1. Set variable fields / array members

It is possible to set variable field and member of array:

{% set foo = {} %}
{% set foo.bar = 10 %}
{{ foo.bar }} ## 10
{% set foos = [1, 2, 3] %}
{% set foos[2] = 30 %}
{{ foos }} ## [1, 2, 30]
2. UFCS

It is possible to use Uniform Function Call Syntax for variables like in D:

{% set foo = [1, 1, 1, 1, 1] %}
{{ range(length(foo)) }} ## [0, 1, 2, 3, 4]
{{ foo.length.range }}   ## [0, 1, 2, 3, 4]
3. Macro return expression

You can return value from user defined macros due to return keyword in endmacro statement:

{% macro sum(numbers) %}
    {% sum = 0 %}
    {% for num in numbers %}
        {% sum = sum + num %}
    {% endfor %}
{% endmacro return sum %}

{{ sum([1, 2, 3, 4]) * 10 }} ## 100
4. Macro as function / test / filter

Since macros can return value, you can use them as functions / filters / tests:

{% macro sum(numbers) %}
    {% sum = 0 %}
    {% for num in numbers %}
        {% sum = sum + num %}
    {% endfor %}
{% endmacro return sum %}

{{ [0, 1, 2, 4] | sum | e }} ## 7
{% macro large(l) %}
{% endmacro return l is list and l.length > 10 %}

{{ 'yes' if [0, 1, 2] is large else 'no' }} ## no
5. Macro as closure

It is possible to define macro inside another macro and inner macro has link to external contexts.

{% set a = 1 %}
{% macro m1 %}  ## variables `a`, `b` are available inside 
    {% set b = 2 %}
    {% macro m2 %}  ## variables `a`, `b`, `c` are available inside
    	{% set c = 3 %}
    {% endmcaro %}
{% endmcaro %}
{% set counter = 0 -%}

{%- macro inc() -%}
    {% set counter = counter + 1 %}
{%- endmacro return counter -%}

{{ inc() }} ## 1
{{ inc() }} ## 2
{{ inc() }} ## 3
{{ inc() }} ## 4
6. For

Loop over each item in a sequence. For example, to display a list of users provided in a variable called users:

<h1>Members</h1>
<ul>
{% for user in users %}
  <li>{{ user.username|e }}</li>
{% endfor %}
</ul>

As variables in templates retain their object properties, it is possible to iterate over containers like dict:

<dl>
{% for key, value in my_dict.iteritems() %}
    <dt>{{ key|e }}</dt>
    <dd>{{ value|e }}</dd>
{% endfor %}
</dl>

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

Variable Description
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.depth Indicates how deep in a recursive loop the rendering currently is. Starts at level 1
loop.depth0 Indicates how deep in a recursive loop the rendering currently is. Starts at level 0
loop.previtem The item from the previous iteration of the loop. Undefined during the first iteration.
loop.nextitem The item from the following iteration of the loop. Undefined during the last iteration.