Erlang string templating with macaroni-filled syntax. Based on dactyl.
$ rebar3 compile
Compile a binary into a template:
1> {ok, Template} = roni:compile(<<"my ~adjective~; template">>).
Render a template:
2> IoList = roni:render(Template, #{<<"adjective">> => <<"noodly">>}).
[<<"my ">>, <<"noodly">>, <<" template">>]
3> iolist_to_binary(IoList).
<<"my noodly template">>
Render a template without compiling it first:
4> TemplateBin = <<"my ~adjective~; template">>.
5> Bindings = [{<<"adjective">>, <<"noodly">>}].
6> Rendered = iolist_to_binary(roni:render(TemplateBin, Bindings)).
You can also compile and/or render templates located in files with
compile_file/1
and render_file/2
.
Roni makes extensive use of binaries and iolists, opting to use sub-binaries instead of copying from the template string. Roni also adds the ability to use maps for bindings instead of property lists.
100% stolen lovingly borrowed from dactyl, which supposedly borrowed from Common Lisp's FORMAT
function.
Basic substitution:
roni:render(<<"my ~adj~; template">>, #{<<"adj">> => <<"noodly">>})
[<<"my ">>,<<"noodly">>,<<" template">>]
Conditional substitution:
1> N = 5.
2> roni:render(<<"I've got ~any~?~n~;~:zero~; problems">>,
#{<<"any">> => N > 0, <<"n">> => N}).
[<<"I've got ">>,[<<"5">>],<<" problems">>]
3> roni:render(<<"dramatic pause: ~go~?you won't see this~;">>,
#{<<"go">> => false}).
[<<"dramatic pause: ">>,[]]
List substitution:
1> Bindings = #{<<"things">> => [#{<<"num">> => 1},
#{<<"num">> => 2},
#{<<"num">> => 3}]}.
2> Template = <<"my things: ~things~[Thing ~num~;. ~]">>.
2> iolist_to_binary(roni:render(Template, Bindings)).
<<"my things: Thing 1. Thing 2. Thing 3. ">>
Substitution with custom formatting (using io_lib:format/2
):
1> Bindings = #{<<"terms">> => [{a, 1}, {b, 2}, {c, 3}]}.
2> Template = <<"my terms: ~terms~{~n1. ~p~n2. ~p~n3. ~p~n~}">>.
3> iolist_to_binary(roni:render(Template, Bindings)).
<<"my terms: \n1. {a,1}\n2. {b,2}\n3. {c,3}\n">>
Like dactyl, roni allows the user to pass an
Erlang module in order to perform substitution. Bindings passed to roni:render
will be checked against the exports list of the callback module. Callback
functions must accept a single argument: The map of all bindings passed to
roni:render
.
-module(foo).
-export([bar/1, baz/1]).
bar(_) -> <<"bar">>.
baz(Bindings) -> io_lib:format("~p", [Bindings]).
1> Template = <<"bar: ~bar~;. baz: ~baz~;">>.
2> Bindings = {foo, #{<<"unused">> => <<"yup">>}}.
3> iolist_to_binary(roni:render(Template, Bindings)).
<<"bar: bar. baz: #{<<\"unused\">> => <<\"yup\">>}">>