Browse Source

New compile-time option: custom_filters_modules

Evan Miller 13 years ago
parent
commit
b076a6e245
2 changed files with 47 additions and 13 deletions
  1. 12 0
      README.markdown
  2. 35 13
      src/erlydtl_compiler.erl

+ 12 - 0
README.markdown

@@ -60,6 +60,18 @@ e.g.:
 
 
 The `Context` is specified at render-time with the `custom_tags_context` option.
 The `Context` is specified at render-time with the `custom_tags_context` option.
 
 
+* `custom_filters_modules` - A list of modules to be used for handling custom
+filters. The modules will be searched in order and take precedence over the
+built-in filters. Each custom filter should correspond to an exported filter,
+e.g.
+
+    some_filter(Value) -> iolist()
+
+If the filter takes an argument (e.g. "foo:2"), the argument will be also be
+passed in:
+
+    some_filter(Value, Arg) -> iolist()
+
 * `vars` - Variables (and their values) to evaluate at compile-time rather than
 * `vars` - Variables (and their values) to evaluate at compile-time rather than
 render-time. 
 render-time. 
 
 

+ 35 - 13
src/erlydtl_compiler.erl

@@ -49,6 +49,7 @@
     doc_root = "", 
     doc_root = "", 
     parse_trail = [],
     parse_trail = [],
     vars = [],
     vars = [],
+    filter_modules = [],
     custom_tags_dir = [],
     custom_tags_dir = [],
     custom_tags_modules = [],
     custom_tags_modules = [],
     reader = {file, read_file},
     reader = {file, read_file},
@@ -221,6 +222,7 @@ init_dtl_context(File, Module, Options) ->
         parse_trail = [File], 
         parse_trail = [File], 
         module = Module,
         module = Module,
         doc_root = proplists:get_value(doc_root, Options, filename:dirname(File)),
         doc_root = proplists:get_value(doc_root, Options, filename:dirname(File)),
