forked from Alberdi/stamp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stamp_server.erl
126 lines (105 loc) · 5.07 KB
/
stamp_server.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
-module(stamp_server).
-behaviour(gen_server).
%% API
-export([start_link/0, post/2, get/2, get_tags/1, normalize/2]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-define(MAX_TAGS, 100).
-define(MAX_MSGS, 100).
%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
gen_server:start_link({local, server}, ?MODULE, [], []).
post(Tag, Msg) ->
gen_server:call(server, {post, Tag, Msg}).
get(Tag, N) ->
gen_server:call(server, {get, Tag, N}).
get_tags(N) ->
gen_server:call(server, {gettags, N}).
normalize(tag, Tag) ->
normalize(string:to_lower(Tag), 20, "[^a-z]");
normalize(msg, Msg) ->
normalize(string:strip(Msg), 140, "[<>]").
%%====================================================================
%% gen_server callbacks
%%====================================================================
%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
{ok, {dict:new(), []}}.
%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%% {reply, Reply, State, Timeout} |
%% {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, Reply, State} |
%% {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call({post, Tag, Msg}, _From, {Dict, Tags}) ->
M = preprocess(normalize(msg, Msg)),
T = normalize(tag, Tag),
case M == [] orelse T == [] of
true -> {reply, nook, {Dict, Tags}};
false -> {reply, ok, {dict:update(T, fun(Old) -> lists:sublist([M|Old], ?MAX_MSGS) end, [M], Dict),
lists:sublist([T|lists:delete(T, Tags)], ?MAX_TAGS)}}
end;
handle_call({get, Tag, N}, _From, {Dict, Tags}) ->
case dict:find(normalize(tag, Tag), Dict) of
{ok, L} -> {reply, lists:sublist(L, N), {Dict, Tags}};
error -> {reply, [], {Dict, Tags}}
end;
handle_call({gettags, N}, _From, {Dict, Tags}) ->
{reply, lists:sublist(Tags, N), {Dict, Tags}}.
%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
{noreply, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: 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.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
ok.
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
normalize(Str, N, Pat) ->
lists:sublist(re:replace(Str, Pat, "", [global, {return, list}]), N).
preprocess(Msg) ->
Subs = [{"\\$date", io_lib:format("<span>~w-~2..0w-~2..0w</span>", tuple_to_list(date()))},
{"\\$time", io_lib:format("<span>~2..0w:~2..0w:~2..0w</span>", tuple_to_list(time()))}],
lists:foldl(fun({Pat, Sub}, M) -> re:replace(M, Pat, Sub, [global, {return, list}]) end, Msg, Subs).