Last active
December 21, 2015 03:09
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%%%------------------------------------------------------------------- | |
%%% @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