/src/mochiglobal.erl
Erlang | 127 lines | 82 code | 14 blank | 31 comment | 1 complexity | 2e3151aa1c638bafb486e4ccacafce45 MD5 | raw file
Possible License(s): MIT
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) 31get(K) -> 32 get(K, undefined). 33 34-spec get(atom(), T) -> any() | T. 35%% @doc Get the term for K or return Default. 36get(K, Default) -> 37 get(K, Default, key_to_module(K)). 38 39get(_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. 47put(K, V) -> 48 put(K, V, key_to_module(K)). 49 50put(_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. 58delete(K) -> 59 delete(K, key_to_module(K)). 60 61delete(_K, Mod) -> 62 code:purge(Mod), 63 code:delete(Mod). 64 65-spec key_to_module(atom()) -> atom(). 66key_to_module(K) -> 67 list_to_atom("mochiglobal:" ++ atom_to_list(K)). 68 69-spec compile(atom(), any()) -> binary(). 70compile(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()]. 76forms(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()]. 80term_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"). 102get_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.