Просмотр исходного кода

* Added the ability to use filters in if/ifelse expressions, e.g. :

{% if var1|length_is:10 %}

{% ifequal months_list|length %}

* fixed a bug with erlydtl_runtime:are_equal which always
returned false when comparing 2 single character values, e.g. :

  erlydtl_runtime:are_equal("9", "9").
  erlydtl_runtime:are_equal("x", "x").

.. always returned false.  This would have manifested itself in 
the scenario {% ifequal foo "x" %} where foo was a variable with 
the value "x".

* the generated iolist now goes through an additional output 
filter called erlydtl_tuntime:stringify_final.  This will stringify
any atoms making it safe for filters to return atoms and also 
for atoms to be passed in to the render() function.
Colm Dougan 17 лет назад
Родитель
Сommit
810d8ff92e

+ 27 - 7
src/erlydtl/erlydtl_compiler.erl

@@ -240,11 +240,17 @@ forms(File, Module, BodyAst, BodyInfo, CheckSum) ->
                     ({XFile, XCheckSum}) -> 
                         erl_syntax:tuple([erl_syntax:string(XFile), erl_syntax:string(XCheckSum)])
                 end, BodyInfo#ast_info.dependencies))])]),     
-    
+
+   BodyAstTmp = erl_syntax:application(
+                    erl_syntax:atom(erlydtl_runtime),
+                    erl_syntax:atom(stringify_final),
+                    [BodyAst]
+                ),
+
     RenderInternalFunctionAst = erl_syntax:function(
         erl_syntax:atom(render2), 
             [erl_syntax:clause([erl_syntax:variable("Variables")], none, 
-                [BodyAst])]),   
+                [BodyAstTmp])]),   
     
     ModuleAst  = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]),
     
@@ -464,9 +470,13 @@ filter_ast(Variable, Filter, Context, TreeWalker) ->
 
 filter_ast_noescape(Variable, [{identifier, _, "escape"}], Context, TreeWalker) ->
     body_ast([Variable], Context, TreeWalker);
-filter_ast_noescape(Variable, [{identifier, _, Name} | Arg], Context, TreeWalker) ->
+filter_ast_noescape(Variable, Filter, Context, TreeWalker) ->
     {{VariableAst, Info}, TreeWalker2} = body_ast([Variable], Context, TreeWalker),
