Skip to content

Commit

Permalink
[Fix #280] Fixes and enhancements on sumo event handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
cabol committed Oct 8, 2016
1 parent d7e608e commit ae2b13d
Show file tree
Hide file tree
Showing 13 changed files with 264 additions and 48 deletions.
6 changes: 5 additions & 1 deletion rebar.config
Expand Up @@ -106,4 +106,8 @@
{base_plt_prefix, "sumo_db"}
]}.

{plugins, [rebar3_hex]}.
{plugins, [rebar3_hex]}.

%% == Shell ==

{shell, [{apps, [sumo_db]}]}.
5 changes: 2 additions & 3 deletions src/adapter_test_helpers/sumo_basic_test_helper.erl
Expand Up @@ -229,9 +229,8 @@ init_store(Name) ->
%%% Internal functions
%%%=============================================================================

pick_up_event({Name, _, _} = Event) ->
EventManager = sumo_config:get_event_manager(Name),
EventManager:pick_up_event(Event).
pick_up_event(Event) ->
sumo_test_people_events_manager:pick_up_event(Event).

create(Name, Args) ->
Res = sumo:persist(Name, Args),
Expand Down
13 changes: 7 additions & 6 deletions src/adapter_test_helpers/sumo_events_test_helper.erl
Expand Up @@ -12,12 +12,13 @@
-spec events_manager_supervisor_running(Config :: config()) -> ok.
events_manager_supervisor_running(Config) ->
{_, DocName} = lists:keyfind(name, 1, Config),

case sumo_config:get_event_manager(DocName) of
undefined ->
case sumo_config:get_event_managers(DocName) of
[] ->
ok;
EventManager ->
ct:comment("~p should be running", [EventManager]),
[EventManager] = gen_event:which_handlers(EventManager),
EventManagers ->
ct:comment("~p should be running", [EventManagers]),
EventManagers = [begin
[E] = gen_event:which_handlers(EM), E
end || EM <- EventManagers],
ok
end.
4 changes: 1 addition & 3 deletions src/adapter_test_helpers/sumo_test_people_events_manager.erl
Expand Up @@ -18,9 +18,7 @@

-spec pick_up_event(tuple()) -> ok | no_event.
pick_up_event(Event) ->
EventMgr = proplists:get_value(
people, application:get_env(sumo_db, events, [])),
gen_event:call(EventMgr, ?MODULE, {pick_up_event, Event}).
gen_event:call(?MODULE, ?MODULE, {pick_up_event, Event}).

-spec init([]) -> {ok, state()}.
init([]) ->
Expand Down
105 changes: 80 additions & 25 deletions src/sumo_config.erl
Expand Up @@ -28,10 +28,15 @@
get_store/1,
get_props/1,
get_prop_value/2,
get_prop_value/3,
get_events/0,
get_event_manager/1
get_event_managers/0,
get_event_managers/1,
add_event_managers/2
]).

-include_lib("stdlib/include/ms_transform.hrl").

%%%===================================================================
%%% Types
%%%===================================================================
Expand All @@ -51,28 +56,18 @@

-spec init() -> ok.
init() ->
sumo_config = ets:new(sumo_config, [
protected,
named_table,
{read_concurrency, true}
]),
Docs = application:get_env(sumo_db, docs, []),
Events = application:get_env(sumo_db, events, []),
NewDocs = lists:foldl(fun({DocName, EventManager}, Acc) ->
case lists:keyfind(DocName, 1, Acc) of
{DocName, Store, Props} ->
NewDoc = {DocName, Store, Props#{event_manager => EventManager}},
lists:keyreplace(DocName, 1, Acc, NewDoc);
_ ->
Acc
end
end, Docs, Events),
true = ets:insert(sumo_config, NewDocs),
ok.
case ets:info(?MODULE) of
undefined ->
do_init();
_ ->
true = ets:delete(?MODULE),
do_init()
end.

-spec get_docs() -> [doc_config()].
get_docs() ->
application:get_env(sumo_db, docs, []).
MS = ets:fun2ms(fun({X, Y, Z}) -> {X, Y, Z} end),
ets:select(?MODULE, MS).

-spec get_doc(atom()) -> doc_config() | undefined.
get_doc(DocName) ->
Expand All @@ -91,23 +86,83 @@ get_props(DocName) ->

-spec get_prop_value(atom(), atom()) -> term().
get_prop_value(DocName, Prop) ->
maps:get(Prop, get_props(DocName), undefined).
get_prop_value(DocName, Prop, undefined).

-spec get_prop_value(atom(), atom(), term()) -> term().
get_prop_value(DocName, Prop, Default) ->
maps:get(Prop, get_props(DocName), Default).

-spec get_events() -> [event_config()].
get_events() ->
application:get_env(sumo_db, events, []).

-spec get_event_manager(atom()) -> module().
get_event_manager(DocName) ->
get_prop_value(DocName, event_manager).
-spec get_event_managers() -> [module()].
get_event_managers() ->
lookup_element('$event_managers', 2, []).

-spec get_event_managers(atom()) -> [module()].
get_event_managers(DocName) ->
get_prop_value(DocName, event_managers, []).

-spec add_event_managers(atom(), [module()] | module()) -> [doc_config()].
add_event_managers(DocName, EventManagers) ->
update_doc_entry(DocName, EventManagers, get_docs()).

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

%% @private
do_init() ->
?MODULE = ets:new(?MODULE, [named_table, public, {read_concurrency, true}]),
Docs = application:get_env(sumo_db, docs, []),
Events = application:get_env(sumo_db, events, []),
UpdatedDocs = load_entry(Docs, Events),
EvManagers = load_event_managers(UpdatedDocs),
Entries = [{'$event_managers', EvManagers} | UpdatedDocs],
true = ets:insert(?MODULE, Entries),
ok.

%% @private
load_entry(Docs, Events) ->
lists:foldl(fun({DocName, EventManagers}, DocAcc) ->
update_doc_entry(DocName, EventManagers, DocAcc)
end, Docs, Events).

%% @private
load_event_managers(Docs) ->
lists:usort(lists:foldl(fun({_, _, Props}, Acc) ->
maps:get(event_managers, Props, []) ++ Acc
end, [], Docs)).

%% @private
update_doc_entry(_DocName, [], Docs) ->
Docs;
update_doc_entry('_', EventManagers, Docs) when is_list(EventManagers) ->
DocNames = [DocName || {DocName, _, _} <- Docs],
lists:foldl(fun(DocName, DocAcc) ->
update_doc_entry(DocName, EventManagers, DocAcc)
end, Docs, DocNames);
update_doc_entry(DocName, EventManagers, Docs) when is_list(EventManagers) ->
case lists:keyfind(DocName, 1, Docs) of
{DocName, Store, Props} ->
CurrentEventManagers = maps:get(event_managers, Props, []),
NewEventManagers = lists:usort(EventManagers ++ CurrentEventManagers),
NewDoc = {DocName, Store, Props#{event_managers => NewEventManagers}},
lists:keyreplace(DocName, 1, Docs, NewDoc);
_ ->
Docs
end;
update_doc_entry(DocName, EventManager, Docs) when is_atom(EventManager) ->
update_doc_entry(DocName, [EventManager], Docs).

%% @private
lookup_element(Key, Pos) ->
lookup_element(Key, Pos, undefined).

%% @private
lookup_element(Key, Pos, Default) ->
try ets:lookup_element(?MODULE, Key, Pos)
catch
_:_ -> undefined
_:_ -> Default
end.
10 changes: 7 additions & 3 deletions src/sumo_event.erl
Expand Up @@ -36,7 +36,11 @@ dispatch(DocName, Event) ->
%% @doc Dispatch an event through gen_event:notify/2.
-spec dispatch(sumo:schema_name(), term(), term()) -> ok.
dispatch(DocName, Event, Args) ->
case sumo_config:get_event_manager(DocName) of
undefined -> ok;
EventManager -> gen_event:notify(EventManager, {DocName, Event, Args})
case sumo_config:get_event_managers(DocName) of
[] ->
ok;
EventManagers ->
lists:foreach(fun(EventManager) ->
gen_event:notify(EventManager, {DocName, Event, Args})
end, EventManagers)
end.
5 changes: 2 additions & 3 deletions src/sumo_event_manager_sup.erl
Expand Up @@ -43,9 +43,8 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).

-spec init(any()) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
init([]) ->
Events = sumo_config:get_events(),
EventsManagers = lists:usort([Manager || {_, Manager} <- Events]),
ManagersList = [manager(EventManager) || EventManager <- EventsManagers],
EventManagers = sumo_config:get_event_managers(),
ManagersList = [manager(EventManager) || EventManager <- EventManagers],
{ok, {{one_for_one, 5, 10}, ManagersList}}.

%%%=============================================================================
Expand Down
1 change: 1 addition & 0 deletions test/conditional_logic_SUITE.erl
Expand Up @@ -48,4 +48,5 @@ init_per_suite(Config) ->

-spec end_per_suite(config()) -> config().
end_per_suite(Config) ->
_ = application:stop(sumo_db),
Config.
4 changes: 1 addition & 3 deletions test/sumo_basic_SUITE.erl
Expand Up @@ -44,9 +44,6 @@ all() ->
-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
ok = sumo_test_utils:start_apps(),
EventMgr = proplists:get_value(
people, application:get_env(sumo_db, events, [])),
gen_event:add_handler(EventMgr, sumo_test_people_events_manager, []),
[{name, people} | Config].

-spec init_per_testcase(atom(), config()) -> config().
Expand All @@ -57,4 +54,5 @@ init_per_testcase(_, Config) ->

-spec end_per_suite(config()) -> config().
end_per_suite(Config) ->
_ = application:stop(sumo_db),
Config.
149 changes: 149 additions & 0 deletions test/sumo_config_SUITE.erl
@@ -0,0 +1,149 @@
-module(sumo_config_SUITE).

%% CT
-export([
all/0,
init_per_suite/1,
end_per_suite/1,
init_per_testcase/2
]).

%% Test Cases
-export([
t_sumo_config/1,
t_reload_sumo_config/1
]).

-define(EXCLUDED_FUNS, [
module_info,
all,
init_per_suite,
init_per_testcase
]).

-type config() :: [{atom(), term()}].

%%%=============================================================================
%%% Common Test
%%%=============================================================================

-spec all() -> [atom()].
all() ->
Exports = ?MODULE:module_info(exports),
[F || {F, _} <- Exports, not lists:member(F, ?EXCLUDED_FUNS)].

-spec init_per_suite(config()) -> config().
init_per_suite(Config) ->
ok = sumo_test_utils:start_apps(),
Docs = application:get_env(sumo_db, docs, []),
Events = application:get_env(sumo_db, events, []),
[{docs, Docs}, {events, Events} | Config].

-spec end_per_suite(config()) -> config().
end_per_suite(Config) ->
cleanup_config(Config),
_ = application:stop(sumo_db),
Config.

-spec init_per_testcase(atom(), config()) -> config().
init_per_testcase(_, Config) ->
setup_config(),
Config.

%%%=============================================================================
%%% Test Cases
%%%=============================================================================

-spec t_sumo_config(config()) -> ok.
t_sumo_config(_Config) ->
validate_docs(),

% expected doc managers
ExpectedManagersDoc1 = [
ev_manager1,
super_ev_manager
],
ExpectedManagersDoc2 = [
ev_manager1,
ev_manager2,
super_ev_manager
],
ExpectedManagersDoc3 = [
super_ev_manager
],

% check event managers
ExpectedManagersDoc1 = sumo_config:get_event_managers(doc1),
ExpectedManagersDoc2 = sumo_config:get_event_managers(doc2),
ExpectedManagersDoc3 = sumo_config:get_event_managers(doc3),
ExpectedManagersDoc3 = sumo_config:get_event_managers(doc4),

ok.

-spec t_reload_sumo_config(config()) -> ok.
t_reload_sumo_config(_Config) ->
validate_docs(),

% set other manager and reload it
NewDocs = [{docX, storeX, #{module => mod1}} | docs()],
application:set_env(sumo_db, docs, NewDocs),
application:set_env(sumo_db, events, [
{docX, ev_managerX},
{'_', super_ev_manager}
]),
sumo_config:init(),

% expected doc managers
ExpectedManagersDoc1 = [
ev_managerX,
super_ev_manager
],
ExpectedManagersDoc2 = [
super_ev_manager
],

% check event managers
ExpectedManagersDoc2 = sumo_config:get_event_managers(doc1),
ExpectedManagersDoc2 = sumo_config:get_event_managers(doc2),
ExpectedManagersDoc2 = sumo_config:get_event_managers(doc3),
ExpectedManagersDoc2 = sumo_config:get_event_managers(doc4),
ExpectedManagersDoc1 = sumo_config:get_event_managers(docX),

ok.

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

%% @private
setup_config() ->
ok = application:set_env(sumo_db, docs, docs()),
ok = application:set_env(sumo_db, events, events()),
sumo_config:init().

%% @private
cleanup_config(Config) ->
Docs = sumo_utils:keyfind(docs, Config),
Events = sumo_utils:keyfind(events, Config),
ok = application:set_env(sumo_db, docs, Docs),
ok = application:set_env(sumo_db, events, Events).

%% @private
docs() ->
[{doc1, store1, #{module => mod1}},
{doc2, store2, #{module => mod2}},
{doc3, store3, #{module => mod3}},
{doc4, store4, #{module => mod4}}].

%% @private
events() ->
[{doc1, ev_manager1},
{doc2, [ev_manager1, ev_manager2]},
{doc4, []},
{'_', super_ev_manager}].

%% @private
validate_docs() ->
Docs = lists:usort([DocName || {DocName, _, _} <- docs()]),
Docs = lists:usort([DocName || {DocName, _, _} <- sumo_config:get_docs()]),
ok.

0 comments on commit ae2b13d

Please sign in to comment.