12 |
7u83 |
1 |
%% @author Bob Ippolito <bob@mochimedia.com>
|
|
|
2 |
%% @copyright 2010 Mochi Media, Inc.
|
|
|
3 |
%%
|
|
|
4 |
%% Permission is hereby granted, free of charge, to any person obtaining a
|
|
|
5 |
%% copy of this software and associated documentation files (the "Software"),
|
|
|
6 |
%% to deal in the Software without restriction, including without limitation
|
|
|
7 |
%% the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
|
8 |
%% and/or sell copies of the Software, and to permit persons to whom the
|
|
|
9 |
%% Software is furnished to do so, subject to the following conditions:
|
|
|
10 |
%%
|
|
|
11 |
%% The above copyright notice and this permission notice shall be included in
|
|
|
12 |
%% all copies or substantial portions of the Software.
|
|
|
13 |
%%
|
|
|
14 |
%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
15 |
%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
16 |
%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
|
17 |
%% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
18 |
%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
|
19 |
%% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
|
20 |
%% DEALINGS IN THE SOFTWARE.
|
|
|
21 |
|
|
|
22 |
|
|
|
23 |
%% @doc Abuse module constant pools as a "read-only shared heap" (since erts 5.6)
|
|
|
24 |
%% <a href="http://www.erlang.org/pipermail/erlang-questions/2009-March/042503.html">[1]</a>.
|
|
|
25 |
-module(mochiglobal).
|
|
|
26 |
-author("Bob Ippolito <bob@mochimedia.com>").
|
|
|
27 |
-export([get/1, get/2, put/2, delete/1]).
|
|
|
28 |
|
|
|
29 |
-spec get(atom()) -> any() | undefined.
|
|
|
30 |
%% @equiv get(K, undefined)
|
|
|
31 |
get(K) ->
|
|
|
32 |
get(K, undefined).
|
|
|
33 |
|
|
|
34 |
-spec get(atom(), T) -> any() | T.
|
|
|
35 |
%% @doc Get the term for K or return Default.
|
|
|
36 |
get(K, Default) ->
|
|
|
37 |
get(K, Default, key_to_module(K)).
|
|
|
38 |
|
|
|
39 |
get(_K, Default, Mod) ->
|
|
|
40 |
try Mod:term()
|
|
|
41 |
catch error:undef ->
|
|
|
42 |
Default
|
|
|
43 |
end.
|
|
|
44 |
|
|
|
45 |
-spec put(atom(), any()) -> ok.
|
|
|
46 |
%% @doc Store term V at K, replaces an existing term if present.
|
|
|
47 |
put(K, V) ->
|
|
|
48 |
put(K, V, key_to_module(K)).
|
|
|
49 |
|
|
|
50 |
put(_K, V, Mod) ->
|
|
|
51 |
Bin = compile(Mod, V),
|
|
|
52 |
code:purge(Mod),
|
|
|
53 |
{module, Mod} = code:load_binary(Mod, atom_to_list(Mod) ++ ".erl", Bin),
|
|
|
54 |
ok.
|
|
|
55 |
|
|
|
56 |
-spec delete(atom()) -> boolean().
|
|
|
57 |
%% @doc Delete term stored at K, no-op if non-existent.
|
|
|
58 |
delete(K) ->
|
|
|
59 |
delete(K, key_to_module(K)).
|
|
|
60 |
|
|
|
61 |
delete(_K, Mod) ->
|
|
|
62 |
code:purge(Mod),
|
|
|
63 |
code:delete(Mod).
|
|
|
64 |
|
|
|
65 |
-spec key_to_module(atom()) -> atom().
|
|
|
66 |
key_to_module(K) ->
|
|
|
67 |
list_to_atom("mochiglobal:" ++ atom_to_list(K)).
|
|
|
68 |
|
|
|
69 |
-spec compile(atom(), any()) -> binary().
|
|
|
70 |
compile(Module, T) ->
|
|
|
71 |
{ok, Module, Bin} = compile:forms(forms(Module, T),
|
|
|
72 |
[verbose, report_errors]),
|
|
|
73 |
Bin.
|
|
|
74 |
|
|
|
75 |
-spec forms(atom(), any()) -> [erl_syntax:syntaxTree()].
|
|
|
76 |
forms(Module, T) ->
|
|
|
77 |
[erl_syntax:revert(X) || X <- term_to_abstract(Module, term, T)].
|
|
|
78 |
|
|
|
79 |
-spec term_to_abstract(atom(), atom(), any()) -> [erl_syntax:syntaxTree()].
|
|
|
80 |
term_to_abstract(Module, Getter, T) ->
|
|
|
81 |
[%% -module(Module).
|
|
|
82 |
erl_syntax:attribute(
|
|
|
83 |
erl_syntax:atom(module),
|
|
|
84 |
[erl_syntax:atom(Module)]),
|
|
|
85 |
%% -export([Getter/0]).
|
|
|
86 |
erl_syntax:attribute(
|
|
|
87 |
erl_syntax:atom(export),
|
|
|
88 |
[erl_syntax:list(
|
|
|
89 |
[erl_syntax:arity_qualifier(
|
|
|
90 |
erl_syntax:atom(Getter),
|
|
|
91 |
erl_syntax:integer(0))])]),
|
|
|
92 |
%% Getter() -> T.
|
|
|
93 |
erl_syntax:function(
|
|
|
94 |
erl_syntax:atom(Getter),
|
|
|
95 |
[erl_syntax:clause([], none, [erl_syntax:abstract(T)])])].
|
|
|
96 |
|
|
|
97 |
%%
|
|
|
98 |
%% Tests
|
|
|
99 |
%%
|
|
|
100 |
-ifdef(TEST).
|
|
|
101 |
-include_lib("eunit/include/eunit.hrl").
|
|
|
102 |
get_put_delete_test() ->
|
|
|
103 |
K = '$$test$$mochiglobal',
|
|
|
104 |
delete(K),
|
|
|
105 |
?assertEqual(
|
|
|
106 |
bar,
|
|
|
107 |
get(K, bar)),
|
|
|
108 |
try
|
|
|
109 |
?MODULE:put(K, baz),
|
|
|
110 |
?assertEqual(
|
|
|
111 |
baz,
|
|
|
112 |
get(K, bar)),
|
|
|
113 |
?MODULE:put(K, wibble),
|
|
|
114 |
?assertEqual(
|
|
|
115 |
wibble,
|
|
|
116 |
?MODULE:get(K))
|
|
|
117 |
after
|
|
|
118 |
delete(K)
|
|
|
119 |
end,
|
|
|
120 |
?assertEqual(
|
|
|
121 |
bar,
|
|
|
122 |
get(K, bar)),
|
|
|
123 |
?assertEqual(
|
|
|
124 |
undefined,
|
|
|
125 |
?MODULE:get(K)),
|
|
|
126 |
ok.
|
|
|
127 |
-endif.
|