+        filter_modules = proplists:get_value(custom_filters_modules, Options, Ctx#dtl_context.filter_modules) ++ [erlydtl_filters],
         custom_tags_dir = proplists:get_value(custom_tags_dir, Options, filename:join([erlydtl_deps:get_base_dir(), "priv", "custom_tags"])),
         custom_tags_dir = proplists:get_value(custom_tags_dir, Options, filename:join([erlydtl_deps:get_base_dir(), "priv", "custom_tags"])),
         custom_tags_modules = proplists:get_value(custom_tags_modules, Options, Ctx#dtl_context.custom_tags_modules),
         custom_tags_modules = proplists:get_value(custom_tags_modules, Options, Ctx#dtl_context.custom_tags_modules),
         blocktrans_fun = proplists:get_value(blocktrans_fun, Options, Ctx#dtl_context.blocktrans_fun),
         blocktrans_fun = proplists:get_value(blocktrans_fun, Options, Ctx#dtl_context.blocktrans_fun),
@@ -241,6 +243,7 @@ init_dtl_context_dir(Dir, Module, Options) ->
         parse_trail = [], 
         parse_trail = [], 
         module = Module,
         module = Module,
         doc_root = proplists:get_value(doc_root, Options, Dir),
         doc_root = proplists:get_value(doc_root, Options, Dir),
+        filter_modules = proplists:get_value(custom_filters_modules, Options, Ctx#dtl_context.filter_modules) ++ [erlydtl_filters],
         custom_tags_dir = proplists:get_value(custom_tags_dir, Options, filename:join([erlydtl_deps:get_base_dir(), "priv", "custom_tags"])),
         custom_tags_dir = proplists:get_value(custom_tags_dir, Options, filename:join([erlydtl_deps:get_base_dir(), "priv", "custom_tags"])),
         custom_tags_modules = proplists:get_value(custom_tags_modules, Options, Ctx#dtl_context.custom_tags_modules),
         custom_tags_modules = proplists:get_value(custom_tags_modules, Options, Ctx#dtl_context.custom_tags_modules),
         blocktrans_fun = proplists:get_value(blocktrans_fun, Options, Ctx#dtl_context.blocktrans_fun),
         blocktrans_fun = proplists:get_value(blocktrans_fun, Options, Ctx#dtl_context.blocktrans_fun),
@@ -893,21 +896,34 @@ filter_ast_noescape(Variable, Filter, Context, TreeWalker) ->
     {VarValue, Info2} = filter_ast1(Filter, VariableAst, Context),
     {VarValue, Info2} = filter_ast1(Filter, VariableAst, Context),
     {{VarValue, merge_info(Info1, Info2)}, TreeWalker2}.
     {{VarValue, merge_info(Info1, Info2)}, TreeWalker2}.
 
 
-filter_ast1([{identifier, _, Name}, {string_literal, _, ArgName}], VariableAst, #dtl_context{ binary_strings = true }) ->
-    filter_ast2(Name, VariableAst, [binary_string(unescape_string_literal(ArgName))], []);
-filter_ast1([{identifier, _, Name}, {string_literal, _, ArgName}], VariableAst, #dtl_context{ binary_strings = false }) ->
-    filter_ast2(Name, VariableAst, [erl_syntax:string(unescape_string_literal(ArgName))], []);
-filter_ast1([{identifier, _, Name}, {number_literal, _, ArgName}], VariableAst, _Context) ->
-    filter_ast2(Name, VariableAst, [erl_syntax:integer(list_to_integer(ArgName))], []);
+filter_ast1([{identifier, _, Name}, {string_literal, _, ArgName}], VariableAst, #dtl_context{ binary_strings = true } = Context) ->
+    filter_ast2(Name, VariableAst, [binary_string(unescape_string_literal(ArgName))], [], Context);
+filter_ast1([{identifier, _, Name}, {string_literal, _, ArgName}], VariableAst, #dtl_context{ binary_strings = false } = Context) ->
+    filter_ast2(Name, VariableAst, [erl_syntax:string(unescape_string_literal(ArgName))], [], Context);
+filter_ast1([{identifier, _, Name}, {number_literal, _, ArgName}], VariableAst, Context) ->
+    filter_ast2(Name, VariableAst, [erl_syntax:integer(list_to_integer(ArgName))], [], Context);
 filter_ast1([{identifier, _, Name}, ArgVariable], VariableAst, Context) ->
 filter_ast1([{identifier, _, Name}, ArgVariable], VariableAst, Context) ->
     {ArgAst, ArgVarName} = resolve_variable_ast(ArgVariable, Context),
     {ArgAst, ArgVarName} = resolve_variable_ast(ArgVariable, Context),
-    filter_ast2(Name, VariableAst, [ArgAst], [ArgVarName]);
-filter_ast1([{identifier, _, Name}], VariableAst, _Context) ->
-    filter_ast2(Name, VariableAst, [], []).
+    filter_ast2(Name, VariableAst, [ArgAst], [ArgVarName], Context);
+filter_ast1([{identifier, _, Name}], VariableAst, Context) ->
+    filter_ast2(Name, VariableAst, [], [], Context).
 
 
-filter_ast2(Name, VariableAst, AdditionalArgs, VarNames) ->
-    {erl_syntax:application(erl_syntax:atom(erlydtl_filters), erl_syntax:atom(Name), 
-            [VariableAst | AdditionalArgs]), #ast_info{var_names = VarNames}}.
+filter_ast2(Name, VariableAst, [], VarNames, #dtl_context{ filter_modules = [Module|Rest] } = Context) ->
+    case lists:member({Name, 1}, Module:module_info(exports)) of
+        true ->
+            {erl_syntax:application(erl_syntax:atom(Module), erl_syntax:atom(Name), 
+                    [VariableAst]), #ast_info{var_names = VarNames}};
+        false ->
+            filter_ast2(Name, VariableAst, [], VarNames, Context#dtl_context{ filter_modules = Rest })
+    end;
+filter_ast2(Name, VariableAst, [Arg], VarNames, #dtl_context{ filter_modules = [Module|Rest] } = Context) ->
+    case lists:member({Name, 2}, Module:module_info(exports)) of
+        true ->
+            {erl_syntax:application(erl_syntax:atom(Module), erl_syntax:atom(Name),
+                    [VariableAst, Arg]), #ast_info{var_names = VarNames}};
+        false ->
+            filter_ast2(Name, VariableAst, [Arg], VarNames, Context#dtl_context{ filter_modules = Rest })
+    end.
  
  
 search_for_escape_filter(Variable, Filter, #dtl_context{auto_escape = on}) ->
 search_for_escape_filter(Variable, Filter, #dtl_context{auto_escape = on}) ->
     search_for_safe_filter(Variable, Filter);
     search_for_safe_filter(Variable, Filter);
@@ -1184,7 +1200,13 @@ custom_tags_modules_ast(Name, InterpretedArgs, #dtl_context{ custom_tags_modules
             {erl_syntax:application(erl_syntax:atom(Module), erl_syntax:atom(Name),
             {erl_syntax:application(erl_syntax:atom(Module), erl_syntax:atom(Name),
                     [erl_syntax:list(InterpretedArgs), erl_syntax:variable("CustomTagsContext")]), #ast_info{}};
                     [erl_syntax:list(InterpretedArgs), erl_syntax:variable("CustomTagsContext")]), #ast_info{}};
         false ->
         false ->
-            custom_tags_modules_ast(Name, InterpretedArgs, Context#dtl_context{ custom_tags_modules = Rest })
+            case lists:member({Name, 1}, Module:module_info(exports)) of
+                true ->
+                    {erl_syntax:application(erl_syntax:atom(Module), erl_syntax:atom(Name),
+                            [erl_syntax:list(InterpretedArgs)]), #ast_info{}};
+                false ->
+                    custom_tags_modules_ast(Name, InterpretedArgs, Context#dtl_context{ custom_tags_modules = Rest })
+            end
     end.
     end.
 
 
 options_ast() ->
 options_ast() ->