Skip to content

Commit

Permalink
add batch operation support
Browse files Browse the repository at this point in the history
  • Loading branch information
etrepum committed Apr 24, 2011
1 parent 1f03046 commit c4f8083
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 14 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -29,7 +29,8 @@ resolved in a deterministic manner.
An `op()` is a `{fun(), [term()]}`, with all but the last argument specified An `op()` is a `{fun(), [term()]}`, with all but the last argument specified
in the term list. For example `{ordsets:add_element/2, [a]}`. To evaluate in the term list. For example `{ordsets:add_element/2, [a]}`. To evaluate
this op, `ordsets:add_element(a, value(Statebox))` will be called. It is also this op, `ordsets:add_element(a, value(Statebox))` will be called. It is also
possible to specify an `op()` as a `{module(), atom(), [term()]}` tuple. possible to specify an `op()` as a `{module(), atom(), [term()]}` tuple, or
as a list of `op()` when performing several operations at the same timestamp.


There are several important limitations on the kinds of `op()` that are safe There are several important limitations on the kinds of `op()` that are safe
to use (`{F, [Arg]}` is the example `op()` used below): to use (`{F, [Arg]}` is the example `op()` used below):
Expand Down
49 changes: 36 additions & 13 deletions src/statebox.erl
Expand Up @@ -24,8 +24,9 @@
-type event() :: {timestamp(), op()}. -type event() :: {timestamp(), op()}.
-type timestamp() :: integer(). -type timestamp() :: integer().
-type timedelta() :: integer(). -type timedelta() :: integer().
-type op() :: {module(), atom(), [term()]} | -type basic_op() :: {module(), atom(), [term()]} |
{fun((...) -> statebox()), [term()]}. {fun((...) -> statebox()), [term()]}.
-type op() :: basic_op() | [op()].


%% Used in a test, must be done before function definitions. %% Used in a test, must be done before function definitions.
-ifdef(TEST). -ifdef(TEST).
Expand Down Expand Up @@ -108,8 +109,7 @@ merge(Unordered) ->
-spec modify(timestamp(), op(), statebox()) -> statebox(). -spec modify(timestamp(), op(), statebox()) -> statebox().
modify(T, Op, #statebox{value=Value, queue=Queue, last_modified=OldT}) modify(T, Op, #statebox{value=Value, queue=Queue, last_modified=OldT})
when OldT =< T -> when OldT =< T ->
Event = {T, Op}, new(T, apply_op(Op, Value), queue_in({T, Op}, Queue));
new(T, apply_event(Event, Value), queue_in(Event, Queue));
modify(T, _Op, #statebox{last_modified=OldT}) -> modify(T, _Op, #statebox{last_modified=OldT}) ->
throw({invalid_timestamp, {T, '<', OldT}}). throw({invalid_timestamp, {T, '<', OldT}}).


Expand Down Expand Up @@ -142,21 +142,28 @@ new(T, V, Q) ->
queue_in(Event, Queue) -> queue_in(Event, Queue) ->
Queue ++ [Event]. Queue ++ [Event].


apply_queue(Data, Queue) -> apply_queue(Data, [{_T, Op} | Rest]) ->
lists:foldl(fun apply_event/2, Data, Queue). apply_queue(apply_op(Op, Data), Rest);
apply_queue(Data, []) ->
Data.


apply_event({_T, {F, [A]}}, Data) when is_function(F, 2) -> apply_op({F, [A]}, Data) when is_function(F, 2) ->
F(A, Data); F(A, Data);
apply_event({_T, {F, [A, B]}}, Data) when is_function(F, 3) -> apply_op({F, [A, B]}, Data) when is_function(F, 3) ->
F(A, B, Data); F(A, B, Data);
apply_event({_T, {F, A}}, Data) when is_function(F) -> apply_op({F, A}, Data) when is_function(F) ->
apply(F, A ++ [Data]); apply(F, A ++ [Data]);
apply_event({_T, {M, F, [A]}}, Data) -> apply_op({M, F, [A]}, Data) ->
M:F(A, Data); M:F(A, Data);
apply_event({_T, {M, F, [A, B]}}, Data) -> apply_op({M, F, [A, B]}, Data) ->
M:F(A, B, Data); M:F(A, B, Data);
apply_event({_T, {M, F, A}}, Data) -> apply_op({M, F, A}, Data) ->
apply(M, F, A ++ [Data]). apply(M, F, A ++ [Data]);
apply_op([Op | Rest], Data) ->
apply_op(Rest, apply_op(Op, Data));
apply_op([], Data) ->
Data.



-ifdef(TEST). -ifdef(TEST).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
Expand Down Expand Up @@ -202,6 +209,22 @@ bad_modify_test() ->
dummy_mfa_4(a, b, C, D) -> dummy_mfa_4(a, b, C, D) ->
ordsets:add_element(C, D). ordsets:add_element(C, D).


batch_apply_op_test() ->
S = new(0, fun () -> [] end),
S0 = modify([], S),
S1 = modify([{ordsets, add_element, [N]} || N <- lists:seq(1, 1)], S),
S10 = modify([{ordsets, add_element, [N]} || N <- lists:seq(1, 10)], S),
?assertEqual(
[],
value(S0)),
?assertEqual(
lists:seq(1, 1),
value(S1)),
?assertEqual(
lists:seq(1, 10),
value(S10)),
ok.

alt_apply_op_test() -> alt_apply_op_test() ->
L = [fun (N=1) -> {ordsets, add_element, [N]} end, L = [fun (N=1) -> {ordsets, add_element, [N]} end,
fun (N=2) -> fun (N=2) ->
Expand Down

0 comments on commit c4f8083

Please sign in to comment.