Changing the Iocaine template

  Important

In iocaine 1.x, the templates used handlebars syntax, but iocaine 2.0 uses Jinja2, migration is necessary.

iocaine uses minijinja for templating, a template language largely compatible with Jinja2, and will look for a template named main.jinja in the configured template directory, or, if no directory is configured, it will fall back to the default template.

Available variables

Each time a page is rendered, iocaine makes data in the following variables available:

{
  "static_seed": "...",
  "content_type": "text/html",
  "request_host": "example.com",
  "request_uri": "some-path/",
  "params": {
    "q": "some-value"
  }
}

It is possible to change any of these with {% set %}, but that only really makes sense for content_type and static_seed.

Changing content_type changes the Content-Type header returned for the request by iocaine. While it is possible to set any content type, the templating system requires all output to render into an UTF-8 string, so you’re limited to text-based formats. That still allows rendering SVGs, Atom or RSS feeds, sitemaps, CSS stylesheets, JavaScript stuff, and those kind of things.

Changing static_seed changes the random number generator, but only if it is set before calling any other custom function. It is recommended not to change it, unless you are absolutely sure you know what you are doing. It can be used to set the seed to that of a different page, like that of the containing directory of an SVG image.

Custom functions

To do its job properly, iocaine relies on the template to drive how garbage is generated, and how much of it. To facilitate that, a number of custom functions are provided. These aren’t context-aware, so may end up generating text that is invalid in certain contexts: quotation marks when generating links, HTML elements whene generating garbage. It is recommended to escape them using the engine-provided escape and urlencode filters (the latter is available since iocaine 2.5.0), as appropriate.

rand(max=N, min=1, pos=<automatic>, group="default")

Generates a random number between min and max, seeded by pos and group. Only the max named argument is required, the rest have sensible defaults. If one wants to generate the same random number as elsewhere, use the same parameters, including pos and group.

Every time rand() is called without explicitly giving it a pos argument, the automatically tracked variable gets incremented. This guarantees that any calls to rand() that weren’t explicitly set up to give the same answer as another one, will choose a random answer.

All other functions that need random numbers use this framework, and work in exactly the same way.

Examples:

{{ rand(max=15) }} == {{ rand(min=1, max=15, pos=0, group="default") }}
{{ rand(max=15) }} is not guaranteed to be the same as {{ rand(max=15, pos=0) }}!

Generate 1-5 paragraphs of markov-based garbage:

{% for p in range(rand(max=5, group="paragraphs")) %}
<p>{{ markov(min=10, max=128) }}</p>
{% endfor %}
markov_gen(max=N, min=1, words=None, pos=<automatic>, group="markov")

Generates either words number of words of garbage (if a words argument was given), or a random number of words between min and max. Only the max argument is mandatory, the rest have sensible defaults.

Examples:

{{ markov_gen(min=10, max=128) | escape }} generates 10-128 words of garbage.
{{ markov_gen(words=4) | escape }} always generates 4 words of garbage.

{{ markov_gen(max=128, pos=42, group="title") | escape }}
 - This will always generate the same garbage.
href_gen(max=N, min=1, words=None, pos=<automatic>, group="href")

Generates a string suitable to be included in a href, made up of either words number of words, or a random number between min and max. The words are joined together by a dash (-). Only the max argument is mandatory, the rest have sensible defaults.

Examples:

<ul>
 {% for l in range(rand(max=5, group="links")) %}
 <li>
  <a href="{{ href_gen(max=2) | urlencode }}">
   {{ markov_gen(min=2, max=7, group="href") | escape }}
  </a>
 </li>
 {% endfor %}
</ul>
regex_gen(PATTERN, max=N, min=1, pos=<automatic>, group="regex")

Generates a string that matches the regexp pattern given in PATTERN. N controls the maximum number of repeats in patterns like x*, x+, or x{n,}. Both PATTERN and the max argument are mandatory.

This function can be used to generate URL structures that match a pattern, or “secrets”, or a whole lot of other things the author of this documentation has not thought of.

Examples:

{% set username = regex_gen("[-\\.a-zA-Z0-9]{3,32}", max=32) %}
<a href="/@{{ username | urlencode }}/">@{{ username | escape }}</a>

If you need to use a backslash-escape in the regexp, that will need to be doubly-escaped, as shown above.

qr(CONTENT, format="png", width=<automatic>, height=<automatic>)

Generates a QR code of CONTENT, in a given format and widthxheight dimensions. If specifying dimensions, both of them must be specified, otherwise they will be silently ignored. The supported formats are: png, svg, and svg:raw. The first two generates a base64-encoded data: url that can be directly used by an <img> tag, the latter, svg:raw outputs the generated SVG as-is, and can be included directly in the template, as-is.

Examples:

<img src="{{ qr(markov_gen(max=7, group="qr")) }}"
     alt="{{ markov_gen(min=3, max=15, group="qr") | escape }}">
parse_file(PATH[, format="<json|yaml>"])

Parse the given PATH as either json or yaml, and return the parsed structure. Intended to be used together with {% set %}, to load data from a separate file.

Examples:

{% set secrets = parse_file("secret-rules.yaml", format = "yaml") %}
{%- with rule = secrets["github-pat"],
         name = regex_gen(rule.name_regex, max=256)|upper|replace("-", "_"),
         value = regex_gen(rule.secret_regex, max=256) -%}
{{ name }}="{{ value }}"
{%- endwith -%}

Added in iocaine 2.1.0.

Custom filters

Apart from the functions described above, iocaine also introduces the following filters for the templates:

STRING | matches(REGEXP)

Allows matching a string against a regexp, which can be useful to drive the template to produce more realistic content, based on the request_uri, for example.

Examples:

{% if request_uri|matches("^/blog/") %}
{% include "blog.jinja" %}
{% endif %}
STRING | sanitize_filename(replacement="")

Sanitizes an input string to become a filename that is safe to include. This is meant to make it possible to provide per-host templates:

{% include ("hosts/" ~ request_host|sanitize_filename ~ ".jinja") ignore missing %}