From fe4a256c05727f43c6c793b620b1638b88c830b3 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Sat, 9 Oct 2010 15:04:16 +0200 Subject: Add advanced_event behaviour and use it for account events --- src/core/advanced_event.erl | 154 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 src/core/advanced_event.erl (limited to 'src/core/advanced_event.erl') diff --git a/src/core/advanced_event.erl b/src/core/advanced_event.erl new file mode 100644 index 0000000..d4a5b7e --- /dev/null +++ b/src/core/advanced_event.erl @@ -0,0 +1,154 @@ +-module(advanced_event). +-behaviour(gen_server). +-export([start_link/0, start_link/1, notify/2, sync_notify/2, + add_handler/3, delete_handler/3 + %, call/3, call/4 + ]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +-record(handler, {module :: atom(), + id, + state}). + +-callback init(InitArgs :: term()) -> + {ok, State :: term()}. +-callback handle_event(Event :: term(), State :: term()) -> + {ok, NewState :: term()} | + {handled, NewState :: term()} | + handled_and_remove | + remove_handler. +-callback handle_call(Request :: term(), State :: term()) -> + {ok, Reply :: term(), NewState :: term()} | + {remove_handler, Reply :: term()}. +-callback handle_info(Info :: term(), State :: term()) -> + {ok, NewState :: term()} | + remove_handler. +-callback terminate(Args :: (term() | {stop, Reason :: term()} | + stop | remove_handler | + {error, {'EXIT', Reason :: term()}} | + {error, term()}), + State :: term()) -> + term(). +-callback code_change(OldVsn :: (term() | {down, term()}), + State :: term(), Extra :: term()) -> + {ok, NewState :: term()}. + +-type handler() :: atom() | {atom(), term()}. +-type emgr_name() :: {'local', atom()} | {'global', atom()}. +-type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid(). +-type start_ret() :: {'ok', pid()} | {'error', term()}. + +-define(NO_CALLBACK, 'no callback module'). + +%-spec start_link() -> start_ret(). +start_link() -> + gen_server:start_link(?MODULE, [], []). + +%-spec start_link(emgr_name()) -> start_ret(). +start_link(Name) -> + gen_server:start_link(Name, ?MODULE, [], []). + +init(_Args) -> + process_flag(trap_exit, true), + {ok, []}. + +-spec add_handler(emgr_ref(), handler(), term()) -> term(). +add_handler(Manager, Handler, Args) -> gen_server:call(Manager, {add_handler, Handler, Args}). + +-spec delete_handler(emgr_ref(), handler(), term()) -> term(). +delete_handler(Manager, Handler, Args) -> gen_server:call(Manager, {delete_handler, Handler, Args}). + +-spec notify(emgr_ref(), term()) -> 'ok'. +notify(Manager, Event) -> gen_server:cast(Manager, {notify, Event}). + +-spec sync_notify(emgr_ref(), term()) -> 'ok'. +sync_notify(Manager, Event) -> gen_server:call(Manager, {sync_notify, Event}). + +%-spec call(emgr_ref(), handler(), term()) -> term(). +%call(M, Handler, Query) -> call1(M, Handler, Query). + +%-spec call(emgr_ref(), handler(), term(), timeout()) -> term(). +%call(M, Handler, Query, Timeout) -> call1(M, Handler, Query, Timeout). + + +-spec handle_call(term(), {pid(), term()}, list()) -> {reply, term(), list()}. +handle_call({add_handler, Handler, Args}, _From, Handlers) -> + {Module, Id} = case Handler of + {_Mod, _Id} -> + Handler; + Mod -> + {Mod, undefined} + end, + case catch Module:init(Args) of + {ok, State} -> + {reply,ok,[#handler{module=Module,id=Id,state=State}|Handlers]}; + Other -> + {reply,Other,Handlers} + end; +handle_call({delete_handler, Handler, Args}, _From, Handlers) -> + {Module, Id} = case Handler of + {_Mod, _Id} -> + Handler; + Mod -> + {Mod, undefined} + end, + case remove_from_list(Module, Id, Handlers) of + error -> + {reply,{error,module_not_found},Handlers}; + {H,Hs} -> + case catch (H#handler.module):terminate(Args, (H#handler.state)) of + Result -> + {reply,Result,Hs} + end + end; +handle_call({sync_notify,Event}, _From, Handlers) -> + Handlers1 = call_handlers(Event, Handlers), + {reply,ok,Handlers1}. + +handle_cast({notify,Event}, Handlers) -> + Handlers1 = call_handlers(Event, Handlers), + {noreply,Handlers1}. + +handle_info(_Msg, Handlers) -> + {noreply,Handlers}. + +remove_from_list(Module, Id, [Handler|Handlers]) -> + case Handler of + #handler{module=Module,id=Id} -> + {Handler,Handlers}; + _ -> + case remove_from_list(Module, id, Handlers) of + {H,Hs} -> + {H,[Handler|Hs]}; + error -> + error + end + end; +remove_from_list(_, _, []) -> + error. + +call_handlers(Event, [Handler|Handlers]) -> + case catch (Handler#handler.module):handle_event(Event, Handler#handler.state) of + {ok, State} -> + Rest = call_handlers(Event, Handlers), + [Handler#handler{state=State}|Rest]; + {handled, State} -> + [Handler#handler{state=State}|Handlers]; + remove_handler -> + (Handler#handler.module):terminate(remove_handler, Handler#handler.state), + call_handlers(Event, Handlers); + handled_and_remove -> + (Handler#handler.module):terminate(remove_handler, Handler#handler.state), + [] + end; +call_handlers(_, []) -> + []. + +terminate(Reason, [Handler|Handlers]) -> + (Handler#handler.module):terminate({stop, Reason}, (Handler#handler.state)), + terminate(Reason, Handlers); +terminate(_Reason, []) -> + ok. + +code_change(_OldVersion, Config, _Extra) -> + {ok, Config}. -- cgit v1.2.3