A handlebars environment
Synopsis
Declared in <mrdocs/Support/Handlebars.hpp>
class Handlebars;
Types
Name |
Member Functions
Name |
Description |
|
Construct a handlebars environment |
Register a helper accessible by any template in the environment. |
|
Register a logger |
|
Register a partial |
|
Render a handlebars template |
|
Render a handlebars template |
|
Render a handlebars template |
|
Render a handlebars template |
|
Unregister a helper |
|
Unregister a partial |
Non-Member Functions
Name |
Description |
Register all the Antora helpers into a Handlebars instance |
|
Register all the built‐in helpers into a Handlebars instance |
|
Register contructor helpers into a Handlebars instance |
|
Register helpers to manipulate composite data types |
|
Register logical helpers into a Handlebars instance |
|
Register math helpers into a Handlebars instance |
|
Register string helpers into a Handlebars instance |
|
Register type helpers into a Handlebars instance |
|
Register a JavaScript helper function |
Description
This class implements a handlebars template environment.
It is analogous to the complete state held by the handlebars.js module, including registered helpers and partials.
In the general case, handlebars.js provides a global Handlebars
environment where helpers and partials are registered:
{.js}
let template = Handlebars.compile("{{foo}}");
let result = template({foo: "bar"});
but also provides a way to create a new isolated environment with its own helpers and partials:
{.js}
let OtherHandlebars = Handlebars.create();
let template = OtherHandlebars.compile("{{foo}}");
let result = template({foo: "bar"});
In this implementation, however, there's no global environment. A new environment needs to be created explicitly be instantiating this class:
{.cpp}
Handlebars env;
dom::Object context;
context["foo"] = "bar";
std::string result = env.render("{{ foo }}", context);
assert(result == "bar");
A handlebars template can be rendered using the context data provided as a dom::Value
, which is usually a dom::Object
at the first level when calling render
.
In the most general case, the result can returned as a string or rendered directly to a buffer or stream. The render function provides an overload that allows the caller to provide an output stream where the template will be rendered directly without allocating a string:
{.cpp}
Handlebars env;
dom::Object context;
context["foo"] = "bar";
env.render_to(std::cout, "{{ foo }}", context);
// prints "bar" to stdout
{.cpp}
Handlebars env;
dom::Object context;
context["foo"] = "bar";
std::string result;
env.render_to(result, "{{ foo }}", context);
assert(result == "bar");
Design considerations:
The following notes include some design considerations for the current implementation of this class. These are intended to describe the current state of the class rather than to provide the final specification of the class behavior.
Compiled templates:
Unlike handlebars.js, this implementation renders the template directly to the output stream, without requiring an intermediary object to store a representation of the compiled template (Handlebars.precompile
) or an intermediary callable object required to ultimately render the template (Handlebars.precompile
).
The rationale is that there is not much benefit in pre‐compiling templates in C++, since both iterating the input string and a pre‐compiled template would have very similar costs even in optimal implementations of the compiled template.
The most significant benefit of pre‐compiling templates in C++ would be the faster identification of the ends of blocks, which would allow the engine iterate the block only once. For this reason, compiled templates will still be considered in a future version of this sub‐library.
Also note that compiled templates cannot avoid exceptions, because a compiled template can still invoke a helper that throws exceptions and evaluate dynamic expressions that cannot be identified during the first pass.
Incremental rendering and compilation:
Although this is not supported by handlebars.js, it's common for C++ template engines to support incremental rendering, where the template is compiled or rendered in chunks as it is parsed. This implementation does not yet support this feature.
This is useful for streaming templates, where the template is rendered to a stream as it is parsed, without requiring the entire template to be parsed and compiled before rendering starts.
There are two types of incremental rendering and compilation supported by this implementation:
‐ Incremental rendering of a partial template input to a stream ‐ Incremental rendering into an output buffer of fixed size
In each of these cases, the template is rendered in chunks until the end of the partial template is reached or the output buffer is full.
In a scenario with compiled templates, the complexity of incremental rendering needs to be implemented for both compilation and rendering.
The main difficulty to implement incremental rendering for handlebars.js is that helpers can be invoked from anywhere in the template, and most content is actually rendered by helpers. This means that helpers would need to be able to interoperate with whatever mechanism is designed to support suspension in this recursive‐coroutine‐like interface.
Error propagation:
The main logic to render a template is implemented in the render
function, does not throws by itself. How identifying the next tag in a template string, the algorithms uses a loose implementation where unclosed tags are rendered as‐is instead of throwing errors.
However, helpers are allowed to throw exceptions to propagate errors, so the render
function is not noexcept
.
For this reason, exceptions thrown by helpers are in fact exceptional conditions that should be handled by the caller. In general, exceptions can be avoided completely by not throwing exceptions from helpers.
See Also
https://handlebarsjs.com/
Created with MrDocs