Skip to content

Commit

Permalink
Become to support raise_on_partial_miss. option() become to deprecated.
Browse files Browse the repository at this point in the history
  • Loading branch information
soranoba committed Aug 15, 2018
1 parent 2199a91 commit 4424f60
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 36 deletions.
18 changes: 15 additions & 3 deletions ct/bbmustache_SUITE.erl
Expand Up @@ -11,11 +11,12 @@

variables_ct/1, sections_ct1/1, sections_ct2/1, sections_ct3/1, sections_ct4/1,
lambdas_ct/1, comments_ct/1, partials_ct/1, delimiter_ct/1, dot_ct/1, dot_unescape_ct/1,
indent_partials_ct/1, not_found_partials_ct/1
indent_partials_ct/1, not_found_partials_ct1/1, not_found_partials_ct2/1, not_found_partials_ct3/1
]).
-define(ALL_TEST, [variables_ct, sections_ct1, sections_ct2, sections_ct3, sections_ct4,
lambdas_ct, comments_ct, partials_ct, delimiter_ct, dot_ct, dot_unescape_ct,
indent_partials_ct, not_found_partials_ct, context_stack_ct, context_stack_ct2]).
indent_partials_ct, not_found_partials_ct1, not_found_partials_ct2, not_found_partials_ct3,
context_stack_ct, context_stack_ct2]).

-define(config2, proplists:get_value).
-define(debug(X), begin io:format("~p", [X]), X end).
Expand Down Expand Up @@ -172,12 +173,23 @@ indent_partials_ct(Config) ->
Data = [{"sections", [[{"section", "1st section"}], [{"section", "2nd section"}]]}],
?assertEqual(File, bbmustache:compile(Template, ?debug((?config(data_conv, Config))(Data)), ?config2(options, Config, []))).

not_found_partials_ct(Config) ->
not_found_partials_ct1(Config) ->
Template = bbmustache:parse_file(filename:join([?config(data_dir, Config), <<"not_found_partial.mustache">>])),
{ok, File} = file:read_file(filename:join([?config(data_dir, Config), <<"not_found_partial.result">>])),

?assertEqual(File, bbmustache:compile(Template, ?debug((?config(data_conv, Config))([])), ?config2(options, Config, []))).

not_found_partials_ct2(Config) ->
?assertError({file_not_found, <<"does_not_exist_template">>, enoent},
bbmustache:parse_file(filename:join([?config(data_dir, Config), <<"not_found_partial.mustache">>]),
[raise_on_partial_miss])).

not_found_partials_ct3(Config) ->
Template = bbmustache:parse_file(filename:join([?config(data_dir, Config), <<"not_found_partial.mustache">>])),
?assertError({context_missing, {file_not_found, <<"does_not_exist_template">>}},
bbmustache:compile(Template, ?debug((?config(data_conv, Config))([])),
?config2(options, Config, []) ++ [raise_on_context_miss])).

