bbmustache

repository·master·Indexed 41 days ago

https://github.com/soranoba/bbmustache

A logic-less binary pattern match-based Mustache template engine for Erlang/OTP. It avoids regular expressions for improved performance and reliability, providing compliance with the Mustache v1.4.1 specification. Features include support for maps and associative arrays as data sources, custom HTML escaping via escape_fun, and a two-phase parse and compile process (using parse_binary/1, parse_file/1, and compile/2) to optimize rendering performance.

Tokens
2.6K
Snippets
10
Records
13
Agent score
52%

What's inside bbmustache

  1. Handle HTML escaping in Mustache templates

    master

    By default, bbmustache escapes HTML characters. You can control this in two ways:

    1. Triple Braces: Use {{{tag}}} in your template to render unescaped content.
    2. Custom Escape Function: Use the escape_fun option in bbmustache:render/3 to provide a custom escaping logic (or an identity function to disable it entirely).
    %% Use triple braces for unescaped output
    1> bbmustache:render(<<"<h1>{{{title}}}</h1>">>, #{"title" => "I like Erlang & mustache"}).
    <<"<h1>I like Erlang & mustache</h1>">
    
    %% Use escape_fun to disable escaping
    1> bbmustache:render(<<"<h1>{{title}}</h1>">>, #{"title" => "I like Erlang & mustache"}, [{escape_fun, fun(X) -> X end}]).
    <<"<h1>I like Erlang & mustache</h1>">>
  2. Understand Mustache key syntax and dot notation

    master

    In bbmustache, keys used in templates follow specific rules:

    • Type: Keys must be a non-whitespace character sequence that does not contain the current closing delimiter.
    • Dot Notation (.):
      1. parent.child: Used to find the child key within the parent context.
      2. . (single dot): Represents the current context.

    When using recursive_data(), you can choose the key type (atom, binary, or string) via key_type in compile_option().

  3. Install bbmustache as a library

    master

    To use bbmustache in your Erlang project, add it to your dependency manager configuration.

    For rebar (rebar.config): Use the git dependency pointing to the master branch.

    For rebar3 (rebar.config): Add bbmustache to your deps list.

    %% rebar (rebar.config)
    
    {deps,
      [
       {bbmustache, ".*", {git, "https://github.com/soranoba/bbmustache.git", {branch, "master"}}}
      ]}.
    
    %% rebar3 (rebar.config)
    
    {deps, [bbmustache]}.
  4. Compile pre-parsed templates with bbmustache:compile/2

    master

    To optimize performance, separate the parsing phase from the rendering phase. This avoids re-parsing the template structure for every render operation.

    1. Use bbmustache:parse_binary/1 to parse a binary template.
    2. Use bbmustache:parse_file/1 to parse a template from a file.
    3. Use bbmustache:compile/2 to render the parsed template with your data.
    %% Using parsed binary
    Template1 = bbmustache:parse_binary(<<"{{name}}">>).
    Result = bbmustache:compile(Template1, #{"name" => "hoge"}).
    
    %% Using parsed file
    Template2 = bbmustache:parse_file(<<"./hoge.mustache" >>).
    Result = bbmustache:compile(Template2, [{"name", "hoge"}]).
  5. Render templates using bbmustache:render/2 or /3

    master

    The bbmustache:render/2 function performs a quick render of a binary template using a data source. The bbmustache:render/3 function allows passing an additional list of options.

    Supported data sources:

    • Maps: #{key => value}
    • Associative Arrays: [{key, value}]

    Options available in render/3:

    • escape_fun: A function used to handle HTML escaping. To disable escaping, provide an identity function: fun(X) -> X end.
    • key_type: Specifies the type of keys to expect in the data source (atom or binary).
    • value_serializer: A function used to transform Erlang terms before rendering. This is useful for custom serialization (e.g., JSON encoding).

    Note on performance: For better performance, use parse_binary/1 or parse_file/1 to create a template once, then use compile/2 to render it multiple times with different data.

    %% Quick render with a map
    1> bbmustache:render(<<"{{name}}">>, #{"name" => "hoge"}).
    <<"hoge">
    
    %% Quick render with an associative array
    1> bbmustache:render(<<"{{name}}">>, [{"name", "hoge"}]).
    <<"hoge">
    
    %% Render with custom escaping
    1> bbmustache:render(<<"<h1>{{title}}</h1>">>, #{"title" => "I like Erlang & mustache"}, [{escape_fun, fun(X) -> X end}]).
    <<"<h1>I like Erlang & mustache</h1>">>
    
    %% Render with custom key type and value serializer
    1> bbmustache:render(<<"<h1>{{{title}}}</h1>">>, #{title => #{<<"nested">> => <<"value">>}}, [{key_type, binary}, {value_serializer, fun(X) -> jsone:encode(X) end}]).
    <<"<h1>{\"nested\": \"value\"}</h1>">>
  6. Parse a template from a binary

    master

    Use bbmustache:parse_binary/2 to create a template() object from a binary string. You can provide parse_option() to customize how partials are handled.

    Available parse_option() values:

    • partial_file_reader: A function fun((Dirname::binary(), Key::key()) -> Data::binary()) that delegates reading of partial files. This allows reading partials from sources other than the local file system.
    • raise_on_partial_miss: If a template used in a partial is not found, an exception is thrown.
    Template = bbmustache:parse_binary(<<"{{name}}">>).
    %% Or with options:
    Template = bbmustache:parse_binary(<<"{{name}}">>, [raise_on_partial_miss]).
  7. Parse a template from a file

    master

    Use bbmustache:parse_file/2 to create a template() object from a file on disk. You can provide parse_option() to customize partial loading.

    Available parse_option() values:

    • partial_file_reader: A function fun((Dirname::binary(), Key::key()) -> Data::binary()) to delegate reading of partial files.
    • raise_on_partial_miss: Throws an exception if a partial template is not found.
    Template = bbmustache:parse_file("template.mustache").
    %% Or with options:
    Template = bbmustache:parse_file("template.mustache", [raise_on_partial_miss]).
  8. Compile a template with data

    master

    Use bbmustache:compile/3 to embed data into a template. The data can be an associative array or a map, but all keys must be of the same type. You can provide compile_option() to customize behavior.

    Available compile_option() values:

    • key_type: Specify the type of the key in data/0. Default is string.
    • raise_on_context_miss: If a key exists in the template but not in the data, an exception is thrown.
    • escape_fun: A custom function fun((binary()) -> binary()) for escaping.
    • value_serializer: A custom function fun((any()) -> iodata()) to convert terms to iodata during templating.
    Template = bbmustache:parse_binary(<<"{{name}}">>).
    bbmustache:compile(Template, #{name => "Alice"}).
    %% Result: <<"Alice">>
  9. Render a binary template with data

    master

    Use bbmustache:render/3 to render a raw binary template using provided data and options. This is functionally equivalent to parsing the binary into a template and then calling compile/3.

    render(Bin, Data, Options) is equivalent to compile(parse_binary(Bin), Data, Options).

    Available render_option() values:

    %% Equivalent to:
    bbmustache:render(<<"{{name}}">>, #{name => "Alice"}, []).
  10. Compare bbmustache spec compliance

    master

    The benchmarks/README.md file contains a detailed comparison matrix of how bbmustache implements various parts of the Mustache v1.4.1 specification compared to mustache.erl. This includes compliance for:

    • Comments: Inline, multiline, and standalone variations.
    • Delimiters: Pair behavior, special characters, and whitespace handling.
    • Interpolation: Basic, triple mustache (unescaped), ampersand, and dotted name resolution.
    • Inverted Sections: Truthy/falsey handling and context misses.
    • Partials: Basic behavior, recursion, and failed lookups.
    • Sections: Truthy/falsey, list contexts, and implicit iterators.
    • Dynamic Names: Name resolution and double dereferencing.
    • Lambdas: Inverted section support.