Skip to content

Commit

Permalink
Merge pull request #285 from inaka/ferigis.284.link_events_with_preev…
Browse files Browse the repository at this point in the history
…ents

[#284] link events and pre events, improve README
  • Loading branch information
Brujo Benavides authored Oct 13, 2016
2 parents c7a1383 + 7c9dd1d commit f89fae6
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 44 deletions.
42 changes: 42 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,48 @@ These three concepts have a specific meaning in the context of sumo_db.
TO implement an adapter, you must implement two behaviors: `sumo_backend` and
`sumo_store`.

## Events

Sumo dispatches events when things happen. An Event has this structure:
```erlang
{EventId, Model, Event, Args}
```
- `EventId` is a `reference()` which identifies the event
- `Model` is the model where the event happend, for example, if we are creating a new entitiy in the model `people` the value of `Model` would be `people`.
- `Event` is the type of the event.
- `Args` extra data sent.

Supported types of events:

- `pre_persisted` just before persisting some entity. This event has the entity we want to persist as `Args`. It is dispatched on this function:
- `sumo:persist/2`
- `persisted` just after persisting some entity. This event has the persisted entity as `Args`. This Event has the same `EventId` as its `pre_persisted` event. It is dispatched on this function:
- `sumo:persist/2`
- `pre_delete_all` just before deleting all entities for a model. This event has no `Args`. It is dispatched on this function:
- `sumo:delete_all/1`
- `deleted_all` just after deleting all entities for a model. This event has no `Args`. This Event has the same `EventId` as its `pre_delete_all` event. It is dispatched on this function:
- `sumo:delete_all/1`
- `pre_deleted` just before deleting an entity. This event has the entity id as `Args`. It is dispatched on this function:
- `sumo:delete/2`
- `deleted` just after deleting an entity. This event has the entity id as `Args`. This Event has the same `EventId` as its `pre_deleted` event. It is dispatched on this function:
- `sumo:delete/2`
- `pre_deleted_total` just before deleting by some delete conditions. This event has the sumo conditions as `Args`. It is dispatched on this function:
- `sumo:delete_by/2`
- `deleted_total` just after deleting by some delete conditions. This event has a list with the number of entities deleted and the delete conditions as `Args`. This Event has the same `EventId` as its `pre_deleted_total` event. It is dispatched on this function:
- `sumo:delete_by/2`
- `pre_schema_created` just before creating a sumo schema. This event has no `Args`. It is dispatched on this function:
- `sumo:create_schema/2`
- `schema_created` just after creating a sumo schema. This event has no `Args`. This Event has the same `EventId` as its `pre_schema_created` event. It is dispatched on this function:
- `sumo:create_schema/2`

Sumo requires users to add their own `gen_event`'s in order to handle those events. In order to add them Users have to configure sumo properly. In the `config` file we can add them like this under `sumo_db` configuration:
```erlang
{events, [
{'_', sumo_test_people_events_manager},
{people, sumo_test_people_events_manager}
]}
```
Sumo allows us to add a `gen_event` to one type of model (i.e. `people`) or for all (`'_'`).

## Example

Expand Down
34 changes: 18 additions & 16 deletions src/adapter_test_helpers/sumo_basic_test_helper.erl
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@ delete_all(Config) ->
{_, Name} = lists:keyfind(name, 1, Config),

sumo:delete_all(Name),
pick_up_event({Name, pre_delete_all, []}),
pick_up_event({Name, delete_all, []}),
{EventId, Name, pre_delete_all, []} = pick_up_event(),
{EventId, Name, deleted_all, []} = pick_up_event(),
[] = sumo:find_all(Name),
0 = sumo:count(Name),
ok.
Expand All @@ -131,8 +131,8 @@ delete(Config) ->
2 = sumo:delete_by(Name, Conditions),
6 = sumo:count(Name),

ok = pick_up_event({Name, pre_deleted_total, [Conditions]}),
ok = pick_up_event({Name, deleted_total, [2, Conditions]}),
{EventId, Name, pre_deleted_total, [Conditions]} = pick_up_event(),
{EventId, Name, deleted_total, [2, Conditions]} = pick_up_event(),

Results = sumo:find_by(Name, Conditions),
[] = Results,
Expand All @@ -142,11 +142,12 @@ delete(Config) ->
Id = Module:id(First),
sumo:delete(Name, Id),

ok = pick_up_event({Name, pre_deleted, [Id]}),
ok = pick_up_event({Name, deleted, [Id]}),
% sumo:delete/2 uses internally sumo:delete_by/2, we handle those events too
IdField = sumo_internal:id_field_name(Name),
ok = pick_up_event({Name, pre_deleted_total, [[{IdField, Id}]]}),
ok = pick_up_event({Name, deleted_total, [1, [{IdField, Id}]]}),
{EventId2, Name, pre_deleted, [Id]} = pick_up_event(),
{EventId4, Name, pre_deleted_total, [[{IdField, Id}]]} = pick_up_event(),
{EventId4, Name, deleted_total, [1, [{IdField, Id}]]} = pick_up_event(),
{EventId2, Name, deleted, [Id]} = pick_up_event(),

NewAll = sumo:find_all(Name),
[_] = All -- NewAll,
Expand Down Expand Up @@ -178,12 +179,8 @@ check_proper_dates(Config) ->
-spec init_store(atom()) -> ok.
init_store(Name) ->
sumo:create_schema(Name),
ok = pick_up_event({Name, pre_schema_created, []}),
ok = pick_up_event({Name, schema_created, []}),
Module = sumo_config:get_prop_value(Name, module),
sumo:delete_all(Name),
ok = pick_up_event({Name, pre_delete_all, []}),
ok = pick_up_event({Name, deleted_all, []}),

DT = {Date, _} = calendar:universal_time(),

Expand Down Expand Up @@ -223,17 +220,22 @@ init_store(Name) ->
_:no_workers -> ok
end,

clean_events(),
ok.

%%%=============================================================================
%%% Internal functions
%%%=============================================================================

pick_up_event(Event) ->
sumo_test_people_events_manager:pick_up_event(Event).
pick_up_event() ->
sumo_test_people_events_manager:pick_up_event().

clean_events() ->
sumo_test_people_events_manager:clean_events().

create(Name, Args) ->
clean_events(),
Res = sumo:persist(Name, Args),
ok = pick_up_event({Name, pre_persisted, [Args]}),
ok = pick_up_event({Name, persisted, [Res]}),
{EventId, Name, pre_persisted, [Args]} = pick_up_event(),
{EventId, Name, persisted, [Res]} = pick_up_event(),
Res.
30 changes: 20 additions & 10 deletions src/adapter_test_helpers/sumo_test_people_events_manager.erl
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,21 @@
handle_event/2
]).

