%% @author Marc Worrell <marc@worrell.nl>
%% @copyright 2010 Marc Worrell
%% Date: 2010-07-05
%% @doc Dummy gen_server for modules without any gen_server code.
%% We use this dummy gen_server so that we still can use a simple otp supervisor to oversee the
%% running modules.
%% Copyright 2010 Marc Worrell
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
-module(z_module_dummy).
-author("Marc Worrell <marc@worrell.nl>").
-behaviour(gen_server).
%% gen_server exports
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-export([start_link/1]).
-record(state, {module :: atom(), site :: atom()}).
-include("zotonic.hrl").
%%====================================================================
%% API
%%====================================================================
%% @spec start_link(Args) -> {ok,Pid} | ignore | {error,Error}
%% @doc Starts the server
start_link(Args) when is_list(Args) ->
{ok, Pid} = gen_server:start_link(?MODULE, Args, []),
gen_server:cast(Pid, init),
{ok, Pid}.
%%====================================================================
%% gen_server callbacks
%%====================================================================
%% @spec init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% @doc Initiates the server.
init(Args) ->
process_flag(trap_exit, true),
{module, Module} = proplists:lookup(module, Args),
{context, Context} = proplists:lookup(context, Args),
Site = z_context:site(Context),
logger:set_process_metadata(#{
site => Site,
module => ?MODULE
}),
{ok, #state{module=Module, site=Site}}.
%% @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 Trap unknown calls
handle_call(Message, _From, State) ->
{stop, {unknown_call, Message}, State}.
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @doc Handle the next step in the module initialization.
handle_cast(init, #state{module=Module, site=Site}=State) ->
dummy_module_init(Module, z_context:new(Site)),
{noreply, State};
%% @doc Trap unknown casts
handle_cast(Message, State) ->
{stop, {unknown_cast, Message}, State}.
%% @spec handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @doc Handling all non call/cast messages
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.
terminate(Reason, #state{module=Module, site=Site}) ->
dummy_module_terminate(Reason, Module, z_context:new(Site)),
ok.
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @doc Convert process state when code is changed
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%====================================================================
%% support functions
%%====================================================================
%% @doc When a module doesn't implement a gen_server then check if it exports an init/1 function,
%% if so then call that function with a fresh sudo context.
dummy_module_init(Module, Context) ->
case lists:member({init,1}, erlang:get_module_info(Module, exports)) of
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.