Skip to content

Instantly share code, notes, and snippets.

@rpip
Last active December 21, 2015 03:09
Show Gist options
  • Save rpip/6240580 to your computer and use it in GitHub Desktop.
Save rpip/6240580 to your computer and use it in GitHub Desktop.
A simple events hooks and callbacks module. This is my Erlang port of https://github.com/facine/Events
%%%-------------------------------------------------------------------
%%% @author Mawuli Adzaku <[email protected]>
%%% @copyright (C) 2013, Mawuli Adzaku
%%% @doc
%%% A simple events hooks and callbacks module.
%%% @end
%%% Created : 14 Aug 2013 by Mawuli Adzaku <[email protected]>
%%%-------------------------------------------------------------------
-module(events).
-behaviour(gen_server).
%% API
-export([start_link/0]).
-export([register/1, bind/2, unbind/2, fire/3, fire/2,
get_callbacks/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-record(state, {event_callbacks=[]}).
%% type definitions
-type event() :: string().
-type callback() :: fun().
-type entry() :: {event(), callback()}.
%%%===================================================================
%%% API
%%%===================================================================
%% @doc Register an event
-spec register(event()) -> {ok, list(entry())}.
register(Event)->
gen_server:cast(?SERVER, {add_event, Event}).
%% @doc Bind a callback to an event
-spec bind(event(), callback()) -> ok.
bind(Event, CallBack) ->
gen_server:cast(?SERVER, {bind,{Event, CallBack}}).
%% @doc unbind callback from event
-spec unbind(event(), callback()) -> ok.
unbind(Event, CallBack) ->
gen_server:cast(?SERVER, {unbind, {Event, CallBack}}).
%% @doc Return callbacks for an event
-spec get_callbacks(event()) -> {callbacks, event(), list(callback())}.
get_callbacks(Event)->
gen_server:call(?SERVER, {get_callbacks, Event}).
%% @doc Executes all callbacks when the event is fired
%% The Async paramater executes the callbacks asynchronously, and does
%% not return the return values of the callbacks. Defaults to Async.
-spec fire(event(), string()) -> ok | false.
fire(Event, Args) ->
fire(Event, Args, true).
-spec fire(event(), string(), Async :: true | false) -> list() | false | ok.
fire(Event, Args, true) ->
gen_server:cast(?SERVER, {fire_async, {Event, Args}});
fire(Event, Args, false) ->
gen_server:call(?SERVER, {fire, {Event, Args}}).
%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
{ok, #state{event_callbacks=[]}}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @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}
%% @end
%%--------------------------------------------------------------------
handle_call({fire,{Event, Args}}, _From, State) ->
CallBacks = State#state.event_callbacks,
case lists:keyfind(Event, 1, CallBacks) of
{Event, EventCallBacks} ->
Returns = lists:map(fun(CallBack) -> CallBack(Args) end, EventCallBacks),
Reply = {ok, Returns},
{reply, Reply, State};
false ->
{reply, ok, State}
end;
handle_call({get_callbacks, Event}, _From, State)->
Found = lists:keyfind(Event, 1, State#state.event_callbacks),
Reply = {callbacks, Found},
{reply, Reply, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_cast({add_event, Event}, State)->
CallBacks = State#state.event_callbacks,
CallBacks2 = lists:keystore(Event, 1, CallBacks, {Event,[]}),
{noreply, #state{event_callbacks=CallBacks2}};
handle_cast({bind, {Event, CallBack}}, State)->
CallBacks = State#state.event_callbacks,
case lists:keyfind(Event, 1, CallBacks) of
{Event, EventCallBacks} ->
CallBacks2 = lists:keyreplace(Event, 1, CallBacks, {Event, EventCallBacks ++ [CallBack]}),
{noreply, #state{event_callbacks=CallBacks2}};
false ->
events:register(Event),
events:bind(Event, CallBack),
{noreply, State}
end;
handle_cast({unbind, {Event, CallBack}}, State) ->
CallBacks = State#state.event_callbacks,
case lists:keyfind(Event, 1, CallBacks) of
{Event, EventCallBacks} ->
NewEventCallBacks = lists:filter(fun(X) -> X /= CallBack end, EventCallBacks),
CallBacks2 = lists:keyreplace(Event, 1, CallBacks, {Event, NewEventCallBacks}),
{noreply, #state{event_callbacks=CallBacks2}};
false ->
{noreply, State}
end;
handle_cast({fire_async,{Event, Args}}, State) ->
CallBacks = State#state.event_callbacks,
case lists:keyfind(Event, 1, CallBacks) of
{Event, EventCallBacks} ->
lists:foreach(fun(CallBack) -> CallBack(Args) end, EventCallBacks),
{noreply, State};
false ->
{noreply, State}
end;
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% @private
%% @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.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment