Skip to content

Commit

Permalink
Added optional terminate callback support to non gen_server zotonic m…
Browse files Browse the repository at this point in the history
…odules. Fixes zotonic#458
  • Loading branch information
mmzeeman committed Nov 13, 2012
1 parent 5027164 commit b46e80e
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 8 deletions.
8 changes: 6 additions & 2 deletions doc/manuals/modules/structure.rst
Expand Up @@ -48,8 +48,12 @@ When you need to execute code when the module starts, you can export
an optional ``init/1`` function. This function will be called when the
module is started. The parameter is a context record initialized for
the site the module will be running in. This is useful when you need
to initialize the database or other data structures for which you
don’t need a running process.
to initialize the database or other data structures for which you don’t
need a running process. When you also need to execute code when a module
stops you can export an optional ``terminate\2`` function. This function
will be called when the module terminates. The first parameter is a
Reason parameter which indicates why the module stopped. The second a
context record similar to the one in the ``init/1`` function.

When you do need a running process, read about those in the next
topic, :ref:`manual-modules-gen_server`.
Expand Down
22 changes: 16 additions & 6 deletions src/support/z_module_dummy.erl
Expand Up @@ -31,7 +31,7 @@
-export([
]).

-record(state, {context}).
-record(state, {module, context}).

%%====================================================================
%% API
Expand All @@ -40,7 +40,7 @@
%% @doc Starts the server
start_link(Args) when is_list(Args) ->
{ok, Pid} = gen_server:start_link(?MODULE, Args, []),
gen_server:cast(Pid, {init, proplists:get_value(module, Args)}),
gen_server:cast(Pid, init),
{ok, Pid}.


Expand All @@ -55,8 +55,9 @@ start_link(Args) when is_list(Args) ->
%% @doc Initiates the server.
init(Args) ->
process_flag(trap_exit, true),
{module, Module} = proplists:lookup(module, Args),
{context, Context} = proplists:lookup(context, Args),
{ok, #state{context=z_context:new(Context)}}.
{ok, #state{module=Module, context=z_context:new(Context)}}.

%% @spec handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
Expand All @@ -73,8 +74,8 @@ handle_call(Message, _From, State) ->
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @doc Handle the next step in the module initialization.
handle_cast({init, Module}, State) ->
dummy_module_init(Module, State#state.context),
handle_cast(init, #state{module=Module, context=Context}=State) ->
dummy_module_init(Module, Context),
{noreply, State};

%% @doc Trap unknown casts
Expand All @@ -95,7 +96,8 @@ handle_info(_Info, State) ->
%% 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.
terminate(_Reason, _State) ->
terminate(Reason, #state{module=Module, context=Context}) ->
dummy_module_terminate(Reason, Module, Context),
ok.

%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
Expand All @@ -117,6 +119,14 @@ dummy_module_init(Module, Context) ->
true -> Module:init(z_acl:sudo(Context));
false -> nop
end.

%% @doc When a module doesn't implement a gen_server then check if it exports a terminate/2 function,
%% if so then call that function with a fresh sudo context.
dummy_module_terminate(Reason, Module, Context) ->
case lists:member({terminate,2}, erlang:get_module_info(Module, exports)) of
true -> Module:terminate(Reason, z_acl:sudo(Context));
false -> nop
end.



0 comments on commit b46e80e

Please sign in to comment.