Render context¶
HAML.render accepts a :context named argument — a Raku object whose methods
become reachable inside templates as bare identifiers in expression
position. This is the "view context" of frameworks like Rails: a place to put
template-facing helpers without polluting global scope.
1 2 3 4 5 6 7 8 9 10 11 12 | |
renders:
1 2 | |
Bare identifiers¶
A "bare identifier" is an expression that is exactly one name — letters, digits, underscore, hyphen — with no parentheses, sigils, or operators. Examples:
| Expression | Bare identifier? |
|---|---|
title |
yes |
user-count |
yes |
$title |
no — has sigil |
title() |
no — has parens |
title.uc |
no — has . |
A bare identifier in =, !=, &=, ~, or in a control-flow condition
(if, unless, elsif, while, repeat, given) resolves through:
- Locals — if
%locals{name}exists, use it. - Context method — otherwise if
context.^can(name)is true, callcontext.name. - Fallback to eval — otherwise the expression is handed to the normal
Raku evaluator. If the name isn't a valid Raku identifier in scope, you'll
get
X::HAML::Eval.
Context methods with arguments¶
A context method is also callable as a bare name with arguments, so a helper that takes parameters reads like a plain function call, no sigil:
1 2 3 | |
1 2 3 | |
Each context method named in an expression is bound as a lexical routine for
that render, dispatching to the context. Locals are still referenced with their
sigil ($label), since only standalone bare identifiers resolve to a local.
Built-in helpers (surround, list-of, …) remain callable as before. This
works in the interpreted renderer and the default direct-emit renderer.
1 2 3 4 | |
Overriding a built-in helper¶
A context method takes precedence over the built-in helper of the same name. To
replace surround, partial, list-of, or any other built-in for a given
render, define a method with that name on your context:
1 2 3 | |
1 2 | |
renders <div>[OVERRIDDEN]</div> instead of the built-in [inner]. Because the
default ViewContext exposes the built-ins as methods, every built-in name is
resolved through the context, so overriding one is just defining it.
Declaring helpers explicitly¶
Helper discovery normally introspects the context with .^methods. Helpers
installed in a way that introspection does not report, such as methods added at
runtime with ^add_method or handled through FALLBACK, are invisible to that
scan. Declare them with a haml-helper-names method that returns their names:
1 2 3 4 | |
When haml-helper-names is present its list is trusted verbatim: each entry is
kept if it is a valid helper name (starts with a lowercase letter or underscore,
then word characters or hyphens), then de-duplicated and sorted. The list is not
cached, so a context whose helper set varies per instance can return a different
list each time.
Default context¶
If you don't pass :context, the renderer constructs a
Template::HAML::ViewContext for you. It composes the
Template::HAML::HelpersRole role, so every built-in helper (surround,
escape-once, list-of, find-and-preserve, html-safe, capture-haml,
yield, content-for, tab-up, tab-down, haml-concat) is reachable as
a method on the context.
Extending the context¶
Two composition points are supported.
Mix in the helper role into your own class:
1 2 3 4 5 6 7 8 | |
The role brings the built-in helpers along, so you can add your own methods
without losing surround, list-of, etc.
Subclass ViewContext:
1 2 3 4 5 | |
Either approach works; the role form is slightly more flexible because you can compose other roles alongside it:
1 2 3 4 | |
Bare identifiers (hello, footer, surround, …) resolve against every
composed role.
Locals still win¶
When both a local and a context method have the same name, the local wins:
1 2 3 4 5 6 7 8 9 10 | |
This is the same precedence used by Rails-style view contexts and keeps per-render values from being silently shadowed by a stray helper method.