Skip to content

Instantly share code, notes, and snippets.

@fishcakez
Forked from dvv/recypc_session.erl
Last active December 18, 2015 15:29
Show Gist options
  • Save fishcakez/5805105 to your computer and use it in GitHub Desktop.
Save fishcakez/5805105 to your computer and use it in GitHub Desktop.
-module(pecypc_session).
-author('Vladimir Dronnikov <dronnikov@gmail.com>').
%% -----------------------------------------------------------------------------
%% API exports
%% -----------------------------------------------------------------------------
-export([
add/1,
add/2,
kill/0,
kill/1,
remove/1,
start_link/1,
where/1
]).
-export([
call/1,
call/2,
read/0,
read/1,
touch/0,
touch/1,
write/2
]).
-export([
init/2,
handle/2
]).
%% -----------------------------------------------------------------------------
%% gen_server exports
%% -----------------------------------------------------------------------------
-behaviour(gen_server).
-export([
init/1, terminate/2, code_change/3,
handle_call/3, handle_cast/2, handle_info/2
]).
%% -----------------------------------------------------------------------------
%% API functions
%% -----------------------------------------------------------------------------
start_link({Id, _Data} = Arg) ->
gen_server:start_link({via, gproc, {n, l, {?MODULE, Id}}}, ?MODULE, Arg, []).
% call(Id, Msg) ->
% case gproc:where({n, l, {?MODULE, Id}}) of
% undefined -> {error, badarg};
% Pid -> gen_server:call(Pid, Msg)
% end.
% send(Prop, Msg) ->
% gproc:send({p, l, {?MODULE, Prop}}, Msg).
add(Data) ->
add(base64url:encode(crypto:rand_bytes(16)), Data).
add(Id, Data) ->
case supervisor:start_child(pecypc_session_sup, [{Id, Data}]) of
{ok, _Pid} ->
{ok, Id};
{error, {already_started, _Pid}} ->
{ok, Id}
end.
remove(Id) ->
gen_server:cast(where(Id), stop).
where(Id) ->
gproc:where({n, l, {?MODULE, Id}}).
%% @todo: get all ids?
all() ->
gproc:lookup_pids({p, l, ?MODULE}).
call(Cmd) ->
[gen_server:call(P, {command, Cmd}) || P <- all()].
call(Id, Cmd) ->
gen_server:call(where(Id), {command, Cmd}).
read() ->
[gen_server:call(P, get) || P <- all()].
read(Id) ->
gen_server:call(where(Id), get).
write(Id, Data) ->
gen_server:cast(where(Id), {set, Data}).
touch() ->
[gen_server:cast(P, touch) || P <- all()],
ok.
touch(Id) ->
gen_server:cast(where(Id), touch).
kill() ->
kill(normal).
kill(Reason) ->
[exit(P, Reason) || P <- all()],
ok.
%% -----------------------------------------------------------------------------
%% gen_server behaviour
%% -----------------------------------------------------------------------------
-record(state, {
expires,
handler,
state
}).
init({Id, Opts}) ->
pecypc_log:info({init_pecypc_session, Id, Opts}),
{_, Handler} = {handler, pecypc_session}, % @todo: configurable
true = gproc:reg({p, l, ?MODULE}),
% true = gproc:mreg(p, l, [{{?MODULE, Key}, Value} || {Key, Value} <- Props]),
{MegaSecs, Secs, _} = os:timestamp(),
State = #state{
expires = MegaSecs * 1000000 + Secs,
handler = Handler,
state = Handler:init(Id, Opts)
},
{ok, prolong(State)}.
terminate(Reason, State) ->
pecypc_log:info({terminate_pecypc_session, Reason, State}),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
handle_call(get, _From, State = #state{state = S}) ->
{reply, S, prolong(State)};
handle_call({command, Cmd}, _From, State = #state{handler = H, state = S}) ->
% pecypc_log:info({call_pecypc_session_command, Cmd, State}),
{Reply, S2} = H:handle(Cmd, S),
{reply, Reply, prolong(State#state{state = S2})};
handle_call(_Request, _From, State) ->
% pecypc_log:info({call_pecypc_session, Request, From, State}),
{reply, {error, badarg}, State}.
handle_cast({set, S}, State) ->
{noreply, prolong(State#state{state = S})};
handle_cast(touch, State) ->
{noreply, prolong(State)};
handle_cast(stop, State = #state{handler = H, state = S}) ->
H:handle(stop, S),
{stop, normal, State};
handle_cast(Msg, State = #state{handler = H, state = S}) ->
% pecypc_log:info({cast_pecypc_session, Msg, State}),
{ok, S2} = H:handle(Msg, S),
{noreply, prolong(State#state{state = S2})}.
handle_info({'EXIT', _, Reason}, State) ->
terminate(Reason, State),
{noreply, State};
handle_info(info, State) ->
pecypc_log:info({info_pecypc_session, info, State}),
{noreply, State}.
%% -----------------------------------------------------------------------------
%% Private functions
%% -----------------------------------------------------------------------------
prolong(State = #state{expires = Expires}) ->
Expires2 = Expires + 120,
State#state{expires = Expires2}.
%% -----------------------------------------------------------------------------
%% Handler functions
%% -----------------------------------------------------------------------------
init(_Id, Opts) ->
Opts.
handle(stop, State) ->
{ok, State};
handle({command, Cmd}, State) ->
pecypc_log:info({pecypc_session_handle, Cmd, State}),
{ok, State}.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment