Skip to content

Commit 32b2f69

Browse files
committed
[#30] – Allow to create the state passing the rest of its attributes
1 parent e3b1a89 commit 32b2f69

File tree

8 files changed

+204
-75
lines changed

8 files changed

+204
-75
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ test: check_rebar check_epmd
4444
rm -rf test/*.beam
4545

4646
local_test: check_rebar check_epmd
47-
$(REBAR) ct --suite=test/local_SUITE
47+
$(REBAR) ct --suite=test/task_SUITE,test/state_SUITE,test/local_SUITE
4848
$(REBAR) cover
4949
rm -rf test/*.beam
5050

README.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ Therefore, one of the most common and proven strategies to deal with these probl
2323

2424
Here is where **Shards** comes in. **Shards** makes extremely easy achieve all this, with **zero** effort.
2525
It provides an API compatible with [ETS](http://erlang.org/doc/man/ets.html) – with few exceptions.
26-
You can find the list of compatible ETS functions that **Shards** provides [HERE](https://github.com/cabol/shards/issues/1).
26+
You can check the list of compatible ETS functions that **Shards** provides [HERE](https://github.com/cabol/shards/issues/1).
2727

2828

2929
## Usage
@@ -228,9 +228,9 @@ If any microsecond matters to you, you can skip the call to the control ETS tabl
228228

229229
1. The first option is getting the `state`, and passing it as argument. Now the question is:
230230
how to get the **State**? Well, it's extremely easy, you can get the `state` when you call
231-
`shards:new/2` by first time, or you can call `shards:state/1` or `shards_state:get/1`
232-
at any time you want, and then it might be stored within the calling process, or wherever
233-
you want. E.g.:
231+
`shards:new/2` by first time, or you can call `shards:state/1`, `shards_state:get/1` or
232+
`shards_state:new/0,1,2,3,4` at any time you want, and then it might be stored within
233+
the calling process, or wherever you want. E.g.:
234234

235235
```erlang
236236
% take a look at the 2nd element of the returned tuple, that is the state
@@ -247,6 +247,11 @@ If any microsecond matters to you, you can skip the call to the control ETS tabl
247247
true
248248
> shards_local:lookup(mytab, 1, State).
249249
[{1,1}]
250+
251+
% in this case, only the n_shards is different from default, so you
252+
% can do this:
253+
> shards_local:lookup(mytab, 1, shards_state:new(4)).
254+
[{1,1}]
250255
```
251256

252257
2. The 2nd option is to call `shards_local` directly without the `state`, but this is only

src/shards_state.erl

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@
1010
-export([
1111
get/1,
1212
new/0,
13+
new/1,
14+
new/2,
15+
new/3,
16+
new/4,
1317
to_map/1,
1418
from_map/1
1519
]).
@@ -112,23 +116,30 @@
112116
%%% API
113117
%%%===================================================================
114118

115-
%% @doc
116-
%% Returns the `state' for the given table `Tab'.
117-
%% @end
118-
-spec get(Tab :: atom()) -> state().
119-
get(Tab) when is_atom(Tab) ->
120-
case ets:lookup(Tab, state) of
121-
[State] -> State;
122-
_ -> throw({badarg, Tab})
123-
end.
124-
125-
%% @doc
126-
%% Creates a new `state' with default values.
127-
%% @end
128119
-spec new() -> state().
129120
new() ->
130121
#state{}.
131122

123+
-spec new(pos_integer()) -> state().
124+
new(Shards) ->
125+
#state{n_shards = Shards}.
126+
127+
-spec new(pos_integer(), module()) -> state().
128+
new(Shards, Module) ->
129+
#state{n_shards = Shards, module = Module}.
130+
131+
-spec new(pos_integer(), module(), pick_fun()) -> state().
132+
new(Shards, Module, PickShardFun) ->
133+
#state{n_shards = Shards, module = Module, pick_shard_fun = PickShardFun}.
134+
135+
-spec new(pos_integer(), module(), pick_fun(), pick_fun()) -> state().
136+
new(Shards, Module, PickShardFun, PickNodeFun) ->
137+
#state{
138+
n_shards = Shards,
139+
module = Module,
140+
pick_shard_fun = PickShardFun,
141+
pick_node_fun = PickNodeFun}.
142+
132143
%% @doc
133144
%% Converts the given `state' into a `map'.
134145
%% @end
@@ -150,6 +161,16 @@ from_map(Map) ->
150161
pick_shard_fun = maps:get(pick_shard_fun, Map, fun shards_local:pick/3),
151162
pick_node_fun = maps:get(pick_node_fun, Map, fun shards_local:pick/3)}.
152163

164+
%% @doc
165+
%% Returns the `state' for the given table `Tab'.
166+
%% @end
167+
-spec get(Tab :: atom()) -> state().
168+
get(Tab) when is_atom(Tab) ->
169+
case ets:lookup(Tab, state) of
170+
[State] -> State;
171+
_ -> throw({badarg, Tab})
172+
end.
173+
153174
%%%===================================================================
154175
%%% API – Getters & Setters
155176
%%%===================================================================
@@ -165,8 +186,8 @@ module(Module, #state{} = State) when is_atom(Module) ->
165186
State#state{module = Module}.
166187

167188
-spec n_shards(state() | atom()) -> pos_integer().
168-
n_shards(#state{n_shards = NumShards}) ->
169-
NumShards;
189+
n_shards(#state{n_shards = Shards}) ->
190+
Shards;
170191
n_shards(Tab) when is_atom(Tab) ->
171192
n_shards(?MODULE:get(Tab)).
172193

src/shards_task.erl

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,30 @@
1111
-module(shards_task).
1212

1313
%% Task API
14-
-export([start/1, start/2, start/3]).
15-
-export([start_link/1, start_link/2, start_link/3]).
16-
-export([async/1, async/2, async/3]).
17-
-export([await/1, await/2]).
14+
-export([
15+
start/1,
16+
start/2,
17+
start/3,
18+
start_link/1,
19+
start_link/2,
20+
start_link/3,
21+
async/1,
22+
async/2,
23+
async/3,
24+
await/1,
25+
await/2
26+
]).
1827

1928
%% Task Supervised API
20-
-export([sup_start/2, sup_start_link/2, sup_start_link/4]).
21-
-export([sup_spawn_link/3, sup_spawn_link/4]).
22-
-export([reply/4, noreply/2]).
29+
-export([
30+
sup_start/2,
31+
sup_start_link/2,
32+
sup_start_link/4,
33+
sup_spawn_link/3,
34+
sup_spawn_link/4,
35+
reply/4,
36+
noreply/2
37+
]).
2338

2439
-define(TIMEOUT, 5000).
2540

@@ -285,8 +300,7 @@ initial_call(MFA) ->
285300
put('$initial_call', get_initial_call(MFA)).
286301

287302
%% @private
288-
get_initial_call({erlang, apply, [Fun, Args]})
289-
when is_function(Fun, length(Args)) ->
303+
get_initial_call({erlang, apply, [Fun, Args]}) when is_function(Fun, length(Args)) ->
290304
{module, Module} = erlang:fun_info(Fun, module),
291305
{name, Name} = erlang:fun_info(Fun, name),
292306
{Module, Name, length(Args)};
@@ -313,8 +327,10 @@ exit(Info, MFA, LogReason, Reason) ->
313327
exit(Reason).
314328

315329
%% @private
316-
get_from({Node, PidOrName}) when Node == node() -> PidOrName;
317-
get_from(Other) -> Other.
330+
get_from({Node, PidOrName}) when Node == node() ->
331+
PidOrName;
332+
get_from(Other) ->
333+
Other.
318334

319335
%% @private
320336
get_running({erlang, apply, [Fun, Args]}) when is_function(Fun, length(Args)) ->
@@ -323,8 +339,7 @@ get_running({Mod, Fun, Args}) ->
323339
{erlang:make_fun(Mod, Fun, length(Args)), Args}.
324340

325341
%% @private
326-
get_reason({undef, [{Mod, Fun, Args, _Info} | _] = Stacktrace} = Reason)
327-
when is_atom(Mod) and is_atom(Fun) ->
342+
get_reason({undef, [{Mod, Fun, Args, _Info} | _] = Stacktrace} = Reason) when is_atom(Mod) and is_atom(Fun) ->
328343
FunExported = fun
329344
(M, F, A) when is_list(A) ->
330345
erlang:function_exported(M, F, length(A));
@@ -346,9 +361,13 @@ get_reason(Reason) ->
346361
Reason.
347362

348363
%% @private
349-
reason(noconnection, Proc) -> {nodedown, monitor_node(Proc)};
350-
reason(Reason, _) -> Reason.
364+
reason(noconnection, Proc) ->
365+
{nodedown, monitor_node(Proc)};
366+
reason(Reason, _) ->
367+
Reason.
351368

352369
%% @private
353-
monitor_node(Pid) when is_pid(Pid) -> node(Pid);
354-
monitor_node({_, Node}) -> Node.
370+
monitor_node(Pid) when is_pid(Pid) ->
371+
node(Pid);
372+
monitor_node({_, Node}) ->
373+
Node.

test/dist_SUITE.erl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
-include_lib("mixer/include/mixer.hrl").
1717
-mixin([
1818
{test_helper, [
19-
t_state/1,
2019
t_basic_ops/1
2120
]}
2221
]).
@@ -44,7 +43,6 @@ all() ->
4443
groups() ->
4544
[{dist_test_group, [sequence], [
4645
t_join_leave_ops,
47-
t_state,
4846
t_basic_ops,
4947
t_match_ops,
5048
t_select_ops,

test/local_SUITE.erl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
-include_lib("mixer/include/mixer.hrl").
1616
-mixin([
1717
{test_helper, [
18-
t_state/1,
1918
t_basic_ops/1,
2019
t_match_ops/1,
2120
t_select_ops/1,

test/state_SUITE.erl

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
-module(state_SUITE).
2+
3+
-include_lib("common_test/include/ct.hrl").
4+
5+
%% Common Test
6+
-export([
7+
all/0,
8+
init_per_suite/1,
9+
end_per_suite/1
10+
]).
11+
12+
%% Test Cases
13+
-export([
14+
t_create_state/1,
15+
t_state_ops/1,
16+
t_get_state_badarg_error/1
17+
]).
18+
19+
-define(EXCLUDED_FUNS, [
20+
module_info,
21+
all,
22+
init_per_suite,
23+
end_per_suite
24+
]).
25+
26+
-include("test_helper.hrl").
27+
28+
%%%===================================================================
29+
%%% Common Test
30+
%%%===================================================================
31+
32+
all() ->
33+
Exports = ?MODULE:module_info(exports),
34+
[F || {F, _} <- Exports, not lists:member(F, ?EXCLUDED_FUNS)].
35+
36+
init_per_suite(Config) ->
37+
shards:start(),
38+
[{scope, l} | Config].
39+
40+
end_per_suite(Config) ->
41+
shards:stop(),
42+
Config.
43+
44+
%%%===================================================================
45+
%%% Tests Cases
46+
%%%===================================================================
47+
48+
t_create_state(_Config) ->
49+
% create state with all default attr values
50+
State0 = shards_state:new(),
51+
shards_local = shards_state:module(State0),
52+
true = ?N_SHARDS == shards_state:n_shards(State0),
53+
true = fun shards_local:pick/3 == shards_state:pick_shard_fun(State0),
54+
true = fun shards_local:pick/3 == shards_state:pick_node_fun(State0),
55+
56+
% create state using shards_state:new/1
57+
State1 = shards_state:new(4),
58+
shards_local = shards_state:module(State1),
59+
true = 4 == shards_state:n_shards(State1),
60+
true = fun shards_local:pick/3 == shards_state:pick_shard_fun(State1),
61+
true = fun shards_local:pick/3 == shards_state:pick_node_fun(State1),
62+
63+
% create state using shards_state:new/2
64+
State2 = shards_state:new(2, shards_dist),
65+
shards_dist = shards_state:module(State2),
66+
true = 2 == shards_state:n_shards(State2),
67+
true = fun shards_local:pick/3 == shards_state:pick_shard_fun(State2),
68+
true = fun shards_local:pick/3 == shards_state:pick_node_fun(State2),
69+
70+
% create state using shards_state:new/3
71+
Fun = fun(X, Y, Z) -> (X + Y + Z) rem Y end,
72+
State3 = shards_state:new(4, shards_dist, Fun),
73+
shards_dist = shards_state:module(State3),
74+
true = 4 == shards_state:n_shards(State3),
75+
true = Fun == shards_state:pick_shard_fun(State3),
76+
true = fun shards_local:pick/3 == shards_state:pick_node_fun(State3),
77+
78+
% create state using shards_state:new/4
79+
State4 = shards_state:new(4, shards_dist, Fun, Fun),
80+
shards_dist = shards_state:module(State4),
81+
true = 4 == shards_state:n_shards(State4),
82+
true = Fun == shards_state:pick_shard_fun(State4),
83+
true = Fun == shards_state:pick_node_fun(State4),
84+
85+
ok.
86+
87+
t_state_ops(_Config) ->
88+
test_set = shards:new(test_set, [
89+
{pick_node_fun, fun test_helper:pick_node/3}
90+
]),
91+
StateSet = shards_state:get(test_set),
92+
DefaultShards = ?N_SHARDS,
93+
#{n_shards := DefaultShards} = shards_state:to_map(StateSet),
94+
95+
Mod = shards_state:module(test_set),
96+
true = Mod == shards_local orelse Mod == shards_dist,
97+
DefaultShards = shards_state:n_shards(test_set),
98+
Fun1 = fun shards_local:pick/3,
99+
Fun1 = shards_state:pick_shard_fun(test_set),
100+
Fun2 = fun test_helper:pick_node/3,
101+
Fun2 = shards_state:pick_node_fun(test_set),
102+
103+
State0 = shards_state:new(),
104+
State1 = shards_state:module(shards_dist, State0),
105+
State2 = shards_state:n_shards(100, State1),
106+
Fun = fun(X, Y, Z) -> (X + Y + Z) rem Y end,
107+
State3 = shards_state:pick_shard_fun(Fun, State2),
108+
State4 = shards_state:pick_node_fun(Fun, State3),
109+
110+
#{module := shards_dist,
111+
n_shards := 100,
112+
pick_shard_fun := Fun,
113+
pick_node_fun := Fun
114+
} = shards_state:to_map(State4),
115+
116+
ok.
117+
118+
t_get_state_badarg_error(_Config) ->
119+
wrong_tab = ets:new(wrong_tab, [public, named_table]),
120+
_ = try shards_state:get(wrong_tab)
121+
catch _:{badarg, wrong_tab} -> ok
122+
end, ok.

0 commit comments

Comments
 (0)