context_stack_ct(Config) ->
Template = bbmustache:parse_file(filename:join([?config(data_dir, Config), <<"context.mustache">>])),
{ok, File} = file:read_file(filename:join([?config(data_dir, Config), <<"context.result">>])),
Expand Down
77 changes: 66 additions & 11 deletions doc/bbmustache.md
Expand Up @@ -35,6 +35,22 @@ assoc_data() = [{atom(), <a href="#type-data_value">data_value()</a>}] | [{binar



### <a name="type-compile_option">compile_option()</a> ###


<pre><code>
compile_option() = {key_type, atom | binary | string} | raise_on_context_miss | {escape_fun, fun((binary()) -&gt; binary())}
</code></pre>

- key_type:
-- Specify the type of the key in [`data/0`](#data-0). Default value is `string`.
- raise_on_context_miss:
-- If key exists in template does not exist in data, it will throw an exception (error).
- escape_fun:
-- Specify your own escape function.



### <a name="type-data">data()</a> ###


Expand All @@ -60,15 +76,32 @@ data_value() = <a href="#type-data">data()</a> | iodata() | number() | atom() |


<pre><code>
option() = {key_type, atom | binary | string} | raise_on_context_miss | {escape_fun, fun((binary()) -&gt; binary())}
option() = <a href="#type-render_option">render_option()</a>
</code></pre>

**Depreacted**



### <a name="type-parse_option">parse_option()</a> ###


<pre><code>
parse_option() = raise_on_partial_miss
</code></pre>

- raise_on_partial_miss
-- If template using partials does not found, it will throw an exception (error).



### <a name="type-render_option">render_option()</a> ###


<pre><code>
render_option() = <a href="#type-compile_option">compile_option()</a> | <a href="#type-parse_option">parse_option()</a>
</code></pre>

- key_type:
-- Specify the type of the key in [`data/0`](#data-0). Default value is `string`.
- raise_on_context_miss:
-- If key exists in template does not exist in data, it will throw an exception (error).
- escape_fun:
-- Specify your own escape function.



Expand All @@ -82,7 +115,7 @@ __abstract datatype__: `template()`
## Function Index ##


<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#compile-2">compile/2</a></td><td>Equivalent to <a href="#compile-3"><tt>compile(Template, Data, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#compile-3">compile/3</a></td><td>Embed the data in the template.</td></tr><tr><td valign="top"><a href="#parse_binary-1">parse_binary/1</a></td><td>Create a <a href="#template-0"><code>template/0</code></a> from a binary.</td></tr><tr><td valign="top"><a href="#parse_file-1">parse_file/1</a></td><td>Create a <a href="#template-0"><code>template/0</code></a> from a file.</td></tr><tr><td valign="top"><a href="#render-2">render/2</a></td><td>Equivalent to <a href="#render-3"><tt>render(Bin, Data, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#render-3">render/3</a></td><td>Equivalent to <a href="#compile-3"><tt>compile(parse_binary(Bin), Data, Options)</tt></a>.</td></tr></table>
<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#compile-2">compile/2</a></td><td>Equivalent to <a href="#compile-3"><tt>compile(Template, Data, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#compile-3">compile/3</a></td><td>Embed the data in the template.</td></tr><tr><td valign="top"><a href="#parse_binary-1">parse_binary/1</a></td><td>Equivalent to <a href="#parse_binary-2"><tt>parse_binary(Bin, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#parse_binary-2">parse_binary/2</a></td><td>Create a <a href="#template-0"><code>template/0</code></a> from a binary.</td></tr><tr><td valign="top"><a href="#parse_file-1">parse_file/1</a></td><td>Equivalent to <a href="#parse_file-2"><tt>parse_file(Filename, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#parse_file-2">parse_file/2</a></td><td>Create a <a href="#template-0"><code>template/0</code></a> from a file.</td></tr><tr><td valign="top"><a href="#render-2">render/2</a></td><td>Equivalent to <a href="#render-3"><tt>render(Bin, Data, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#render-3">render/3</a></td><td>Equivalent to <a href="#compile-3"><tt>compile(parse_binary(Bin), Data, Options)</tt></a>.</td></tr></table>


<a name="functions"></a>
Expand All @@ -105,7 +138,7 @@ Equivalent to [`compile(Template, Data, [])`](#compile-3).
### compile/3 ###

<pre><code>
compile(Bbmustache::<a href="#type-template">template()</a>, Data::<a href="#type-data">data()</a>, Options::[<a href="#type-option">option()</a>]) -&gt; binary()
compile(Bbmustache::<a href="#type-template">template()</a>, Data::<a href="#type-data">data()</a>, Options::[<a href="#type-compile_option">compile_option()</a>]) -&gt; binary()
</code></pre>
<br />

Expand All @@ -129,6 +162,17 @@ parse_binary(Bin::binary()) -&gt; <a href="#type-template">template()</a>
</code></pre>
<br />

Equivalent to [`parse_binary(Bin, [])`](#parse_binary-2).

<a name="parse_binary-2"></a>

### parse_binary/2 ###

<pre><code>
parse_binary(Bin::binary(), Options::[<a href="#type-parse_option">parse_option()</a>]) -&gt; <a href="#type-template">template()</a>
</code></pre>
<br />

Create a [`template/0`](#template-0) from a binary.

<a name="parse_file-1"></a>
Expand All @@ -140,6 +184,17 @@ parse_file(Filename::<a href="file.md#type-filename_all">file:filename_all()</a>
</code></pre>
<br />

Equivalent to [`parse_file(Filename, [])`](#parse_file-2).

<a name="parse_file-2"></a>

### parse_file/2 ###

<pre><code>
parse_file(Filename::<a href="file.md#type-filename_all">file:filename_all()</a>, Options::[<a href="#type-parse_option">parse_option()</a>]) -&gt; <a href="#type-template">template()</a>
</code></pre>
<br />

Create a [`template/0`](#template-0) from a file.

<a name="render-2"></a>
Expand All @@ -153,14 +208,14 @@ render(Bin::binary(), Data::<a href="#type-data">data()</a>) -&gt; binary()

Equivalent to [`render(Bin, Data, [])`](#render-3).

__See also:__ [compile/2](#compile-2), [parse_binary/1](#parse_binary-1), [parse_file/1](#parse_file-1), [render/2](#render-2).
__See also:__ [compile/2](#compile-2), [compile_option/0](#compile_option-0), [parse_binary/1](#parse_binary-1), [parse_file/1](#parse_file-1), [parse_option/0](#parse_option-0), [render/2](#render-2).

<a name="render-3"></a>

### render/3 ###

<pre><code>
render(Bin::binary(), Data::<a href="#type-data">data()</a>, Options::[<a href="#type-option">option()</a>]) -&gt; binary()
render(Bin::binary(), Data::<a href="#type-data">data()</a>, Options::[<a href="#type-render_option">render_option()</a>]) -&gt; binary()
</code></pre>
<br />

Expand Down
76 changes: 54 additions & 22 deletions src/bbmustache.erl
Expand Up @@ -16,15 +16,20 @@
render/2,
render/3,
parse_binary/1,
parse_binary/2,
parse_file/1,
parse_file/2,
compile/2,
compile/3
]).

-export_type([
template/0,
data/0,
option/0
option/0, % depreacted
compile_option/0,
parse_option/0,
render_option/0
]).

%%----------------------------------------------------------------------------------------------------------------------
Expand All @@ -44,6 +49,8 @@

-define(RAISE_ON_CONTEXT_MISS_ENABLED(Options),
proplists:get_bool(raise_on_context_miss, Options)).
-define(RAISE_ON_PARTIAL_MISS_ENABLED(Options),
proplists:get_bool(raise_on_partial_miss, Options)).

-type key() :: binary().
%% Key MUST be a non-whitespace character sequence NOT containing the current closing delimiter. <br />
Expand Down Expand Up @@ -82,7 +89,7 @@
partials = [] :: [{key(), [tag()]} | key()],
%% When it include key(), The key already parsed but its file does not exist.

options = [] :: [option()],
options = [] :: [compile_option()],
indents = [] :: [binary()],
context_stack = [] :: [data()]
}).
Expand All @@ -109,16 +116,27 @@

-type assoc_data() :: [{atom(), data_value()}] | [{binary(), data_value()}] | [{string(), data_value()}].

-type option() :: {key_type, atom | binary | string}
| raise_on_context_miss
| {escape_fun, fun((binary()) -> binary())}.
-type parse_option() :: raise_on_partial_miss.
%% - raise_on_partial_miss
%% -- If template using partials does not found, it will throw an exception (error).

-type compile_option() :: {key_type, atom | binary | string}
| raise_on_context_miss
| {escape_fun, fun((binary()) -> binary())}.
%% - key_type:
%% -- Specify the type of the key in {@link data/0}. Default value is `string'.
%% - raise_on_context_miss:
%% -- If key exists in template does not exist in data, it will throw an exception (error).
%% - escape_fun:
%% -- Specify your own escape function.

-type render_option() :: compile_option() | parse_option().
%% @see compile_option/0
%% @see parse_option/0

-type option() :: render_option().
%% **Depreacted**

-ifdef(namespaced_types).
-type maps_data() :: #{atom() => data_value()} | #{binary() => data_value()} | #{string() => data_value()}.
-type data() :: maps_data() | assoc_data().
Expand All @@ -141,26 +159,36 @@ render(Bin, Data) ->
render(Bin, Data, []).

%% @equiv compile(parse_binary(Bin), Data, Options)
-spec render(binary(), data(), [option()]) -> binary().
-spec render(binary(), data(), [render_option()]) -> binary().
render(Bin, Data, Options) ->
compile(parse_binary(Bin), Data, Options).
compile(parse_binary(Bin, Options), Data, Options).

%% @doc Create a {@link template/0} from a binary.
%% @equiv parse_binary(Bin, [])
-spec parse_binary(binary()) -> template().
parse_binary(Bin) when is_binary(Bin) ->
parse_binary_impl(#state{}, Bin).
parse_binary(Bin, []).

%% @doc Create a {@link template/0} from a file.
%% @doc Create a {@link template/0} from a binary.
-spec parse_binary(binary(), [parse_option()]) -> template().
parse_binary(Bin, Options) ->
parse_binary_impl(#state{}, Bin, Options).

%% @equiv parse_file(Filename, [])
-spec parse_file(file:filename_all()) -> template().
parse_file(Filename) ->
parse_file(Filename, []).

%% @doc Create a {@link template/0} from a file.
-spec parse_file(file:filename_all(), [parse_option()]) -> template().
parse_file(Filename, Options) ->
State = #state{dirname = filename:dirname(Filename)},
case to_binary(filename:extension(Filename)) of
<<".mustache">> = Ext ->
Partials = [Key = to_binary(filename:basename(Filename, Ext))],
parse_binary_impl(State#state{partials = Partials}, #?MODULE{data = [{'>', Key, <<>>}]});
parse_binary_impl(State#state{partials = Partials}, #?MODULE{data = [{'>', Key, <<>>}]}, Options);
_ ->
case file:read_file(Filename) of
{ok, Bin} -> parse_binary_impl(State, Bin);
{ok, Bin} -> parse_binary_impl(State, Bin, Options);
_ -> error(?FILE_ERROR, [Filename])
end
end.
Expand All @@ -179,7 +207,7 @@ compile(Template, Data) ->
%% '''
%% Data support assoc list or maps (OTP17 or later). <br />
%% All key in assoc list or maps MUST be same type.
-spec compile(template(), data(), [option()]) -> binary().
-spec compile(template(), data(), [compile_option()]) -> binary().
compile(#?MODULE{data = Tags} = T, Data, Options) ->
case check_data_type(Data) of
false -> error(function_clause, [T, Data]);
Expand Down Expand Up @@ -249,28 +277,32 @@ compile_impl([Bin | T], Map, Result, State) ->
compile_impl(T, Map, [Bin | Result], State).

%% @see parse_binary/1
-spec parse_binary_impl(state(), Input | template()) -> template() when
-spec parse_binary_impl(state(), Input | template(), [parse_option()]) -> template() when
Input :: binary().
parse_binary_impl(#state{partials = []}, Template = #?MODULE{}) ->
parse_binary_impl(#state{partials = []}, Template = #?MODULE{}, _Options) ->
Template;
parse_binary_impl(State = #state{partials = [P | PartialKeys]}, Template = #?MODULE{partials = Partials}) ->
parse_binary_impl(State = #state{partials = [P | PartialKeys]}, Template = #?MODULE{partials = Partials}, Options) ->
case proplists:is_defined(P, Partials) of
true -> parse_binary_impl(State#state{partials = PartialKeys}, Template);
true -> parse_binary_impl(State#state{partials = PartialKeys}, Template, Options);
false ->
Filename0 = <<P/binary, ".mustache">>,
Dirname = State#state.dirname,
Filename = ?IIF(Dirname =:= <<>>, Filename0, filename:join([Dirname, Filename0])),
case file:read_file(Filename) of
{ok, Input} ->
{State1, Data} = parse(State, Input),
parse_binary_impl(State1, Template#?MODULE{partials = [{P, Data} | Partials]});
_ ->
parse_binary_impl(State#state{partials = PartialKeys}, Template#?MODULE{partials = [P | Partials]})
parse_binary_impl(State1, Template#?MODULE{partials = [{P, Data} | Partials]}, Options);
{error, Reason} ->
case ?RAISE_ON_PARTIAL_MISS_ENABLED(Options) of
true -> error({?FILE_ERROR, P, Reason});
false -> parse_binary_impl(State#state{partials = PartialKeys},
Template#?MODULE{partials = [P | Partials]}, Options)
end
end
end;
parse_binary_impl(State, Input) ->
parse_binary_impl(State, Input, Options) ->
{State1, Data} = parse(State, Input),
parse_binary_impl(State1, #?MODULE{data = Data}).
parse_binary_impl(State1, #?MODULE{data = Data}, Options).

%% @doc Analyze the syntax of the mustache.
-spec parse(state(), binary()) -> {#state{}, [tag()]}.
Expand Down

0 comments on commit 4424f60

Please sign in to comment.