Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

e_dict supports unlimited deep structures

  • Loading branch information...
commit 130de891d85c0bbc5a928c801a2da1e6bb596522 1 parent 5b6a440
Michal Ptaszek authored
1  Emakefile
@@ -49,3 +49,4 @@
49 49 debug_info,
50 50 strict_record_tests,
51 51 netload]}.
  52 +
2  bin/start.erl
@@ -636,6 +636,8 @@ copy_escripts(RootDir) ->
636 636 {ok, _} ->
637 637 Files = [filename:join(["bin", "compile.erl"]),
638 638 filename:join(["bin", "add.erl"]),
  639 + filename:join(["bin", "generate.erl"]),
  640 + filename:join(["bin", "test.erl"]),
639 641 filename:join(["bin", "e_component.erl"])],
640 642
641 643 case filelib:is_file("Emakefile") of
2  bin/test.erl
@@ -43,6 +43,8 @@ start_interactive_mode_node(ReportDir) ->
43 43 print_output(Port).
44 44
45 45 compile() ->
  46 + [code:add_path(Dir) || Dir <- filelib:wildcard(filename:join(["lib", "*", "test"]))],
  47 + [code:add_path(Dir) || Dir <- filelib:wildcard(filename:join(["lib", "*", "ebin"]))],
46 48 lists:foreach(fun(App) ->
47 49 FilesTest = filelib:wildcard(filename:join(["lib", App, "test", "*erl"])),
48 50 make:files(FilesTest, [{outdir, filename:join(["lib", App, "test"])},
158 lib/eptic-1.3/src/e_dict.erl
@@ -15,7 +15,7 @@
15 15
16 16 %%%-------------------------------------------------------------------
17 17 %%% File : e_dict.erl
18   -%%% @author Martin Carlson <martin@erlang-consulting.com>
  18 +%%% @author Michal Ptaszek <michal.ptaszek@erlang-consulting.com>
19 19 %%% @doc Module responsible for managing the request dictionary.
20 20 %%% @end
21 21 %%%-------------------------------------------------------------------
@@ -23,7 +23,7 @@
23 23
24 24 %% API
25 25 -export([init_state/1, terminate_state/0, get_state/0]).
26   --export([fset/2, fset/3, fget/1, fget/2, fget/3, finsert/3]).
  26 +-export([fset/2, fset/3, fget/1, fget/2, fget0/2, fget/3, finsert/3]).
27 27 -export([fdelete/1, fdelete/2]).
28 28
29 29 -export([start_link/0]).
@@ -31,8 +31,6 @@
31 31 -export([handle_call/3, handle_cast/2, handle_info/2]).
32 32 -export([code_change/3]).
33 33
34   --record(state, {}).
35   -
36 34 %%====================================================================
37 35 %% Exported functions
38 36 %%====================================================================
@@ -41,13 +39,13 @@ start_link() ->
41 39 gen_server:start_link(?MODULE, [], []).
42 40
43 41 %% @hidden
44   -init_state(Dict) when is_list(Dict) ->
45   - ets:insert(?MODULE, {self(), dict:from_list(Dict)});
46   -init_state(Dict) ->
47   - ets:insert(?MODULE, {self(), Dict}).
  42 +init_state(List) when is_list(List) ->
  43 + ets:insert(?MODULE, {self(), List});
  44 +init_state(Dict) when is_tuple(Dict) ->
  45 + ets:insert(?MODULE, {self(), dict:to_list(Dict)}).
48 46
49 47 %% @hidden
50   --spec(get_state/0 :: () -> {ok, term()} | undefined).
  48 +-spec(get_state/0 :: () -> {ok, list()} | undefined).
51 49 get_state() ->
52 50 case ets:lookup(?MODULE, self()) of
53 51 [{_, Dict}] ->
@@ -61,18 +59,15 @@ terminate_state() ->
61 59 ets:delete(?MODULE, self()).
62 60
63 61 %%
64   -%% @spec fset(Key :: term(), Value :: term()) -> true
  62 +%% @spec fset(Key :: string(), Value :: term()) -> true
65 63 %% @doc Inserts the <i>Value</i> under the specified <i>Key</i> in the request dictionary.
66 64 %%
67   --spec(fset/2 :: (term(), term()) -> true).
  65 +-spec(fset/2 :: (string(), term()) -> true).
68 66 fset(Key, Value) ->
69   - case ets:lookup(?MODULE, self()) of
70   - [{_, Dict}] -> ets:insert(?MODULE, {self(), dict:store(Key, Value, Dict)});
71   - [] -> exit(no_dict_attached)
72   - end.
  67 + fset0(string:tokens(Key, [$:]), Value).
73 68
74 69 %%
75   -%% @spec fset(List :: term(), Key :: term(), Value :: term()) -> true
  70 +%% @spec fset(List :: string(), Key :: string(), Value :: term()) -> true
76 71 %% @doc Appends the <i>Value</i> into the <i>List</i> under the <i>Key</i> in the request dictionary.
77 72 %% <i>List</i> is the top-level key to the proplist in the dictionary.
78 73 %% If there is no list, the new one is created - the call is equivalent to:
@@ -82,25 +77,30 @@ fset(Key, Value) ->
82 77 %% Otherwise, the <i>{Key, Value}</i> is appended to the list and the list is then
83 78 %% stored in the request dictionary.
84 79 %%
85   --spec(fset/3 :: (term(), term(), term()) -> true).
  80 +-spec(fset/3 :: (string(), string(), term()) -> true).
86 81 fset(List, Key, Value) ->
  82 + fset0([List, Key], Value).
  83 +
  84 +-spec(fset0/2 :: (list(string()), term()) -> true).
  85 +fset0(Keys, Value) ->
87 86 case ets:lookup(?MODULE, self()) of
88 87 [{_, Dict}] ->
89   - Current = dict_fetch(List, Dict),
90   - check_store({Key, Value},Current,List,Dict);
91   - [] ->
  88 + ets:insert(?MODULE, {self(), fset0(Keys, Value, Dict)});
  89 + [] ->
92 90 exit(no_dict_attached)
93 91 end.
94 92
95   -check_store(T, undefined, List, Dict) ->
96   - ets:insert(?MODULE, {self(), dict:store(List,[T], Dict)});
97   -
98   -check_store(T, Current, List, Dict) ->
99   - New = case lists:member(T,Current) of
100   - true -> Current;
101   - false -> [T|Current]
102   - end,
103   - ets:insert(?MODULE, {self(), dict:store(List, New, Dict)}).
  93 +-spec(fset0/3 :: (list(string()), term(), tuple()) -> tuple()).
  94 +fset0([Key], Value, Dict) ->
  95 + lists:keystore(Key, 1, Dict, {Key, Value});
  96 +fset0([Key | Rest], Value, Dict) ->
  97 + SubDict = case proplists:get_value(Key, Dict) of
  98 + undefined ->
  99 + [];
  100 + S ->
  101 + S
  102 + end,
  103 + lists:keystore(Key, 1, Dict, {Key, fset0(Rest, Value, SubDict)}).
104 104
105 105 %%
106 106 %% @spec finsert(List :: term(), Key :: term(), Value :: term()) -> true
@@ -113,49 +113,50 @@ check_store(T, Current, List, Dict) ->
113 113 %%
114 114 -spec(finsert/3 :: (term(), term(), term()) -> true).
115 115 finsert(List, Key, Value) ->
116   - case ets:lookup(?MODULE, self()) of
117   - [{_, Dict}] ->
118   - Current = dict_fetch(List, Dict),
119   - New = case lists:keysearch(Key,1,Current) of
120   - {value, _} -> lists:keyreplace(Key,1,Current,{Key,Value});
121   - false -> [{Key,Value}|Current]
122   - end,
123   - ets:insert(?MODULE, {self(), dict:store(List, New, Dict)});
124   - [] -> exit(no_dict_attached)
125   - end.
126   -
  116 + fset0([List, Key], Value).
  117 +
127 118 %%
128   -%% @spec fget(Key :: term()) -> Value :: term() | undefined
  119 +%% @spec fget(Key :: string()) -> Value :: term() | undefined
129 120 %% @doc Fetches the element kept in the request dictionary under the <i>Key</i>.
130 121 %% If there is no element under <i>Key</i> inside the request dictionary,
131 122 %% <i>undefined</i> is returned.
132 123 %%
133   --spec(fget/1 :: (term()) -> term()).
134   -fget(Key) ->
  124 +-spec(fget/1 :: (string()) -> term()).
  125 +fget(Key0) ->
  126 + Key = string:tokens(Key0, [$:]),
135 127 case ets:lookup(?MODULE, self()) of
136   - [{_, Dict}] -> dict_fetch(Key, Dict);
137   - [] -> exit(no_dict_attached)
  128 + [{_, Dict}] ->
  129 + fget0(Key, Dict);
  130 + [] ->
  131 + exit(no_dict_attached)
138 132 end.
139 133
140 134 %%
141   -%% @spec fget(List :: term(), Key :: term()) -> term() | undefined
  135 +%% @spec fget(List :: string(), Key :: string()) -> term() | undefined
142 136 %% @doc Fetches the element kept in the <i>List</i> in the dictionary under the <i>Key</i>.
143 137 %% <i>List</i> is top-level key to the container stored in the request dictionary.
144 138 %% The values kept in the container under the <i>Key</i> are returned.
145 139 %% If there is no values under <i>Key</i> or there is no <i>List</i>
146 140 %% in the request dictionary, the <i>undefined</i> is returned.
147 141 %%
148   --spec(fget/2 :: (term(), term()) -> term()).
  142 +-spec(fget/2 :: (string(), string()) -> term()).
149 143 fget(List, Key) ->
150   - case fget(List) of
151   - PropList when is_list(PropList) ->
152   - case proplists:get_all_values(Key, PropList) of
153   - [] -> undefined;
154   - [Value] -> Value;
155   - Values -> Values
156   - end;
157   - Result ->
158   - Result
  144 + case ets:lookup(?MODULE, self()) of
  145 + [{_, Dict}] ->
  146 + fget0([List, Key], Dict);
  147 + [] ->
  148 + exit(no_dict_attached)
  149 + end.
  150 +
  151 +-spec(fget0/2 :: (list(string()), tuple()) -> term()).
  152 +fget0([Key], Dict) ->
  153 + proplists:get_value(Key, Dict);
  154 +fget0([Key | Rest], Dict) ->
  155 + case proplists:get_value(Key, Dict) of
  156 + undefined ->
  157 + undefined;
  158 + SubDict ->
  159 + fget0(Rest, SubDict)
159 160 end.
160 161
161 162 %% @hidden
@@ -175,10 +176,11 @@ fget(List, Key, Validator) ->
175 176 %% pernamently.
176 177 %%
177 178 -spec(fdelete/1 :: (term()) -> true).
178   -fdelete(Key) ->
  179 +fdelete(Key0) ->
  180 + Key = string:tokens(Key0, [$:]),
179 181 case ets:lookup(?MODULE, self()) of
180 182 [{_, Dict}] ->
181   - ets:insert(?MODULE, {self(), dict:erase(Key, Dict)});
  183 + fdelete0(Key, Dict);
182 184 [] ->
183 185 exit(no_dict_attached)
184 186 end.
@@ -194,28 +196,33 @@ fdelete(Key) ->
194 196 fdelete(List, Key) ->
195 197 case ets:lookup(?MODULE, self()) of
196 198 [{_, Dict}] ->
197   - case dict:find(List, Dict) of
198   - {ok, Proplist} ->
199   - case lists:keydelete(Key, 1, Proplist) of
200   - [] ->
201   - ets:insert(?MODULE, {self(), dict:erase(List, Dict)});
202   - NewProplist ->
203   - ets:insert(?MODULE, {self(), dict:store(List, NewProplist, Dict)})
204   - end;
205   - error ->
206   - true
207   - end;
208   - [] ->
  199 + fdelete0([List, Key], Dict);
  200 + [] ->
209 201 exit(no_dict_attached)
210 202 end.
211 203
  204 +-spec(fdelete0/2 :: (list(string()), tuple()) -> true).
  205 +fdelete0(Keys, Dict) ->
  206 + ets:insert(?MODULE, {self(), fdelete1(Keys, Dict)}).
  207 +
  208 +-spec(fdelete1/2 :: (list(string()), tuple()) -> tuple()).
  209 +fdelete1([Key], Dict) ->
  210 + lists:keydelete(Key, 1, Dict);
  211 +fdelete1([Key | Rest], Dict) ->
  212 + case proplists:get_value(Key, Dict) of
  213 + undefined ->
  214 + Dict;
  215 + SubDict ->
  216 + lists:keystore(Key, 1, Dict, {Key, fdelete1(Rest, SubDict)})
  217 + end.
  218 +
212 219 %%====================================================================
213 220 %% Server functions
214 221 %%====================================================================
215 222 %% @hidden
216 223 init([]) ->
217 224 ets:new(?MODULE, [named_table, public]),
218   - {ok, #state{}}.
  225 + {ok, not_used}.
219 226
220 227 %% @hidden
221 228 handle_call(_Req, _From, State) ->
@@ -237,12 +244,3 @@ terminate(_Reason, _State) ->
237 244 %% @hidden
238 245 code_change(_VSN, State, _Extra) ->
239 246 {ok, State}.
240   -
241   -%%====================================================================
242   -%% Internal functions
243   -%%====================================================================
244   -dict_fetch(Key, Dict) ->
245   - case dict:find(Key, Dict) of
246   - {ok, Value} -> Value;
247   - error -> undefined
248   - end.
136 lib/eptic-1.3/test/e_dict_test.erl
... ... @@ -0,0 +1,136 @@
  1 +%% The contents of this file are subject to the Erlang Web Public License,
  2 +%% Version 1.0, (the "License"); you may not use this file except in
  3 +%% compliance with the License. You should have received a copy of the
  4 +%% Erlang Web Public License along with this software. If not, it can be
  5 +%% retrieved via the world wide web at http://www.erlang-consulting.com/.
  6 +%%
  7 +%% Software distributed under the License is distributed on an "AS IS"
  8 +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9 +%% the License for the specific language governing rights and limitations
  10 +%% under the License.
  11 +%%
  12 +%% The Initial Developer of the Original Code is Erlang Training & Consulting
  13 +%% Ltd. Portions created by Erlang Training & Consulting Ltd are Copyright 2009,
  14 +%% Erlang Training & Consulting Ltd. All Rights Reserved.
  15 +
  16 +%%%-------------------------------------------------------------------
  17 +%%% File : e_dict_test.erl
  18 +%%% Author : Michal Ptaszek <michal.ptaszek@erlang-consulting.com>
  19 +%%% Description :
  20 +%%%
  21 +%%% Created : 26 Oct 2009 by michalptaszek <michalptaszek@coltrane.erlangsystems.com>
  22 +%%%-------------------------------------------------------------------
  23 +-module(e_dict_test).
  24 +
  25 +-compile(export_all).
  26 +
  27 +-include_lib("eunit/include/eunit.hrl").
  28 +-include_lib("ewts/include/request.hrl").
  29 +
  30 +single_level_store_test() ->
  31 + e_dict:init_state([]),
  32 + ?assert(e_dict:fset("test", "test")),
  33 + ?assert(e_dict:fset("abc", 123)),
  34 + ?assert(e_dict:fset("aaa", {this, is, a, test})),
  35 +
  36 + {ok, Dict} = e_dict:get_state(),
  37 + ?assertEqual(lists:sort(Dict),
  38 + lists:sort([{"test", "test"},
  39 + {"abc", 123},
  40 + {"aaa", {this, is, a, test}}])).
  41 +
  42 +double_level_store_test() ->
  43 + e_dict:init_state([]),
  44 + ?assert(e_dict:fset("test", "test")),
  45 + ?assert(e_dict:fset("abc", 123)),
  46 + ?assert(e_dict:fset("aaa", {this, is, a, test})),
  47 + ?assert(e_dict:fset("1:2", second_level)),
  48 + ?assert(e_dict:fset("1:3", third_level)),
  49 + ?assert(e_dict:fset("test2:wow", 0)),
  50 +
  51 + {ok, Dict} = e_dict:get_state(),
  52 + ?assertEqual(lists:sort(Dict),
  53 + lists:sort([{"test", "test"},
  54 + {"abc", 123},
  55 + {"aaa", {this, is, a, test}},
  56 + {"1", [{"2", second_level},
  57 + {"3", third_level}]},
  58 + {"test2", [{"wow", 0}]}])).
  59 +
  60 +multi_level_store_test() ->
  61 + e_dict:init_state([]),
  62 + ?assert(e_dict:fset("test", "test")),
  63 + ?assert(e_dict:fset("abc", 123)),
  64 + ?assert(e_dict:fset("aaa", {this, is, a, test})),
  65 + ?assert(e_dict:fset("1:2", second_level)),
  66 + ?assert(e_dict:fset("1:3", third_level)),
  67 + ?assert(e_dict:fset("test2:wow", 0)),
  68 + ?assert(e_dict:fset("a:b:c:d:e", f)),
  69 +
  70 + {ok, Dict} = e_dict:get_state(),
  71 + ?assertEqual(lists:sort(Dict),
  72 + lists:sort([{"test", "test"},
  73 + {"abc", 123},
  74 + {"a", [{"b",
  75 + [{"c",
  76 + [{"d",
  77 + [{"e", f}]}]}]}]},
  78 + {"aaa", {this, is, a, test}},
  79 + {"1", [{"2", second_level},
  80 + {"3", third_level}]},
  81 + {"test2", [{"wow", 0}]}])).
  82 +
  83 +get_test() ->
  84 + e_dict:init_state([]),
  85 + ?assert(e_dict:fset("test", "test")),
  86 + ?assert(e_dict:fset("abc", 123)),
  87 + ?assert(e_dict:fset("aaa", {this, is, a, test})),
  88 + ?assert(e_dict:fset("1:2", second_level)),
  89 + ?assert(e_dict:fset("1:3", third_level)),
  90 + ?assert(e_dict:fset("test2:wow", 0)),
  91 + ?assert(e_dict:fset("a:b:c:d:e", f)),
  92 +
  93 + {ok, Dict} = e_dict:get_state(),
  94 + ?assertEqual(lists:sort(Dict),
  95 + lists:sort([{"test", "test"},
  96 + {"abc", 123},
  97 + {"a", [{"b",
  98 + [{"c",
  99 + [{"d",
  100 + [{"e", f}]}]}]}]},
  101 + {"aaa", {this, is, a, test}},
  102 + {"1", [{"2", second_level},
  103 + {"3", third_level}]},
  104 + {"test2", [{"wow", 0}]}])),
  105 +
  106 + ?assertEqual("test", e_dict:fget("test")),
  107 + ?assertEqual(123, e_dict:fget("abc")),
  108 + ?assertEqual({this, is, a, test}, e_dict:fget("aaa")),
  109 + ?assertEqual([{"2", second_level}, {"3", third_level}], e_dict:fget("1")),
  110 + ?assertEqual(second_level, e_dict:fget("1:2")),
  111 + ?assertEqual(third_level, e_dict:fget("1:3")),
  112 + ?assertEqual(f, e_dict:fget("a:b:c:d:e")).
  113 +
  114 +delete_test() ->
  115 + e_dict:init_state([]),
  116 + ?assert(e_dict:fset("test", "test")),
  117 + ?assert(e_dict:fset("abc", 123)),
  118 + ?assert(e_dict:fset("aaa", {this, is, a, test})),
  119 + ?assert(e_dict:fset("1:2", second_level)),
  120 + ?assert(e_dict:fset("1:3", third_level)),
  121 + ?assert(e_dict:fset("test2:wow", 0)),
  122 + ?assert(e_dict:fset("a:b:c:d:e", f)),
  123 + ?assert(e_dict:fdelete("abc")),
  124 + ?assert(e_dict:fdelete("1:2")),
  125 + ?assert(e_dict:fdelete("test2")),
  126 +
  127 + {ok, Dict} = e_dict:get_state(),
  128 + ?assertEqual(lists:sort(Dict),
  129 + lists:sort([{"test", "test"},
  130 + {"a", [{"b",
  131 + [{"c",
  132 + [{"d",
  133 + [{"e", f}]}]}]}]},
  134 + {"aaa", {this, is, a, test}},
  135 + {"1", [{"3", third_level}]}])).
  136 +
30 lib/ewts-1.0/src/ewts.erl
@@ -82,34 +82,8 @@ start_tests(Outdir) ->
82 82
83 83 -spec(fget/2 :: (string(), term()) -> undefined | term()).
84 84 fget(Key0, Dict) ->
85   - case string:tokens(Key0, ":") of
86   - [List, Key] ->
87   - fget0(List, Key, Dict);
88   - [Key] ->
89   - fget0(Key, Dict)
90   - end.
91   -
92   --spec(fget0/2 :: (string(), term()) -> undefined | term()).
93   -fget0(Key, Dict) ->
94   - case dict:find(Key, Dict) of
95   - {ok, Val} ->
96   - Val;
97   - error ->
98   - undefined
99   - end.
100   -
101   --spec(fget0/3 :: (string(), string(), term()) -> undefined | term()).
102   -fget0(List, Key, Dict) ->
103   - case fget0(List, Dict) of
104   - PropList when is_list(PropList) ->
105   - case proplists:get_all_values(Key, PropList) of
106   - [] -> undefined;
107   - [Value] -> Value;
108   - Values -> Values
109   - end;
110   - Result ->
111   - Result
112   - end.
  85 + Key = string:tokens(Key0, ":"),
  86 + e_dict:fget0(Key, Dict).
113 87
114 88 -spec(get_apps/0 :: () -> (list(string()))).
115 89 get_apps() ->

0 comments on commit 130de89

Please sign in to comment.
Something went wrong with that request. Please try again.