-export([pick_up_event/1]).
-export([
pick_up_event/0,
clean_events/0
]).

-record(state, {event_list = [] :: list()}).
-type state() :: #state{}.

-spec pick_up_event(tuple()) -> ok | no_event.
pick_up_event(Event) ->
gen_event:call(?MODULE, ?MODULE, {pick_up_event, Event}).
-spec pick_up_event() -> tuple() | no_event.
pick_up_event() ->
gen_event:call(?MODULE, ?MODULE, pick_up_event).

-spec clean_events() -> ok.
clean_events() ->
gen_event:call(?MODULE, ?MODULE, clean_events).

-spec init([]) -> {ok, state()}.
init([]) ->
Expand All @@ -31,12 +38,15 @@ handle_info(_Info, State) ->
-spec handle_call(Event, State) -> Result when
Event :: term(),
State :: state(),
Result :: {ok, ok | no_event | not_implemented, state()}.
handle_call({pick_up_event, Event}, #state{event_list = EventList} = State) ->
case lists:member(Event, EventList) of
true ->
{ok, ok, #state{event_list = lists:delete(Event, EventList)}};
false ->
Result :: {ok, ok | no_event | not_implemented | term(), state()}.
handle_call(clean_events, _State) ->
{ok, ok, #state{event_list = []}};
handle_call(pick_up_event, #state{event_list = EventList} = State) ->
try
Event = lists:last(EventList),
{ok, Event, #state{event_list = lists:delete(Event, EventList)}}
catch
error:function_clause ->
{ok, no_event, State}
end;
handle_call(_, State) ->
Expand Down
30 changes: 18 additions & 12 deletions src/sumo.erl
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,11 @@ persist(DocName, UserDoc) ->
Module = sumo_config:get_prop_value(DocName, module),
DocMap = Module:sumo_sleep(UserDoc),
Store = sumo_config:get_store(DocName),
sumo_event:dispatch(DocName, pre_persisted, [UserDoc]),
EventId = sumo_event:dispatch(DocName, pre_persisted, [UserDoc]),
case sumo_store:persist(Store, sumo_internal:new_doc(DocName, DocMap)) of
{ok, NewDoc} ->
Ret = sumo_internal:wakeup(NewDoc),
sumo_event:dispatch(DocName, persisted, [Ret]),
EventId = sumo_event:dispatch(DocName, EventId, persisted, [Ret]),
Ret;
Error ->
throw(Error)
Expand Down Expand Up @@ -218,11 +218,11 @@ find_by(DocName, Conditions, SortFields, Limit, Offset) ->
-spec delete_all(schema_name()) -> non_neg_integer().
delete_all(DocName) ->
Store = sumo_config:get_store(DocName),
sumo_event:dispatch(DocName, pre_delete_all),
EventId = sumo_event:dispatch(DocName, pre_delete_all),
case sumo_store:delete_all(Store, DocName) of
{ok, NumRows} ->
case NumRows > 0 of
true -> sumo_event:dispatch(DocName, deleted_all);
_ = case NumRows > 0 of
true -> EventId = sumo_event:dispatch(DocName, EventId, deleted_all, []);
_ -> ok
end,
NumRows;
Expand All @@ -234,22 +234,25 @@ delete_all(DocName) ->
-spec delete(schema_name(), user_doc()) -> boolean().
delete(DocName, Id) ->
IdField = sumo_internal:id_field_name(DocName),
sumo_event:dispatch(DocName, pre_deleted, [Id]),
EventId = sumo_event:dispatch(DocName, pre_deleted, [Id]),
case delete_by(DocName, [{IdField, Id}]) of
1 -> sumo_event:dispatch(DocName, deleted, [Id]), true;
1 -> EventId = sumo_event:dispatch(DocName, EventId, deleted, [Id]), true;
0 -> false
end.

%% @doc Deletes the doc identified by Conditions.
-spec delete_by(schema_name(), conditions()) -> non_neg_integer().
delete_by(DocName, Conditions) ->
Store = sumo_config:get_store(DocName),
sumo_event:dispatch(DocName, pre_deleted_total, [Conditions]),
EventId = sumo_event:dispatch(DocName, pre_deleted_total, [Conditions]),
case sumo_store:delete_by(Store, DocName, Conditions) of
{ok, 0} ->
0;
{ok, NumRows} ->
sumo_event:dispatch(DocName, deleted_total, [NumRows, Conditions]),
EventId = sumo_event:dispatch(DocName,
EventId,
deleted_total,
[NumRows, Conditions]),
NumRows;
Error ->
throw(Error)
Expand Down Expand Up @@ -294,10 +297,13 @@ create_schema(DocName) ->
%% @end
-spec create_schema(schema_name(), atom()) -> ok.
create_schema(DocName, Store) ->
sumo_event:dispatch(DocName, pre_schema_created),
EventId = sumo_event:dispatch(DocName, pre_schema_created),
case sumo_store:create_schema(Store, sumo_internal:get_schema(DocName)) of
ok -> sumo_event:dispatch(DocName, schema_created), ok;
Error -> throw(Error)
ok ->
EventId = sumo_event:dispatch(DocName, EventId, schema_created, []),
ok;
Error ->
throw(Error)
end.

%% @doc Returns a new schema.
Expand Down
26 changes: 20 additions & 6 deletions src/sumo_event.erl
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,39 @@
-license("Apache License 2.0").

%%% API
-export([dispatch/2, dispatch/3]).
-export([dispatch/2, dispatch/3, dispatch/4]).

%%% Types
-type event_id() :: reference().

-export_type([event_id/0]).

%%%=============================================================================
%%% API
%%%=============================================================================

%% @doc Dispatch an event through gen_event:notify/2.
-spec dispatch(sumo:schema_name(), term()) -> ok.
-spec dispatch(sumo:schema_name(), term()) -> event_id() | no_event_managers.
dispatch(DocName, Event) ->
dispatch(DocName, Event, []).

%% @doc Dispatch an event through gen_event:notify/2.
-spec dispatch(sumo:schema_name(), term(), term()) -> ok.
-spec dispatch(sumo:schema_name(), term(), term()) ->
event_id() | no_event_managers.
dispatch(DocName, Event, Args) ->
EventId = make_ref(),
dispatch(DocName, EventId, Event, Args).

%% @doc Dispatch an event through gen_event:notify/2.
-spec dispatch(sumo:schema_name(), event_id(), term(), term()) ->
event_id() | no_event_managers.
dispatch(DocName, EventId, Event, Args) ->
case sumo_config:get_event_managers(DocName) of
[] ->
ok;
no_event_managers;
EventManagers ->
lists:foreach(fun(EventManager) ->
gen_event:notify(EventManager, {DocName, Event, Args})
end, EventManagers)
gen_event:notify(EventManager, {EventId, DocName, Event, Args})
end, EventManagers),
EventId
end.

0 comments on commit f89fae6

Please sign in to comment.