Source

chut / src / chut_user_listen_handler.erl

%%%===================================================================
%%% This event handler is to be attached by every client of a user.
%%% Its role is to redirect messages routed through a user to its
%%% own client.
%%%===================================================================
-module(chut_user_listen_handler).
-compile(export_all).
-behaviour(gen_event).
-export([init/1, handle_event/2, handle_call/2, handle_info/2,
         terminate/2, code_change/3]).

%%====================================================================
%% gen_event callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(State={UserId, SubsriberPid, HandlerId}) -> {ok, State}
%% Description: Whenever a new event handler is added to an event manager,
%% this function is called to initialize the event handler. The state
%% is constituted of the User's Id (to couple it to messages), 
%% the client's pid (to send it messages) and a unique HandlerId (to
%% handle a client's termination and identify messages).
%%--------------------------------------------------------------------
init({UserId, SubscriberPid, HandlerId}) when is_pid(SubscriberPid) ->
    {ok, {UserId, SubscriberPid, HandlerId}}.

%%--------------------------------------------------------------------
%% Function:
%% handle_event(Event, State) -> {ok, State} |
%%                               {swap_handler, Args1, State1, Mod2, Args2} |
%%                               remove_handler
%% Description:Whenever an event manager receives an event sent using
%% gen_event:notify/2 or gen_event:sync_notify/2, this function is called for
%% each installed event handler to handle the event.
%%
%% Handles two kind of messages: received and sent messages. It wraps
%% them with a tuple containing the handler's id and the user's id
%% so many clients can be within a single process.
%%--------------------------------------------------------------------
handle_event({received, From, Msg}, {UserId, SubscriberPid, HandlerId}) ->
    SubscriberPid ! {{UserId, HandlerId}, {received, From, Msg}},
    {ok, {UserId, SubscriberPid, HandlerId}};
handle_event({send, To, Msg}, {UserId, SubscriberPid, HandlerId}) ->
    SubscriberPid ! {{UserId, HandlerId}, {sent, To, Msg}},
    {ok, {UserId, SubscriberPid, HandlerId}};
handle_event(_Event, State) ->
    {ok, State}.

%%--------------------------------------------------------------------
%% Function:
%% handle_call(Request, State) -> {ok, Reply, State} |
%%                                {swap_handler, Reply, Args1, State1,
%%                                  Mod2, Args2} |
%%                                {remove_handler, Reply}
%% Description: Whenever an event manager receives a request sent using
%% gen_event:call/3,4, this function is called for the specified event
%% handler to handle the request.
%%--------------------------------------------------------------------
handle_call(_Request, State) ->
    Reply = ok,
    {ok, Reply, State}.


%%--------------------------------------------------------------------
%% Function:
%% handle_info(Info, State) -> {ok, State} |
%%                             {swap_handler, Args1, State1, Mod2, Args2} |
%%                              remove_handler
%% Description: This function is called for each installed event handler when
%% an event manager receives any other message than an event or a synchronous
%% request (or a system message).
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
  {ok, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description:Whenever an event handler is deleted from an event manager,
%% this function is called. It should be the opposite of Module:init/1 and
%% do any necessary cleaning up.
%%
%% Whenever the termination reason is stop (the client is dead), the
%% handler is removed. This is a call that's a bit ugly, because it
%% goes straight to the fsm in order to make sure no dead handlers
%% are counted.
%% It also removes the link between the client and the manager to avoid
%% killing process pools for no reason.
%%--------------------------------------------------------------------
terminate({stop,_}, {UserId, SubscriberPid, HandlerId}) ->
  chut_user_monitor:delete_handler(UserId, HandlerId),
  unlink(SubscriberPid),
  ok;
terminate(_Reason, _State) ->
  ok.

%%--------------------------------------------------------------------
%% Function: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
  {ok, State}.