-    {{erl_syntax:application(erl_syntax:atom(erlydtl_filters), erl_syntax:atom(Name), 
+    VarValue = filter_ast1(Filter, VariableAst),
+    {{VarValue, Info}, TreeWalker2}.
+
+filter_ast1([{identifier, _, Name} | Arg], VariableAst) ->
+    erl_syntax:application(erl_syntax:atom(erlydtl_filters), erl_syntax:atom(Name), 
         [VariableAst | case Arg of 
                 [{string_literal, _, ArgName}] ->
                     [erl_syntax:string(unescape_string_literal(ArgName))];
@@ -474,8 +484,8 @@ filter_ast_noescape(Variable, [{identifier, _, Name} | Arg], Context, TreeWalker
                     [erl_syntax:integer(list_to_integer(ArgName))];
                 _ ->
                     []
-            end]), Info}, TreeWalker2}.
-
+            end]).
+ 
 search_for_escape_filter(_, _, #dtl_context{auto_escape = on}) ->
     on;
 search_for_escape_filter(_, _, #dtl_context{auto_escape = did}) ->
@@ -490,6 +500,8 @@ search_for_escape_filter({apply_filter, Variable, Filter}, _) ->
 search_for_escape_filter(_Variable, _Filter) ->
     off.
 
+
+
 resolve_variable_ast(VarTuple, Context) ->
     resolve_variable_ast(VarTuple, Context, 'fetch_value').
  
@@ -509,7 +521,15 @@ resolve_variable_ast({variable, {identifier, _, VarName}}, Context, FinderFuncti
         Val ->
             Val
     end,
-    {VarValue, VarName}.
+    {VarValue, VarName};
+
+resolve_variable_ast({apply_filter, Variable, Filter}, Context, FinderFunction) ->
+    {VarAst, VarName} = resolve_variable_ast(Variable, Context, FinderFunction),
+    VarValue = filter_ast1(Filter, erl_syntax:list([VarAst])),
+    {VarValue, VarName};
+
+resolve_variable_ast(What, _Context, _FinderFunction) ->
+   error_logger:error_msg("~p:resolve_variable_ast unhandled: ~p~n", [?MODULE, What]).
 
 resolve_scoped_variable_ast(VarName, Context) ->
     lists:foldl(fun(Scope, Value) ->

+ 5 - 2
src/erlydtl/erlydtl_filters.erl

@@ -128,13 +128,16 @@ last(Input) when is_binary(Input) ->
 last(Input) when is_list(Input) ->
     [lists:last(Input)].
 
+length([]) -> "0";
 length([Input]) when is_list(Input) ->
     integer_to_list(erlang:length(Input));
 length([Input]) when is_binary(Input) ->
     integer_to_list(size(Input)).
 
-length_is(Input, Number) when is_list(Input) ->
-    lists:concat([?MODULE:length(Input) =:= integer_to_list(Number)]).
+length_is(Input, Number) when is_list(Input), is_integer(Number) ->
+    length_is(Input, integer_to_list(Number));
+length_is(Input, Number) when is_list(Input), is_list(Number) ->
+    ?MODULE:length(Input) =:= Number.
 
 linebreaksbr([Input]) when is_list(Input) or is_binary(Input) ->
     linebreaksbr(Input);

+ 21 - 6
src/erlydtl/erlydtl_runtime.erl

@@ -38,16 +38,22 @@ fetch_value(Key, Data) ->
             Val
     end.
 
-are_equal([Arg1], Arg2) when is_list(Arg1) ->
-    are_equal(Arg1, Arg2);
-are_equal(Arg1, [Arg2]) when is_list(Arg1) ->
-    are_equal(Arg1, Arg2);
+are_equal(Arg1, Arg2) when Arg1 =:= Arg2 ->
+    true;
 are_equal(Arg1, Arg2) when is_binary(Arg1) ->
     are_equal(binary_to_list(Arg1), Arg2);
 are_equal(Arg1, Arg2) when is_binary(Arg2) ->
     are_equal(Arg1, binary_to_list(Arg2));
-are_equal(Arg1, Arg2) ->
-    Arg1 =:= Arg2.
+are_equal(Arg1, Arg2) when is_integer(Arg1) ->
+    are_equal(integer_to_list(Arg1), Arg2);
+are_equal(Arg1, Arg2) when is_integer(Arg2) ->
+    are_equal(Arg1, integer_to_list(Arg2));
+are_equal([Arg1], Arg2) when is_list(Arg1) ->
+    are_equal(Arg1, Arg2);
+are_equal(Arg1, [Arg2]) when is_list(Arg1) ->
+    are_equal(Arg1, Arg2);
+are_equal(_, _) ->
+    false.
 
 is_false("") ->
     true;
@@ -64,6 +70,15 @@ is_false(<<>>) ->
 is_false(_) ->
     false.
 
+stringify_final(In) ->
+   stringify_final(In, []).
+stringify_final([], Out) ->
+   lists:reverse(Out);
+stringify_final([El | Rest], Out) when is_atom(El) ->
+   stringify_final(Rest, [atom_to_list(El) | Out]);
+stringify_final([El | Rest], Out) ->
+   stringify_final(Rest, [El | Out]).
+
 init_counter_stats(List) ->
     init_counter_stats(List, undefined).
 

+ 61 - 4
src/tests/erlydtl_unittests.erl

@@ -73,6 +73,8 @@ tests() ->
                     <<"{% if var1 %}boo{% endif %}">>, [{var1, "0"}], <<>>},
                 {"If false",
                     <<"{% if var1 %}boo{% endif %}">>, [{var1, false}], <<>>},
+                {"If false string",
+                    <<"{% if var1 %}boo{% endif %}">>, [{var1, "false"}], <<"boo">>},
                 {"If undefined",
                     <<"{% if var1 %}boo{% endif %}">>, [{var1, undefined}], <<>>},
                 {"If other atom",
@@ -136,7 +138,16 @@ tests() ->
                     [{var1, "foo"}], <<"yay">>},
                 {"Compare literal to unequal variable",
                     <<"{% ifequal \"foo\" var1 %}boo{% endifequal %}">>,
-                    [{var1, "bar"}], <<>>}
+                    [{var1, "bar"}], <<>>},
+                {"Compare variable to literal (int string)",
+                    <<"{% ifequal var1 \"2\" %}yay{% else %}boo{% endifequal %}">>,
+                    [{var1, "2"}], <<"yay">>},
+                {"Compare variable to literal (int)",
+                    <<"{% ifequal var1 2 %}yay{% else %}boo{% endifequal %}">>,
+                    [{var1, 2}], <<"yay">>},
+                {"Compare variable to unequal literal (int)",
+                    <<"{% ifequal var1 2 %}boo{% else %}yay{% endifequal %}">>,
+                    [{var1, 3}], <<"yay">>}
             ]},
         {"ifequal/else", [
                 {"Compare variable to literal",
@@ -180,8 +191,8 @@ tests() ->
                     <<"{% ifnotequal \"foo\" var1 %}yay{% else %}boo{% endifnotequal %}">>,
                     [{var1, "bar"}], <<"yay">>}
             ]},
