Browse Source

New `load` tag (#100)

Need more tests before being ready to close this.
Andreas Stenius 11 years ago
parent
commit
cd72bf3f3b

+ 3 - 2
Makefile

@@ -19,8 +19,9 @@ update-deps:
 
 
 compile_test:
 compile_test:
 	-mkdir -p ebintest
 	-mkdir -p ebintest
-	$(ERLC) -o tests/src -I include/erlydtl_preparser.hrl tests/src/erlydtl_extension_testparser.yrl
-	$(ERL) -make
+	$(ERLC) -o tests/src -I include/erlydtl_preparser.hrl \
+		tests/src/erlydtl_extension_testparser.yrl
+	$(ERL) -pa ebin -make
 
 
 test: compile compile_test
 test: compile compile_test
 	$(ERL) -noshell -pa ebin -pa ebintest -pa deps/merl/ebin \
 	$(ERL) -noshell -pa ebin -pa ebintest -pa deps/merl/ebin \

+ 28 - 9
src/erlydtl_compiler_utils.erl

@@ -44,6 +44,8 @@
 %% Definitions
 %% Definitions
 %% --------------------------------------------------------------------
 %% --------------------------------------------------------------------
 
 
+-define(LIB_VERSION, 1).
+
 -export([
 -export([
          add_error/3, add_errors/2,
          add_error/3, add_errors/2,
          add_filters/2, add_tags/2,
          add_filters/2, add_tags/2,
@@ -254,29 +256,30 @@ load_library(Pos, Lib, Accept, #treewalker{ context=Context }=TreeWalker) ->
 load_library(Pos, Lib, Accept, Context) ->
 load_library(Pos, Lib, Accept, Context) ->
     case lib_module(Lib, Context) of
     case lib_module(Lib, Context) of
         {ok, Mod} ->
         {ok, Mod} ->
+            ?LOG_DEBUG(
+               "~s: Load library '~s' from ~s ~p~n",
+               [get_current_file(Context), Lib, Mod, Accept], Context),
             add_filters(
             add_filters(
-              [{Name, lib_function(Mod, Filter)}
-               || {Name, Filter} <- Mod:inventory(filters),
-                  Accept =:= [] orelse lists:member(Name, Accept)
-              ],
+              read_library(Mod, filters, Accept),
               add_tags(
               add_tags(
-                [{Name, lib_function(Mod, Tag)}
-                 || {Name, Tag} <- Mod:inventory(tags),
-                    Accept =:= [] orelse lists:member(Name, Accept)
-                ],
+                read_library(Mod, tags, Accept),
                 Context));
                 Context));
         Error ->
         Error ->
             ?WARN({Pos, Error}, Context)
             ?WARN({Pos, Error}, Context)
     end.
     end.
 
 
 add_filters(Load, #dtl_context{ filters=Filters }=Context) ->
 add_filters(Load, #dtl_context{ filters=Filters }=Context) ->
+    ?LOG_TRACE("Load filters: ~p~n", [Load], Context),
     Context#dtl_context{ filters=Load ++ Filters }.
     Context#dtl_context{ filters=Load ++ Filters }.
 
 
 add_tags(Load, #dtl_context{ tags=Tags }=Context) ->
 add_tags(Load, #dtl_context{ tags=Tags }=Context) ->
+    ?LOG_TRACE("Load tags: ~p~n", [Load], Context),
     Context#dtl_context{ tags=Load ++ Tags }.
     Context#dtl_context{ tags=Load ++ Tags }.
 
 
 format_error({load_library, Name, Mod, Reason}) ->
 format_error({load_library, Name, Mod, Reason}) ->
     io_lib:format("Failed to load library '~s' from '~s' (~s)", [Name, Mod, Reason]);
     io_lib:format("Failed to load library '~s' from '~s' (~s)", [Name, Mod, Reason]);
+format_error({library_version, Name, Mod, Version}) ->
+    io_lib:format("Unknown library version for '~s' from '~s': ~p", [Name, Mod, Version]);
 format_error({unknown_extension, Tag}) ->
 format_error({unknown_extension, Tag}) ->
     io_lib:format("Unhandled extension: ~p", [Tag]);
     io_lib:format("Unhandled extension: ~p", [Tag]);
 format_error(Other) ->
 format_error(Other) ->
@@ -423,15 +426,31 @@ lib_module(Name, #dtl_context{ libraries=Libs }) ->
                             lists:member(erlydtl_library, Behaviours);
                             lists:member(erlydtl_library, Behaviours);
                         _ -> false
                         _ -> false
                     end,
                     end,
-            if IsLib -> {ok, Mod};
+            if IsLib ->
+                    case Mod:version() of
+                        ?LIB_VERSION -> {ok, Mod};
+                        V -> {library_version, Name, Mod, V}
+                    end;
                true -> {load_library, Name, Mod, "not a library"}
                true -> {load_library, Name, Mod, "not a library"}
             end;
             end;
         {error, Reason} ->
         {error, Reason} ->
             {load_library, Name, Mod, Reason}
             {load_library, Name, Mod, Reason}
     end.
     end.
 
 
+read_library(Mod, Section, Accept) ->
+    [{Name, lib_function(Mod, Fun)}
+     || {Name, Fun} <- read_inventory(Mod, Section),
+        Accept =:= [] orelse lists:member(Name, Accept)
+    ].
+
 lib_function(_, {Mod, Fun}) ->
 lib_function(_, {Mod, Fun}) ->
     lib_function(Mod, Fun);
     lib_function(Mod, Fun);
 lib_function(Mod, Fun) ->
 lib_function(Mod, Fun) ->
     %% TODO: we can check for lib function availability here.. (sanity check)
     %% TODO: we can check for lib function availability here.. (sanity check)
     {Mod, Fun}.
     {Mod, Fun}.
+
+read_inventory(Mod, Section) ->
+    [case Item of
+         {_Name, _Fun} -> Item;
+         Fun -> {Fun, Fun}
+     end || Item <- Mod:inventory(Section)].

+ 3 - 2
src/erlydtl_library.erl

@@ -40,6 +40,7 @@
 -type exported_fun() :: Name::atom().
 -type exported_fun() :: Name::atom().
 -type external_fun() :: {Module::atom(), exported_fun()}.
 -type external_fun() :: {Module::atom(), exported_fun()}.
 -type library_function() :: exported_fun() | external_fun().
 -type library_function() :: exported_fun() | external_fun().
--type inventory() :: [{Name::atom(), library_function()}].
+-type inventory_item() :: exported_fun() | {Name::atom(), library_function()}.
 
 
--callback inventory(filters|tags) -> inventory().
+-callback inventory(filters|tags) -> list(inventory_item()).
+-callback version() -> 1.

+ 14 - 4
src/erlydtl_parser.yrl

@@ -105,7 +105,6 @@ Nonterminals
     Args
     Args
 
 
     RegroupTag
     RegroupTag
-    EndRegroupTag
 
 
     SpacelessBlock
     SpacelessBlock
 
 
@@ -127,7 +126,10 @@ Nonterminals
 
 
     CallTag
     CallTag
     CallWithTag
     CallWithTag
-    
+
+    LoadTag
+    LoadArgs
+
     Unot.
     Unot.
 
 
 Terminals
 Terminals
@@ -162,6 +164,7 @@ Terminals
     filter_keyword
     filter_keyword
     firstof_keyword
     firstof_keyword
     for_keyword
     for_keyword
+    from_keyword
     identifier
     identifier
     if_keyword
     if_keyword
     ifchanged_keyword
     ifchanged_keyword
@@ -169,6 +172,7 @@ Terminals
     ifnotequal_keyword
     ifnotequal_keyword
     in_keyword
     in_keyword
     include_keyword
     include_keyword
+    load_keyword
     noop_keyword
     noop_keyword
     not_keyword
     not_keyword
     now_keyword
     now_keyword
@@ -231,9 +235,9 @@ Elements -> Elements IfEqualBlock : '$1' ++ ['$2'].
 Elements -> Elements IfNotEqualBlock : '$1' ++ ['$2'].
 Elements -> Elements IfNotEqualBlock : '$1' ++ ['$2'].
 Elements -> Elements IfChangedBlock : '$1' ++ ['$2'].
 Elements -> Elements IfChangedBlock : '$1' ++ ['$2'].
 Elements -> Elements IncludeTag : '$1' ++ ['$2'].
 Elements -> Elements IncludeTag : '$1' ++ ['$2'].
+Elements -> Elements LoadTag : '$1' ++ ['$2'].
 Elements -> Elements NowTag : '$1' ++ ['$2'].
 Elements -> Elements NowTag : '$1' ++ ['$2'].
 Elements -> Elements RegroupTag : '$1' ++ ['$2'].
 Elements -> Elements RegroupTag : '$1' ++ ['$2'].
-Elements -> Elements EndRegroupTag : '$1' ++ ['$2'].
 Elements -> Elements SpacelessBlock : '$1' ++ ['$2'].
 Elements -> Elements SpacelessBlock : '$1' ++ ['$2'].
 Elements -> Elements SSITag : '$1' ++ ['$2'].
 Elements -> Elements SSITag : '$1' ++ ['$2'].
 Elements -> Elements TemplatetagTag : '$1' ++ ['$2'].
 Elements -> Elements TemplatetagTag : '$1' ++ ['$2'].
@@ -280,6 +284,12 @@ IncludeTag -> open_tag include_keyword string_literal with_keyword Args close_ta
 IncludeTag -> open_tag include_keyword string_literal only_keyword close_tag : {include_only, '$3', []}.
 IncludeTag -> open_tag include_keyword string_literal only_keyword close_tag : {include_only, '$3', []}.
 IncludeTag -> open_tag include_keyword string_literal with_keyword Args only_keyword close_tag : {include_only, '$3', '$5'}.
 IncludeTag -> open_tag include_keyword string_literal with_keyword Args only_keyword close_tag : {include_only, '$3', '$5'}.
 
 
+LoadTag -> open_tag load_keyword LoadArgs close_tag : {load_libs, '$3'}.
+LoadTag -> open_tag load_keyword LoadArgs from_keyword identifier close_tag : {load_from_lib, '$3', '$5'}.
+
+LoadArgs -> '$empty' : [].
+LoadArgs -> identifier LoadArgs : ['$1'|'$2'].
+
 NowTag -> open_tag now_keyword string_literal close_tag : {date, now, '$3'}.
 NowTag -> open_tag now_keyword string_literal close_tag : {date, now, '$3'}.
 
 
 CommentBlock -> CommentBraced Elements EndCommentBraced : {comment, '$2'}.
 CommentBlock -> CommentBraced Elements EndCommentBraced : {comment, '$2'}.
@@ -361,7 +371,7 @@ IfNotEqualExpression -> Value : '$1'.
 EndIfNotEqualBraced -> open_tag endifnotequal_keyword close_tag.
 EndIfNotEqualBraced -> open_tag endifnotequal_keyword close_tag.
 
 
 RegroupTag -> open_tag regroup_keyword Value by_keyword Value as_keyword identifier close_tag : {regroup, {'$3', '$5', '$7'}}.
 RegroupTag -> open_tag regroup_keyword Value by_keyword Value as_keyword identifier close_tag : {regroup, {'$3', '$5', '$7'}}.
-EndRegroupTag -> open_tag endregroup_keyword close_tag : end_regroup.
+RegroupTag -> open_tag endregroup_keyword close_tag : end_regroup.
 
 
 SpacelessBlock -> open_tag spaceless_keyword close_tag Elements open_tag endspaceless_keyword close_tag : {spaceless, '$4'}.
 SpacelessBlock -> open_tag spaceless_keyword close_tag Elements open_tag endspaceless_keyword close_tag : {spaceless, '$4'}.
 
 

+ 3 - 1
src/erlydtl_scanner.erl

@@ -36,7 +36,7 @@
 %%%-------------------------------------------------------------------
 %%%-------------------------------------------------------------------
 -module(erlydtl_scanner).
 -module(erlydtl_scanner).
 
 
-%% This file was generated 2014-02-21 13:38:27 UTC by slex 0.2.1.
+%% This file was generated 2014-02-27 13:29:50 UTC by slex 0.2.1.
 %% http://github.com/erlydtl/slex
 %% http://github.com/erlydtl/slex
 -slex_source(["src/erlydtl_scanner.slex"]).
 -slex_source(["src/erlydtl_scanner.slex"]).
 
 
@@ -86,6 +86,7 @@ is_keyword(any, "and") -> true;
 is_keyword(any, "as") -> true;
 is_keyword(any, "as") -> true;
 is_keyword(any, "by") -> true;
 is_keyword(any, "by") -> true;
 is_keyword(any, "with") -> true;
 is_keyword(any, "with") -> true;
+is_keyword(any, "from") -> true;
 is_keyword(close, "only") -> true;
 is_keyword(close, "only") -> true;
 is_keyword(close, "parsed") -> true;
 is_keyword(close, "parsed") -> true;
 is_keyword(close, "noop") -> true;
 is_keyword(close, "noop") -> true;
@@ -136,6 +137,7 @@ is_keyword(open, "endwith") -> true;
 is_keyword(open, "trans") -> true;
 is_keyword(open, "trans") -> true;
 is_keyword(open, "blocktrans") -> true;
 is_keyword(open, "blocktrans") -> true;
 is_keyword(open, "endblocktrans") -> true;
 is_keyword(open, "endblocktrans") -> true;
+is_keyword(open, "load") -> true;
 is_keyword(_, _) -> false.
 is_keyword(_, _) -> false.
 
 
 format_error({illegal_char, C}) ->
 format_error({illegal_char, C}) ->

+ 2 - 0
src/erlydtl_scanner.slex

@@ -307,6 +307,7 @@ form \
   is_keyword(any, "as") -> true; \
   is_keyword(any, "as") -> true; \
   is_keyword(any, "by") -> true; \
   is_keyword(any, "by") -> true; \
   is_keyword(any, "with") -> true; \
   is_keyword(any, "with") -> true; \
+  is_keyword(any, "from") -> true; \
   \
   \
   is_keyword(close, "only") -> true; \
   is_keyword(close, "only") -> true; \
   is_keyword(close, "parsed") -> true; \
   is_keyword(close, "parsed") -> true; \
@@ -359,6 +360,7 @@ form \
   is_keyword(open, "trans") -> true; \
   is_keyword(open, "trans") -> true; \
   is_keyword(open, "blocktrans") -> true; \
   is_keyword(open, "blocktrans") -> true; \
   is_keyword(open, "endblocktrans") -> true; \
   is_keyword(open, "endblocktrans") -> true; \
+  is_keyword(open, "load") -> true; \
   is_keyword(_, _) -> false \
   is_keyword(_, _) -> false \
 end.
 end.
 
 

+ 14 - 0
tests/src/erlydtl_lib_test1.erl

@@ -0,0 +1,14 @@
+-module(erlydtl_lib_test1).
+-behaviour(erlydtl_library).
+
+-export([version/0, inventory/1, reverse/1]).
+
+version() -> 1.
+
+inventory(filters) -> [reverse];
+inventory(tags) -> [].
+
+reverse(String) when is_list(String) ->
+    lists:reverse(String);
+reverse(String) when is_binary(String) ->
+    reverse(binary_to_list(String)).

+ 6 - 0
tests/src/erlydtl_unittests.erl

@@ -1352,6 +1352,12 @@ tests() ->
          []
          []
         }
         }
        }
        }
+      ]},
+     {"load",
+      [{"filter",
+        <<"{% load test1 %}{{ \"w00t\"|reverse }}">>, [], [],
+        [{libraries, [{test1, erlydtl_lib_test1}]}],
+        <<"t00w">>}
       ]}
       ]}
     ].
     ].