Browse Source

Merge pull request #27 from garazdawi/regroup_extend

Allow nested keys in regroup and dictsort
Evan Miller 13 years ago
parent
commit
914781a403

+ 12 - 6
src/erlydtl_compiler.erl

@@ -585,8 +585,8 @@ body_ast(DjangoParseTree, Context, TreeWalker) ->
                 include_ast(unescape_string_literal(File), Args, Context#dtl_context.local_scopes, Context, TreeWalkerAcc);
             ({'include_only', {string_literal, _, File}, Args}, TreeWalkerAcc) ->
                 include_ast(unescape_string_literal(File), Args, [], Context, TreeWalkerAcc);
-            ({'regroup', {ListVariable, {identifier, _, Attribute}, {identifier, _, NewVariable}}, Contents}, TreeWalkerAcc) ->
-                regroup_ast(ListVariable, Attribute, NewVariable, Contents, Context, TreeWalkerAcc);
+            ({'regroup', {ListVariable, Grouper, {identifier, _, NewVariable}}, Contents}, TreeWalkerAcc) ->
+                regroup_ast(ListVariable, Grouper, NewVariable, Contents, Context, TreeWalkerAcc);
             ({'spaceless', Contents}, TreeWalkerAcc) ->
                 spaceless_ast(Contents, Context, TreeWalkerAcc);
             ({'ssi', Arg}, TreeWalkerAcc) ->
@@ -1038,20 +1038,26 @@ with_ast(ArgList, Contents, Context, TreeWalker) ->
                         erl_syntax:clause(lists:map(fun({_, Var}) -> Var end, NewScope), none,
                             [InnerAst])]), ArgAstList), merge_info(ArgInfo, InnerInfo)}, TreeWalker2}.
 
-regroup_ast(ListVariable, AttributeName, LocalVarName, Contents, Context, TreeWalker) ->
+regroup_ast(ListVariable, GrouperVariable, LocalVarName, Contents, Context, TreeWalker) ->
     {{ListAst, ListInfo}, TreeWalker1} = value_ast(ListVariable, false, Context, TreeWalker),
     NewScope = [{LocalVarName, erl_syntax:variable(lists:concat(["Var_", LocalVarName]))}],
 
     {{InnerAst, InnerInfo}, TreeWalker2} = body_ast(Contents, 
         Context#dtl_context{ local_scopes = [NewScope|Context#dtl_context.local_scopes] }, TreeWalker1),
 
-    {{erl_syntax:application(
+    Ast = {erl_syntax:application(
                 erl_syntax:fun_expr([
                         erl_syntax:clause([erl_syntax:variable(lists:concat(["Var_", LocalVarName]))], none,
                             [InnerAst])]), 
                 [erl_syntax:application(erl_syntax:atom(erlydtl_runtime), erl_syntax:atom(regroup),
-                        [ListAst, erl_syntax:atom(AttributeName)])]), merge_info(ListInfo, InnerInfo)},
-        TreeWalker2}.
+                        [ListAst, regroup_filter(GrouperVariable,[])])]), merge_info(ListInfo, InnerInfo)},
+    {Ast,TreeWalker2}.
+
+regroup_filter({attribute,{{identifier,_,Ident},Next}},Acc) ->
+    regroup_filter(Next,[erl_syntax:atom(Ident)|Acc]);
+regroup_filter({variable,{identifier,_,Var}},Acc) ->
+    erl_syntax:list([erl_syntax:atom(Var)|Acc]).
+
 
 for_loop_ast(IteratorList, LoopValue, Contents, {EmptyContentsAst, EmptyContentsInfo}, Context, TreeWalker) ->
     Vars = lists:map(fun({identifier, _, Iterator}) -> 

+ 14 - 3
src/erlydtl_filters.erl

@@ -218,10 +218,21 @@ default_if_none(undefined, Default) ->
 default_if_none(Input, _) ->
     Input.
 
-%% @doc Takes a list of dictionaries and returns that list sorted by the key given in the argument.
+%% @doc Takes a list of dictionaries or proplists and returns that list sorted by the key given in the argument.
+dictsort(DictList, Key) when is_binary(Key) ->
+    dictsort(DictList, [binary_to_atom(B,latin1) ||
+			   B <- binary:split(Key,<<".">>)]);
 dictsort(DictList, Key) ->
-    case lists:all(fun(Dict) -> dict:is_key(Key, Dict) end, DictList) of
-        true -> lists:sort(fun(K1,K2) -> dict:find(Key,K1) =< dict:find(Key,K2) end, DictList);
+    case lists:all(
+	   fun(Dict) ->
+		   erlydtl_runtime:find_deep_value(Key, Dict) /= undefined
+	   end, DictList) of
+        true ->
+	    lists:sort(
+	      fun(K1,K2) ->
+		      erlydtl_runtime:find_deep_value(Key,K1) =<
+			  erlydtl_runtime:find_deep_value(Key,K2)
+	      end, DictList);
         false -> error
     end.
 

+ 1 - 1
src/erlydtl_parser.yrl

@@ -337,7 +337,7 @@ IfNotEqualExpression -> Value : '$1'.
 EndIfNotEqualBraced -> open_tag endifnotequal_keyword close_tag.
 
 RegroupBlock -> RegroupBraced Elements EndRegroupBraced : {regroup, '$1', '$2'}.
-RegroupBraced -> open_tag regroup_keyword Value by_keyword identifier as_keyword identifier close_tag : {'$3', '$5', '$7'}.
+RegroupBraced -> open_tag regroup_keyword Value by_keyword Value as_keyword identifier close_tag : {'$3', '$5', '$7'}.
 EndRegroupBraced -> open_tag endregroup_keyword close_tag.
 
 SpacelessBlock -> open_tag spaceless_keyword close_tag Elements open_tag endspaceless_keyword close_tag : {spaceless, '$4'}.

+ 9 - 2
src/erlydtl_runtime.erl

@@ -49,6 +49,13 @@ find_value(Key, Tuple) when is_tuple(Tuple) ->
             end
     end.
 
+find_deep_value([Key|Rest],Item) ->
+    case find_value(Key,Item) of
+	undefined -> undefined;
+	NewItem -> find_deep_value(Rest,NewItem)
+    end;
+find_deep_value([],Item) -> Item.
+
 fetch_value(Key, Data) ->
     case find_value(Key, Data) of
         undefined ->
@@ -65,9 +72,9 @@ regroup([], _, []) ->
 regroup([], _, [[{grouper, LastGrouper}, {list, LastList}]|Acc]) ->
     lists:reverse([[{grouper, LastGrouper}, {list, lists:reverse(LastList)}]|Acc]);
 regroup([Item|Rest], Attribute, []) ->
-    regroup(Rest, Attribute, [[{grouper, find_value(Attribute, Item)}, {list, [Item]}]]);
+    regroup(Rest, Attribute, [[{grouper, find_deep_value(Attribute, Item)}, {list, [Item]}]]);
 regroup([Item|Rest], Attribute, [[{grouper, PrevGrouper}, {list, PrevList}]|Acc]) ->
-    case find_value(Attribute, Item) of
+    case find_deep_value(Attribute, Item) of
         Value when Value =:= PrevGrouper ->
             regroup(Rest, Attribute, [[{grouper, PrevGrouper}, {list, [Item|PrevList]}]|Acc]);
         Value ->

+ 26 - 1
tests/src/erlydtl_unittests.erl

@@ -399,6 +399,13 @@ tests() ->
                    <<"{{ var1|default_if_none:\"foo\" }}">>, [], <<"foo">>},
                 {"|default_if_none:\"foo\" 2",
                     <<"{{ var1|default_if_none:\"foo\" }}">>, [{var1, "bar"}], <<"bar">>},