-        {"filters", [
-                {"Filter a literal",
+       {"filters", [
+               {"Filter a literal",
                     <<"{{ \"pop\"|capfirst }}">>, [],
                     <<"Pop">>},
                 {"Filters applied in order",
@@ -254,7 +265,53 @@ tests() ->
                 {"|urlencode",
                     <<"{{ url|urlencode }}">>, [{url, "You #$*@!!"}],
                     <<"You+%23%24%2A%40%21%21">>}
-            ]}
+            ]},
+        {"filters_if", [
+                {"Filter if 1.1",
+                    <<"{% if var1|length_is:0 %}Y{% else %}N{% endif %}">>,
+                     [{var1, []}],
+                     <<"Y">>},
+                {"Filter if 1.2",
+                    <<"{% if var1|length_is:1 %}Y{% else %}N{% endif %}">>,
+                     [{var1, []}],
+                     <<"N">>},
+                {"Filter if 1.3",
+                    <<"{% if var1|length_is:7 %}Y{% else %}N{% endif %}">>,
+                     [{var1, []}],
+                     <<"N">>},
+                {"Filter if 2.1",
+                    <<"{% if var1|length_is:0 %}Y{% else %}N{% endif %}">>,
+                     [{var1, ["foo"]}],
+                     <<"N">>},
+                {"Filter if 2.2",
+                    <<"{% if var1|length_is:1 %}Y{% else %}N{% endif %}">>,
+                     [{var1, ["foo"]}],
+                     <<"Y">>},
+                {"Filter if 2.3",
+                    <<"{% if var1|length_is:7 %}Y{% else %}N{% endif %}">>,
+                     [{var1, ["foo"]}],
+                     <<"N">>},
+                {"Filter if 3.1",
+                    <<"{% ifequal var1|length 0 %}Y{% else %}N{% endifequal %}">>,
+                     [{var1, []}],
+                     <<"Y">>},
+                {"Filter if 3.1",
+                    <<"{% ifequal var1|length 1 %}Y{% else %}N{% endifequal %}">>,
+                     [{var1, []}],
+                     <<"N">>},
+                {"Filter if 4.1",
+                    <<"{% ifequal var1|length 3 %}Y{% else %}N{% endifequal %}">>,
+                     [{var1, ["foo", "bar", "baz"]}],
+                     <<"Y">>},
+                {"Filter if 4.2",
+                    <<"{% ifequal var1|length 0 %}Y{% else %}N{% endifequal %}">>,
+                     [{var1, ["foo", "bar", "baz"]}],
+                     <<"N">>},
+                {"Filter if 4.3",
+                    <<"{% ifequal var1|length 1 %}Y{% else %}N{% endifequal %}">>,
+                     [{var1, ["foo", "bar", "baz"]}],
+                     <<"N">>}
+        ]}
     ].
 
 run_tests() ->