Browse Source

server bugfixes

git-svn-id: http://erlydtl.googlecode.com/svn/trunk@13 a5195066-8e3e-0410-a82a-05b01b1b9875
rsaccon 17 years ago
parent
commit
23cd64e026
5 changed files with 336 additions and 160 deletions
  1. 4 2
      Makefile
  2. 1 1
      src/demo/erlydtl_demo.erl
  3. 20 0
      src/erlydtl/erlydtl.app.src
  4. 11 157
      src/erlydtl/erlydtl.erl
  5. 300 0
      src/erlydtl/erlydtl_server.erl

+ 4 - 2
Makefile

@@ -1,14 +1,16 @@
 ERL=/Users/rsaccon/R11B/start.sh
 #ERL=/usr/local/erlware/bin/erl
 #ERL=erl
+APP_NAME=erlydtl
 
 
 all:
-	$(ERL) -make
+	( $(ERL) -make && \
+	if [ ! -e ebin/$(APP_NAME).app ]; then cp -f src/$(APP_NAME)/$(APP_NAME).app.src ebin/$(APP_NAME).app; fi )	
 
 run:
 	$(ERL) -pa `pwd`/ebin
 	
 clean:
-	rm -fv ebin/*
+	rm -fv ebin/*.beam
 	rm -fv erl_crash.dump

+ 1 - 1
src/demo/erlydtl_demo.erl

@@ -65,7 +65,7 @@ compile_test_template(Name) ->
     DocRoot = filename:join([filename:dirname(code:which(?MODULE)),"..", "demo", "templates"]),
     Name2 = "test_" ++ Name,
     Path = filename:join([DocRoot, Name2 ++ ".html"]),
-    erlydtl:compile(Path, Name2, DocRoot).
+    erlydtl_server:compile(Path, Name2, DocRoot).
 
                        
 %%--------------------------------------------------------------------

+ 20 - 0
src/erlydtl/erlydtl.app.src

@@ -0,0 +1,20 @@
+{application, erlydtl,
+ [{description, "ErlyDTL Server"},
+  {vsn, "0.1"},
+  {modules, [
+    erlydtl,
+    erlydtl_server,
+    erlydtl_app,
+    erlydtl_sup,
+    erlydtl_parser,
+    erlydtl_scanner,
+    erlydtl_tools
+  ]},
+  {registered, []},
+  {applications, [
+    kernel,
+    stdlib
+  ]},
+  {included_applications, []},
+  {env, []},
+  {mod, {erlydtl_app, []}}]}.

+ 11 - 157
src/erlydtl/erlydtl.erl

@@ -1,9 +1,9 @@
 %%%-------------------------------------------------------------------
-%%% File:      erlydtl_api.erl
+%%% File:      erlydtl.erl
 %%% @author    Roberto Saccon <rsaccon@gmail.com> [http://rsaccon.com]
 %%% @copyright 2007 Roberto Saccon
 %%% @doc  
-%%% API for compiling ErlyDTL templeates
+%%% Helper module to start and stop ErlyDTL application
 %%% @end  
 %%%
 %%% The MIT License
@@ -34,161 +34,15 @@
 -author('rsaccon@gmail.com').
 
 %% API
--export([compile/3, compile/4]).
+-export([start/0, stop/0]).
 
-%%--------------------------------------------------------------------
-%% @spec (File:string(), ModuleName:string(), DocRoot:string()) -> 
-%%     {Ok::atom, Ast::tuple() | {Error::atom(), Msg:string()}
-%% @doc compiles a template to a beam file
-%% @end 
-%%--------------------------------------------------------------------
-compile(File, ModuleName, DocRoot) ->
-    compile(File, ModuleName, DocRoot, "render").
-    
 
-%%--------------------------------------------------------------------
-%% @spec (File:string(), ModuleName:string(), DocRoot:string(), FunctionName:atom()) -> 
-%%     {Ok::atom, Ast::tuple() | {Error::atom(), Msg:string()}
-%% @doc compiles a template to a beam file
-%% @end 
-%%--------------------------------------------------------------------
-compile(File, ModuleName, DocRoot, FunctionName) ->   
-    case parse(File) of
-        {ok, Ast} ->
-			RelDir = rel_dir(filename:dirname(File), DocRoot),
-            compile_reload_ast(Ast, ModuleName, FunctionName, RelDir);
-        {error, Msg} = Err ->
-            io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, File ++ " Parser failure:"]),
-            io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, Msg]),
-            Err
-	end.
+%% @spec start() -> ok
+%% @doc Start the erlydtl server.
+start() ->
+    application:start(erlydtl).
 
-
-%%====================================================================
-%% Internal functions
-%%====================================================================
-
-rel_dir(Dir, DocRoot) when Dir =:= DocRoot ->
-    DocRoot;
-rel_dir(Dir, DocRoot) ->
-    RelFile = string:substr(Dir, length(DocRoot)+2),
-    filename:join([DocRoot, RelFile]).
-
-
-parse(File) ->
-	case file:read_file(File) of
-		{ok, B} ->
-	        case erlydtl_scanner:scan(binary_to_list(B)) of
-	            {ok, Tokens} ->
-	                erlydtl_parser:parse(Tokens);
-	            Err ->
-	                io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, File ++ " Scanner failure:"]),
-	                io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, Err]),
-	                Err
-	        end;
-	    Err ->
-	        io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, "File read error"]),
-	        Err   
-	end.
-	
-
-compile_reload_ast([H | T], ModuleName, FunctionName, RelDir) ->
-    {List, Args} = case transl(H, T, [], [], RelDir) of
-	    {regular, List0, Args0} ->
-		    {[inplace_block(X) ||  X <- List0], Args0};
-		{inherited, List0, Arg0} ->
-			{List0, Arg0}
-	end,	           
-    Args2 = lists:reverse([{var, 1, Val} || {Val, _} <- Args]),  
-    Cons = list_fold(lists:reverse(List)),                           
-    Ast2 = {function, 1, list_to_atom(FunctionName), length(Args2),
-        [{clause, 1, Args2, [], [Cons]}]},
-    Ac = erlydtl_tools:create_module(Ast2 , ModuleName),   
-    case compile:forms(Ac) of
-        {ok, Module, Bin} ->
-            case erlydtl_tools:reload(Module, Bin) of
-                ok ->
-                    erlydtl_tools:write_beam(Module, Bin, "ebin");
-                _ -> 
-                    {error, "reload failed"}
-            end;            
-        _ ->
-           {error, "compilation failed"}
-    end.
-
-
-list_fold([E]) ->
-    E;      
-list_fold([E1, E2]) ->
-    {cons, 1, E2, E1};           
-list_fold([E1, E2 | Tail]) ->
-    lists:foldl(fun(X, T) -> 
-        {cons, 1, X, T}
-    end, {cons, 1, E2, E1}, Tail).                       
-
-
-transl(nil, [{extends, _Line, Name}], Out, Args, RelDir) -> 
-    case parse(filename:join([RelDir, Name])) of
-        {ok, ParentAst} ->
-		    [H|T]=ParentAst,
-			{_, List, Args1} = transl(H, T, [], [], RelDir),			 
-			{List3, Args3} = lists:foldl(fun(X, {List2, Args2}) -> 
-                {List4, Args4} = replace_block(X, Out, Args2),            
-                {[List4 | List2], Args4}
-            end, {[], Args1}, List),		   
-		    {inherited, lists:reverse(lists:flatten([List3])), lists:flatten(Args3)};
-	    {error, Msg} ->
-	         io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, Msg]),
-	         io:format("TRACE ~p:~p Parent Parser failure: ~p~n",[?MODULE, ?LINE, Name]),
-		     {regular, Out, Args}			
-    end;
-	
-transl(nil, [{var, Line, Val}], Out, Args, _) ->
-    case lists:keysearch(Val, 2, Args) of
-        false ->
-            Key = list_to_atom(lists:concat(["A", length(Args) + 1])),
-            {regular, [{var, Line, Key} | Out], [{Key, Val} | Args]}; 
-        {value, {Key, _}} ->   
-            {regular, [{var, Line, Key} | Out], Args}
-    end;
-    
-transl(nil, [{tag, Line, _TagArgs}], Out, Args, _) ->
-    %% TODO: call insert_ tag code
-    {regular, [{string, Line, "not_fully_implemented_yet"} | Out], Args};
-
-transl(nil, [Token], Out, Args, _) ->
-    {regular, [Token | Out], Args}; 
-	
-transl([H | T], [{var, Line, Val}], Out, Args, DocRoot) ->
-    case lists:keysearch(Val, 2, Args) of
-        false ->           
-            Key = list_to_atom(lists:concat(["A", length(Args) + 1])),
-            transl(H, T, [{var, Line, Key} | Out], [{Key, Val} | Args], DocRoot);
-        {value, {Key, _}} ->  
-            transl(H, T, [{var, Line, Key} | Out], Args, DocRoot)
-	end;	 
-	
-transl([H | T], [{tag, Line, _TagArgs}], Out, Args, DocRoot) ->
-    %% TODO: call insert_tag code
-    transl(H, T, [{string, Line, "not_fully_implemented_yet"} | Out], Args, DocRoot);
-	
-transl([H | T], [Token], Out, Args, DocRoot) ->      
-    transl(H, T, [Token | Out], Args, DocRoot).
-
-
-replace_block({block, _Line, Name, [nil, Val]}, List, Args) ->
-	case lists:keysearch(Name, 3, List) of
-		false -> 
-			{Val, Args};
-		{value, {_, _, _, [H | T]}} ->  
-		    {_, List2, Args2} = transl(H, T, [], Args, undefined),
-		    {lists:reverse(List2), Args2} 
- 	end;
-replace_block(Other, What, Args) ->	
-	{Other, Args}.
-    
-	
-inplace_block({block, _Line , _Name, [nil, Str]}) ->
-	Str;
-inplace_block(Other) ->	
-	Other.
+%% @spec stop() -> ok
+%% @doc Stop the erlydtl server.
+stop() ->
+    application:stop(erlydtl).

+ 300 - 0
src/erlydtl/erlydtl_server.erl

@@ -0,0 +1,300 @@
+%%%-------------------------------------------------------------------
+%%% File:      erlydtl_server.erl
+%%% @author    Roberto Saccon <rsaccon@gmail.com> [http://rsaccon.com]
+%%% @copyright 2007 Roberto Saccon
+%%% @doc  
+%%% Server for compiling ErlyDTL templeates
+%%% @end  
+%%%
+%%% The MIT License
+%%%
+%%% Copyright (c) 2007 Roberto Saccon
+%%%
+%%% Permission is hereby granted, free of charge, to any person obtaining a copy
+%%% of this software and associated documentation files (the "Software"), to deal
+%%% in the Software without restriction, including without limitation the rights
+%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+%%% copies of the Software, and to permit persons to whom the Software is
+%%% furnished to do so, subject to the following conditions:
+%%%
+%%% The above copyright notice and this permission notice shall be included in
+%%% all copies or substantial portions of the Software.
+%%%
+%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+%%% THE SOFTWARE.
+%%%
+%%% @since 2007-11-17 by Roberto Saccon
+%%%-------------------------------------------------------------------
+-module(erlydtl_server).
+-author('rsaccon@gmail.com').
+
+-behaviour(gen_server).
+	
+%% API
+-export([start_link/0, compile/1, compile/3, compile/4]).
+
+%% gen_server callbacks
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
+         terminate/2, code_change/3]).
+
+-record(state, {}).
+
+
+%%====================================================================
+%% API
+%%====================================================================
+%%--------------------------------------------------------------------
+%% @spec start_link() -> {ok,Pid} | ignore | {error,Error}
+%% @doc Starts the server
+%% @end 
+%%--------------------------------------------------------------------
+start_link() ->
+    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+    
+
+%%--------------------------------------------------------------------
+%% @spec (File:string()) -> 
+%%     {Ok::atom, Ast::tuple() | {Error::atom(), Msg:string()}
+%% @doc compiles a template to a beam file
+%% @end 
+%%--------------------------------------------------------------------
+compile(File) ->
+    compile(File, todo, todo).
+        
+%%--------------------------------------------------------------------
+%% @spec (File:string(), ModuleName:string(), DocRoot:string()) -> 
+%%     {Ok::atom, Ast::tuple() | {Error::atom(), Msg:string()}
+%% @doc compiles a template to a beam file
+%% @end 
+%%--------------------------------------------------------------------
+compile(File, ModuleName, DocRoot) ->
+    compile(File, ModuleName, DocRoot, "render").
+    
+
+%%--------------------------------------------------------------------
+%% @spec (File:string(), ModuleName:string(), DocRoot:string(), FunctionName:atom()) -> 
+%%     {Ok::atom, Ast::tuple() | {Error::atom(), Msg:string()}
+%% @doc compiles a template to a beam file
+%% @end 
+%%--------------------------------------------------------------------
+compile(File, ModuleName, DocRoot, FunctionName) ->   
+    gen_server:call(?MODULE, {compile, File, ModuleName, DocRoot, FunctionName}).
+        
+
+	
+
+%%====================================================================
+%% gen_server callbacks
+%%====================================================================
+
+%%--------------------------------------------------------------------
+%% @spec init(Args) -> {ok, State} |
+%%                         {ok, State, Timeout} |
+%%                         ignore               |
+%%                         {stop, Reason}
+%% @doc Initiates the server
+%% @end 
+%%--------------------------------------------------------------------
+init([]) ->
+    {ok, #state{}}.
+
+%%--------------------------------------------------------------------
+%% @spec 
+%% handle_call(Request, From, State) -> {reply, Reply, State} |
+%%                                      {reply, Reply, State, Timeout} |
+%%                                      {noreply, State} |
+%%                                      {noreply, State, Timeout} |
+%%                                      {stop, Reason, Reply, State} |
+%%                                      {stop, Reason, State}
+%% @doc Handling call messages
+%% @end 
+%%--------------------------------------------------------------------
+handle_call({compile, File, ModuleName, DocRoot, FunctionName}, _From, State) ->
+    Reply = case parse(File) of
+        {ok, Ast} ->
+		    RelDir = rel_dir(filename:dirname(File), DocRoot),
+            compile_reload_ast(Ast, ModuleName, FunctionName, RelDir);
+        {error, Msg} = Err ->
+            io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, File ++ " Parser failure:"]),
+            io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, Msg]),
+            Err
+    end,
+    {reply, Reply, State};
+
+handle_call(_Request, _From, State) ->
+    Reply = ok,
+    {reply, Reply, State}.
+
+%%--------------------------------------------------------------------
+%% @spec handle_cast(Msg, State) -> {noreply, State} |
+%%                                      {noreply, State, Timeout} |
+%%                                      {stop, Reason, State}
+%% @doc Handling cast messages
+%% @end 
+%%--------------------------------------------------------------------
+handle_cast(_Msg, State) ->
+    {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @spec handle_info(Info, State) -> {noreply, State} |
+%%                                       {noreply, State, Timeout} |
+%%                                       {stop, Reason, State}
+%% @doc Handling all non call/cast messages
+%% @end 
+%%--------------------------------------------------------------------
+handle_info(_Info, State) ->
+    {noreply, State}.
+
+%%--------------------------------------------------------------------
+%% @spec terminate(Reason, State) -> void()
+%% @doc This function is called by a gen_server when it is about to
+%% terminate. It should be the opposite of Module:init/1 and do any necessary
+%% cleaning up. When it returns, the gen_server terminates with Reason.
+%% The return value is ignored.
+%% @end 
+%%--------------------------------------------------------------------
+terminate(_Reason, _State) ->
+    ok.
+
+%%--------------------------------------------------------------------
+%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
+%% @doc Convert process state when code is changed
+%% @end 
+%%--------------------------------------------------------------------
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.	
+
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
+
+rel_dir(Dir, DocRoot) when Dir =:= DocRoot ->
+    DocRoot;
+rel_dir(Dir, DocRoot) ->
+    RelFile = string:substr(Dir, length(DocRoot)+2),
+    filename:join([DocRoot, RelFile]).
+
+
+parse(File) ->
+	case file:read_file(File) of
+		{ok, B} ->
+	        case erlydtl_scanner:scan(binary_to_list(B)) of
+	            {ok, Tokens} ->
+	                erlydtl_parser:parse(Tokens);
+	            Err ->
+	                io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, File ++ " Scanner failure:"]),
+	                io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, Err]),
+	                Err
+	        end;
+	    Err ->
+	        io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, "File read error"]),
+	        Err   
+	end.
+	
+
+compile_reload_ast([H | T], ModuleName, FunctionName, RelDir) ->
+    {List, Args} = case transl(H, T, [], [], RelDir) of
+	    {regular, List0, Args0} ->
+		    {[inplace_block(X) ||  X <- List0], Args0};
+		{inherited, List0, Arg0} ->
+			{List0, Arg0}
+	end,	           
+    Args2 = lists:reverse([{var, 1, Val} || {Val, _} <- Args]),  
+    Cons = list_fold(lists:reverse(List)),                           
+    Ast2 = {function, 1, list_to_atom(FunctionName), length(Args2),
+        [{clause, 1, Args2, [], [Cons]}]},
+    Ac = erlydtl_tools:create_module(Ast2 , ModuleName),   
+    case compile:forms(Ac) of
+        {ok, Module, Bin} ->
+            case erlydtl_tools:reload(Module, Bin) of
+                ok ->
+                    erlydtl_tools:write_beam(Module, Bin, "ebin");
+                _ -> 
+                    {error, "reload failed"}
+            end;            
+        _ ->
+           {error, "compilation failed"}
+    end.
+
+
+list_fold([E]) ->
+    E;      
+list_fold([E1, E2]) ->
+    {cons, 1, E2, E1};           
+list_fold([E1, E2 | Tail]) ->
+    lists:foldl(fun(X, T) -> 
+        {cons, 1, X, T}
+    end, {cons, 1, E2, E1}, Tail).                       
+
+
+transl(nil, [{extends, _Line, Name}], Out, Args, RelDir) -> 
+    case parse(filename:join([RelDir, Name])) of
+        {ok, ParentAst} ->
+		    [H|T]=ParentAst,
+			{_, List, Args1} = transl(H, T, [], [], RelDir),			 
+			{List3, Args3} = lists:foldl(fun(X, {List2, Args2}) -> 
+                {List4, Args4} = replace_block(X, Out, Args2),            
+                {[List4 | List2], Args4}
+            end, {[], Args1}, List),		   
+		    {inherited, lists:reverse(lists:flatten([List3])), lists:flatten(Args3)};
+	    {error, Msg} ->
+	         io:format("TRACE ~p:~p ~p~n",[?MODULE, ?LINE, Msg]),
+	         io:format("TRACE ~p:~p Parent Parser failure: ~p~n",[?MODULE, ?LINE, Name]),
+		     {regular, Out, Args}			
+    end;
+	
+transl(nil, [{var, Line, Val}], Out, Args, _) ->
+    case lists:keysearch(Val, 2, Args) of
+        false ->
+            Key = list_to_atom(lists:concat(["A", length(Args) + 1])),
+            {regular, [{var, Line, Key} | Out], [{Key, Val} | Args]}; 
+        {value, {Key, _}} ->   
+            {regular, [{var, Line, Key} | Out], Args}
+    end;
+    
+transl(nil, [{tag, Line, _TagArgs}], Out, Args, _) ->
+    %% TODO: call insert_ tag code
+    {regular, [{string, Line, "not_fully_implemented_yet"} | Out], Args};
+
+transl(nil, [Token], Out, Args, _) ->
+    {regular, [Token | Out], Args}; 
+	
+transl([H | T], [{var, Line, Val}], Out, Args, DocRoot) ->
+    case lists:keysearch(Val, 2, Args) of
+        false ->           
+            Key = list_to_atom(lists:concat(["A", length(Args) + 1])),
+            transl(H, T, [{var, Line, Key} | Out], [{Key, Val} | Args], DocRoot);
+        {value, {Key, _}} ->  
+            transl(H, T, [{var, Line, Key} | Out], Args, DocRoot)
+	end;	 
+	
+transl([H | T], [{tag, Line, _TagArgs}], Out, Args, DocRoot) ->
+    %% TODO: call insert_tag code
+    transl(H, T, [{string, Line, "not_fully_implemented_yet"} | Out], Args, DocRoot);
+	
+transl([H | T], [Token], Out, Args, DocRoot) ->      
+    transl(H, T, [Token | Out], Args, DocRoot).
+
+
+replace_block({block, _Line, Name, [nil, Val]}, List, Args) ->
+	case lists:keysearch(Name, 3, List) of
+		false -> 
+			{Val, Args};
+		{value, {_, _, _, [H | T]}} ->  
+		    {_, List2, Args2} = transl(H, T, [], Args, undefined),
+		    {lists:reverse(List2), Args2} 
+ 	end;
+replace_block(Other, _What, Args) ->	
+	{Other, Args}.
+    
+	
+inplace_block({block, _Line , _Name, [nil, Str]}) ->
+	Str;
+inplace_block(Other) ->	
+	Other.