+		{"|dictsort 1",
+		 <<"{{ var1|dictsort:\"foo\" }}">>,
+		 [{var1,[[{foo,2}],[{foo,1}]]}], <<"{foo,1}{foo,2}">>},
+	        {"|dictsort 2",
+		 <<"{{ var1|dictsort:\"foo.bar\" }}">>,
+		 [{var1,[[{foo,[{bar,2}]}],[{foo,[{bar,1}]}]]}],
+		 <<"{foo,[{bar,1}]}{foo,[{bar,2}]}">>},
                 {"|divisibleby:\"3\"",
                     <<"{% if var1|divisibleby:\"3\" %}yay{% endif %}">>, [{var1, 21}], <<"yay">>},
                 {"|divisibleby:\"3\"",
@@ -946,7 +953,25 @@ tests() ->
                             [{first_name, "Condi"}, {gender, "Female"}],
                             [{first_name, "Bill"}, {gender, "Male"}]
                         ]}],
-                <<"Male\nGeorge\nFemale\nMargaret\nCondi\nMale\nBill\n">>}
+                <<"Male\nGeorge\nFemale\nMargaret\nCondi\nMale\nBill\n">>},
+	    {"NestedOrdered", <<"{% regroup people by name.last as lastname_list %}{% for lastname in lastname_list %}{{ lastname.grouper }}\n{% for item in lastname.list %}{{ item.name.first }}\n{% endfor %}{% endfor %}{% endregroup %}">>,
+                [{people, [[{name, [{first,"George"},{last,"Costanza"}]}],
+			   [{name, [{first,"Margaret"},{last,"Costanza"}]}],
+			   [{name, [{first,"Bill"},{last,"Buffalo"}]}],
+			   [{name, [{first,"Condi"},{last,"Buffalo"}]}]]}],
+               <<"Costanza\nGeorge\nMargaret\nBuffalo\nBill\nCondi\n">>},
+	    {"NestedUnordered", <<"{% regroup people by name.last as lastname_list %}{% for lastname in lastname_list %}{{ lastname.grouper }}\n{% for item in lastname.list %}{{ item.name.first }}\n{% endfor %}{% endfor %}{% endregroup %}">>,
+                [{people, [[{name, [{first,"George"},{last,"Costanza"}]}],
+			   [{name, [{first,"Bill"},{last,"Buffalo"}]}],
+			   [{name, [{first,"Margaret"},{last,"Costanza"}]}],
+			   [{name, [{first,"Condi"},{last,"Buffalo"}]}]]}],
+               <<"Costanza\nGeorge\nBuffalo\nBill\nCostanza\nMargaret\nBuffalo\nCondi\n">>},
+	    {"Filter", <<"{% regroup people|dictsort:\"name.last\" by name.last as lastname_list %}{% for lastname in lastname_list %}{{ lastname.grouper }}\n{% for item in lastname.list %}{{ item.name.first }}\n{% endfor %}{% endfor %}{% endregroup %}">>,
+		  [{people, [[{name, [{first,"George"},{last,"Costanza"}]}],
+			     [{name, [{first,"Bill"},{last,"Buffalo"}]}],
+			     [{name, [{first,"Margaret"},{last,"Costanza"}]}],
+			     [{name, [{first,"Condi"},{last,"Buffalo"}]}]]}],
+		  <<"Buffalo\nBill\nCondi\nCostanza\nGeorge\nMargaret\n">>}
         ]},
     {"spaceless", [
             {"Beginning", <<"{% spaceless %}    <b>foo</b>{% endspaceless %}">>, [], <<"<b>foo</b>">>},