soranoba / bbmustache Goto Github PK
View Code? Open in Web Editor NEWBinary pattern match Based Mustache template engine for Erlang/OTP.
License: MIT License
Binary pattern match Based Mustache template engine for Erlang/OTP.
License: MIT License
When I click on Documentation the link is broken.
Currently one can supply a proplist or a map as Data
argument to render
function. However it may be useful to use a custom function as well. This should be very easy to implement: just add a when is_function
clause to find_data/2
...
I can create a PR unless anyone got objections.
Hey,
Currently i have need to use the mustache array index functionality.
Currently if i run:
1> bbmustache:render(<<"check out my template: {{a.1}}">>, #{"a" => ["foo", "bar"]})
<<"check out my template: ">>
From what i understand from mustache we should see:
1> bbmustache:render(<<"check out my template: {{a.1}}">>, #{"a" => ["foo", "bar"]})
<<"check out my template: foo">>
From looking around online both .1.
and .[1].
seem to be a supported syntax in most libraries, though the former seems more common to me.
I've got a PR that I can put up with the fix for this if you it's decided to be a good addition to the library.
When rendering template I get unexpected result:
iex(7)> :bbmustache.render "{{#foo}}bar\n{{/foo}} \n", [{'foo', fn x, _ -> "<#{x}>" end}]
"<bar\n{{/fo>"
Function receives argument with extra chars.
Must be <bar\n>
, got <bar\n{{/fo>
.
It seems to be a wrong parsing when closing tag is on a separate line:
iex(8)> :bbmustache.parse_binary "{{#foo}}bar\n{{/foo}} \n"
{:bbmustache, [{:"#", ["foo"], ["bar\n"], "bar\n{{/fo"}], [], [], [], []}
In the following code:
Lines 159 to 173 in 8ad09ad
it appears that render/3
accepts [render_option()]
whereas parse_binary/2
accepts [parse_option()]
. Unfortunately, the same data is used in both cases (Options
variable), and the types clash as defined:
Lines 120 to 130 in 8ad09ad
In that case, the only valid options for these functions are []
and [raise_on_partial_miss]
(essentially parse_option()
since parse_binary/2
forces a strict subset of acceptable types.
Either the parse_binary/2
function should see its typespec corrected to accept more types, or render/3
should see its typespec narrowed down to be more restrictive.
If both type specifications are correct, then there should be a function like this:
-spec split_options([render_option()]) -> {[render_option()], [parse_option()]}.
split_options(Options) ->
Parse = case lists:member(raise_on_partial_miss, Options) of
true -> [raise_on_partial_miss];
false -> []
end,
{Options, Parse}.
defined that can be called in:
-spec render(binary(), data(), [render_option()]) -> binary().
render(Bin, Data, Options) ->
{CompileOpts, ParseOpts} = split_options(Options),
compile(parse_binary(Bin, ParseOptions), Data, CompileOptions)
which I think should satisfy dialyzer without changing program semantics. You could also use something like extract_parse_options([render_option()]) -> [parse_option()]
and reuse Options
for the compile/3
call, which would be fine as well.
Hi,
Our team currently has a need to offer users the ability to output maps as types for mustache serialization. I was wondering if I could add custom serializers to the library so that the user could select specific serialization functions for types not supported currently?
Current Behaviour
:bbmustache.render("{{test}}", %{"test" => %{"weee" => 2}}, key_type: :binary)
** (ArgumentError) argument error
:erlang.iolist_to_binary(%{"weee" => 2})
(bbmustache) /Users/ethan/new_faspex4/f4-dev-setup/microservices/workflow-engine/deps/bbmustache/src/bbmustache.erl:233: :bbmustache.compile_impl/4
(bbmustache) /Users/ethan/new_faspex4/f4-dev-setup/microservices/workflow-engine/deps/bbmustache/src/bbmustache.erl:218: :bbmustache.compile/3
Proposed Behaviour
iex(1)> :bbmustache.render("{{test}}", %{"test" => %{"weee" => 2}}, key_type: :binary, map_serializer: fn x -> JSON.encode!() end)
"{\"weee\":2}"
When rendering templates from files, I noticed that the template file is accessed each time the template is compiled. For most scenarios this is probably not mission critical but still unneccessary IO.
I used erlydtl in previous projects and remembered that they compile the templates into their own modules which export a render function.
my_template:render([variables])
I liked the light weight nature of your library and so I wrote a lightweight template loader that creates modules for each template when the application is loaded.
In its current form it is probably not 100% suitable to integrate into your library and maybe you explicitly don't want such functionality.
If you are curious however, this is how I implemented it:
https://github.com/hukl/shortcut.io_new/blob/master/src/scio_view.erl
For each .mustache file in my views folder, a corresponding module with a render function is generated in which you pass in your content.
In my example I omitted the possibility to pass along render options or escape functions, but that could be easily added to make it more suitable for generic use in your library.
I have a LiveView text area that supports mustache templates to be entered on each keystroke, I call :bbmustache.render(text, %{"time" => DateTime.utc_now()}, key_type: :binary)
. As the user is typing their input like this It is currently {
then they hit one more {
(It currently {{
) the bbmustache will blow up with an unclosed_tags
error.
Is there a way to have bbmustache just ignore unclosed tags and render them as {{
until a closed tag is detected?
Rebar and relx templates use atoms as keys. Could mustache convert the data key to an atom if the value isn't found for the string, at least?
An option to crash on missing keys would be nice to have. Is this possible to implement?
I need to instantiate templates in files using erlang terms (also in other files).
I can open a PR with Main func on bbmustache and then we rebar3 escriptalise it (same as we do for other erlang tools like elvis or rebar3).
Does it make sense ?
Thanks
The package in hex.pm doesn't work properly with rebar3 because the version in the .app.src
is set to git
. This means rebar3 will always think it has the wrong version of bbmustache and continually try to upgrade when building a project.
If you use rebar3 hex cut
you can easily set the new version in .app.src
and publish https://asciinema.org/a/32q4wprb5crr3zpvldlv4f4bs
bbmustache v1.4.0
1> bbmustache:render(<<"{{#parent}}{{parent.child}}{{/parent}}">>, #{"parent" => true}).
** exception error: no function clause matching proplists:lookup("child",true) (proplists.erl, line 133)
in function bbmustache:find_data/2 (/xxxxxx/bbmustache/_build/dev/lib/bbmustache/src/bbmustache.erl, line 577)
in call from bbmustache:get_data_recursive/4 (/xxxxxxx/bbmustache/_build/dev/lib/bbmustache/src/bbmustache.erl, line 562)
in call from bbmustache:compile_impl/4 (/xxxxxxxx/bbmustache/_build/dev/lib/bbmustache/src/bbmustache.erl, line 187)
in call from bbmustache:compile_impl/4 (/xxxxxxxx/bbmustache/_build/dev/lib/bbmustache/src/bbmustache.erl, line 205)
in call from bbmustache:compile/3 (/xxxxxxx/bbmustache/_build/dev/lib/bbmustache/src/bbmustache.erl, line 172)
A crash occurs when parent is specified term other than bbmustache:data ()
.
Hi!
I've noticed that the newest version v1.1.0 is not backward compatible with the version v1.0.4 in the escaping context.
v1.04:
bbmustache:render(<<"{{{profile_dir}}}/bin/app1_402184">>, [{profile_dir, "/home/test/test"}], [{key_type, atom}]).
<<"/home/test/test/bin/app1_402184">>
v1.1.0:
bbmustache:render(<<"{{profile_dir}}/bin/app1_402184">>, [{profile_dir, "/home/test/test"}], [{key_type, atom}]).
<<"/home/test/test/bin/app1_402184">>
This example is taken from rebar3 common tests. I tried to upgrade bbmustache for rebar3, but due to this incompatibility I could not. Event, when I fixed the test, there is a problem with rebar3 itself. I think in many places it uses bbmustache:render function with "paths" as a variables, which are escaped in v.1.1.0.
It would be nice if new version would be backaward compatible or maybe as a workaround , the option unescape
could be passed to the render
function. It would be much easier only to add this parameter. What do you think about that?
Not a real issue, just opening this because I saw you used a mix.exs to publish to hex. You could also simply use rebar3: http://www.rebar3.org/v3.0/docs/publishing-packages
Thanks for publishing!
(in Elixir...)
A tag with a space in the middle doesn't get rendered. Is this intentional, or a bug?
iex(12)> :bbmustache.render("{{foo bar}}", [{'foo bar', "potato"}])
""
I can't find any thing in the Mustache docs about white space being disallowed.
Given the following map:
Map = #{
"author" => "Bill",
"os" => [
#{ "name" => "Ubuntu", "version" => "12" },
#{ "name" => "Ubuntu", "version" => "16" }
] }.
And the following template:
Tmpl = <<"Author: {{author}}, OSes: {{#os}}{{name}}/{{version}} ({{author}}) {{/os}}">>.
The following result is in error:
1> bbmustache:render(Tmpl, Map).
<<"Author: Bill, OSes: Ubuntu/12 () Ubuntu/16 () ">>
The output should be <<"Author: Bill, OSes: Ubuntu/12 (Bill) Ubuntu/16 (Bill) ">>
.
Hi
How to use bbmustache to allow to scape mustache variables, what I want is:
{environment, {{environment_var}}}.
rebar3 new my_template myservice
the environment config in sys.config
is generated {environment, }
because in the rebar3 template generation the variable is expanded and because does not exists in the context of the rebar3 template it will be replaced by “nothing”Any sugestion?
Thank you
Pedro
The issue is located within https://github.com/soranoba/bbmustache, specifically at
Line 278 in 7631da9
unicode:characters_to_binary
instead of iolist_to_binary
.
There are likely other locations for such calls, but sending in unicode data that has a wide representation can break when the codepoints get too large with the current calls.
data:
{"parent.child" : "before", "parent": {"child" : "after"}}
template:
{{parent.child}}
v1.1.x result:
before
v1.2.x result:
after
💡 Dot in the tag become the meaning as a separator.
💡 It had become an infinite loop in v1.1.x.
data:
{"sections" : [ {"section" : "1st section"}, {"section" : "2nd section"}]}
template:
{{>a}}
a.mustache
{{# sections }}
{{ section }}
{{>b}}
{{/ sections }}
b.mustache
{{ section }}
{{>c}}
c.mustache
Hello, indent paritals !
2nd line
3rd line
result:
1st section
1st section
Hello, indent paritals !
2nd line
3rd line
2nd section
2nd section
Hello, indent paritals !
2nd line
3rd line
💡 Escape characters is & " > <
💡 It did not reset the delimiters in v1.1.x.
data:
{"keep space" : "keep_space_tag", "deletespace" : "delete_space_tag"}
template:
{{ keep space }}{{ delete space }}
v1.1.x result:
keep_space_tag
v1.2.x result:
delete_space_tag
💡 Tag MUST NOT include the spaces. The current behavior is likely to change in the future.
Except for the Variables (that is, {{key}}
or {{{key}}}
, {{&key}}
),
this line is not included in the output, when there is only a tag on the line.
[{}]
This time, those that saw off the implementation.
In our Elixir project, we couldn't save partials in the filesystem. To work around this, we wrapped bbmustache
like so:
defmodule OurApp.Mustache do
@partial_templates %{
foo: "Foo",
bar: "Bar"
}
def render(bin, data) do
{:bbmustache, tags, partial_keys, options, indents, context_stack} =
:bbmustache.parse_binary(bin)
:bbmustache.compile(
{
:bbmustache,
tags,
parse_remaining_partials(partial_keys, [], []),
options,
indents,
context_stack
},
data,
key_type: :atom,
raise_on_context_miss: true
)
end
defp parse_remaining_partials([] = _keys, partials, _parsed_keys), do: partials
defp parse_remaining_partials([key | rest_of_keys], partials, parsed_keys) do
if key in parsed_keys do
parse_remaining_partials(rest_of_keys, partials, parsed_keys)
else
bin = Map.fetch!(@partial_templates, String.to_existing_atom(key))
{:bbmustache, tags, nested_keys, _options, _indents, _context_stack} =
:bbmustache.parse_binary(bin)
parse_remaining_partials(rest_of_keys ++ nested_keys, [{key, tags} | partials], [
key | parsed_keys
])
end
end
end
However, dialyzer is complaining about the opacity of :bbmustache.template()
being broken because it is matched against a pattern.
It would be nice if :bbmustache.template()
weren't opaque. Better yet, it would be nice if :bbmustache.parse_option()
allows get_partial_fun
which defaults to the current implementation of reading the partial from a file.
I'd love to submit a PR but my Erlang skills are severely lacking.
I can't find it in the mustache documentation, but using the demo ( http://mustache.github.io/#demo ) shows that this:
{{#mylist}}
{{.}}
{{/mylist}}
Will work on lists that look like this:
mylist: ["Item 1", "Item 2", "Item 3"]
Producing output like this:
Item 1
Item 2
Item 3
I was hoping that an erlang map that looked like this:
#{ "mylist" => ["Item 1", "Item 2", "Item 3"] }
would allow the same behavior, but it seems to be unsupported.
This is a rehash of #10, as i'm sure you're aware, bbmustache
is being used in relx
to template scripts and overlay vars, in this context we don't really need html escaping and it would be useful to disable it completely, this way users wouldn't have to use the {{{
syntax to escape binary values in overlays variables for example.
Hey,
I'm working on an elixir project and I'm having some problems to make partials to work. I've the partials in a local file, same place as the templates.
/templates/my_template.mustache
{{> partial}}
/templates/partial.mustache
<h1>Hey there</h1>
I keep getting an empty string when I render the my_template
. Should it work out of the box like this or am I missing something 😅 ? Is there any examples I could look into?
Thanks!
What am I doing wrong here?
{{#events}}
Hi {{act}}!
{{/events}}
Events = [{events, [{act, "Pink Floyd"}] }],
Parsed = bbmustache:parse_file(TemplateFileName),
bbmustache:compile(Parsed, Events).
The bbmustache.compile/2 returns <<"">>
and I expected <<"Hi Pink Floyd!">>
Thanks!
(Edit: added actual return value and what I expected.)
When including a non-existing file with {{> my_file_name}}
the error generated contains the file name of the original template, not the included file: {context_missing, {file_not_found, <<"the_template">>}}
. The error should be {context_missing, {file_not_found, <<"my_file_name">>}}
.
Running the variable test from the spec results in something I didn't expect:
1> bbmustache:render(<<"\"{{#foo}}{{.}} is {{foo}}{{/foo}}\"">>, #{"foo" => "bar"}).
<<"\"98 is bar97 is bar114 is bar\"">>
I was expecting the result <<"\"bar is bar\"">>
. Perhaps I'm missing something or is this a bug?
I think it would be useful to not do html escaping for non web page rendering purposes.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.