Templates

Views are pluggable engines registered with the views keyword; the first registered is the default. Punk ships Template::Stencil, a fast C-backed engine.

views Stencil => {
    template_dir => 'root/templates',
    wrapper      => 'layout.tmpl',
};

Or, equivalently, from config/punk.yml:

views  Stencil    template_dir: root/templates    wrapper: layout.tmpl

The engine is constructed at boot, so bad options fail before the app serves. Templates themselves hot-reload through Stencil's mtime cache - no restart while you edit.

Rendering

sub view {
    my ($c) = @_;
    return $c->render('book/view', { book => $book });
}

Template names resolve against template_dir with .tmpl inferred, and render inside the wrapper layout: book/view becomes root/templates/book/view.tmpl wrapped by layout.tmpl. Options override per call:

$c->render('feed', \%data, type => 'application/atom+xml', status => 200);
$c->render('email/welcome', \%data, engine => 'Other');

Stencil syntax

<h1>{% title | upper %}</h1>
<ul>
{% for item in items %}<li>{% item.name %}</li>
{% end %}
</ul>
{% if admin %}<a href="/admin">admin</a>{% end %}
{% raw trusted_html %}

Values are HTML-escaped by default; {% raw %} opts out for values you built yourself. Filters chain with |, and dotted paths walk hashes and method-less objects. The wrapper receives the page as {% content %}.

Layouts

A wrapper is an ordinary template around the page:

<!doctype html>
<html lang="en">
<head><title>{% title | default('MyApp') %}</title></head>
<body>
<main>
{% content %}
</main>
</body>
</html>

Other engines

Register any class with the engine contract as a view engine, by name or with a literal +Class:

views '+My::View::TT' => { ... };

See Punk::Views for the contract.