Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
Merge remote-tracking branch 'github/pr/15'
  • Loading branch information
kxepal committed Oct 27, 2015
2 parents 77dfaf4 + 86531ee commit 807aa3f58e39f1273e732020c80d630d9177790c
Showing 3 changed files with 99 additions and 3 deletions.
@@ -101,6 +101,22 @@ Notes:
- if the same plugin provides multiple implementations of the same service
the order is as defined in providers callback

## decide functionality

There are cases when we want to call configured providers until any of them
would make a decission. We also would want to be able to find out if any
decision has been made so we could call default handler. In order to be able
to do so there is couch_epi:decide/5. Every service which uses this feature
would get either:

- no_decision
- {decided, Decision :: term()}

The provider module should return one of the above results. The current logic is
to call all configured providers in order of their definition until we get
`{decided, term()}`. If none of the providers would return this term we would
return `no_decision`.

# couch_epi_plugin behaviour

The module implementing behaviour need to export following functions:
@@ -22,7 +22,7 @@
keys/1, subscribers/1]).

%% apply
-export([apply/5]).
-export([apply/5, decide/5]).
-export([any/5, all/5]).

-export([is_configured/3]).
@@ -169,3 +169,10 @@ is_configured(Handle, Function, Arity) when Handle /= undefined ->

register_service(Plugin, Children) ->
couch_epi_sup:plugin_childspecs(Plugin, Children).

-spec decide(Handle :: handle(), ServiceId :: atom(), Function :: atom(),
Args :: [term()], Opts :: apply_opts()) ->
no_decision | {decided, term()}.

decide(Handle, ServiceId, Function, Args, Opts) when Handle /= undefined ->
couch_epi_functions_gen:decide(Handle, ServiceId, Function, Args, Opts).
@@ -22,7 +22,8 @@
-export([
apply/4,
apply/5,
modules/3
modules/3,
decide/5
]).

-ifdef(TEST).
@@ -34,7 +35,8 @@
-record(opts, {
ignore_errors = false,
pipe = false,
concurrent = false
concurrent = false,
interruptible = false
}).

get_handle(ServiceId) ->
@@ -51,6 +53,15 @@ apply(Handle, _ServiceId, Function, Args, Opts) ->
Modules = providers(Handle, Function, length(Args), DispatchOpts),
dispatch(Handle, Modules, Function, Args, DispatchOpts).

-spec decide(Handle :: atom(), ServiceId :: atom(), Function :: atom(),
Args :: [term()], Opts :: couch_epi:apply_opts()) ->
no_decision | {decided, term()}.

decide(Handle, _ServiceId, Function, Args, Opts) ->
DispatchOpts = parse_opts([interruptible|Opts]),
Modules = providers(Handle, Function, length(Args), DispatchOpts),
dispatch(Handle, Modules, Function, Args, DispatchOpts).

%% ------------------------------------------------------------------
%% Codegeneration routines
%% ------------------------------------------------------------------
@@ -241,6 +252,9 @@ dispatch(Handle, Modules, Function, Args,
lists:foldl(fun(Module, Acc) ->
Handle:dispatch(Module, Function, Acc)
end, Args, Modules);
dispatch(Handle, Modules, Function, Args,
#opts{interruptible = true}) ->
apply_while(Modules, Handle, Function, Args);
dispatch(Handle, Modules, Function, Args, #opts{} = Opts) ->
[do_dispatch(Handle, Module, Function, Args, Opts) || Module <- Modules].

@@ -258,6 +272,15 @@ do_dispatch(Handle, Module, Function, Args,
do_dispatch(Handle, Module, Function, Args, #opts{}) ->
Handle:dispatch(Module, Function, Args).

apply_while([], _Handle, _Function, _Args) ->
no_decision;
apply_while([Module | Modules], Handle, Function, Args) ->
case Handle:dispatch(Module, Function, Args) of
no_decision ->
apply_while(Modules, Handle, Function, Args);
{decided, _Decission} = Result ->
Result
end.

parse_opts(Opts) ->
parse_opts(Opts, #opts{}).
@@ -268,6 +291,8 @@ parse_opts([pipe|Rest], #opts{} = Acc) ->
parse_opts(Rest, Acc#opts{pipe = true});
parse_opts([concurrent|Rest], #opts{} = Acc) ->
parse_opts(Rest, Acc#opts{concurrent = true});
parse_opts([interruptible|Rest], #opts{} = Acc) ->
parse_opts(Rest, Acc#opts{interruptible = true});
parse_opts([], Acc) ->
Acc.

@@ -326,4 +351,52 @@ basic_test() ->

ok.

generate_module(Name, Body) ->
Tokens = couch_epi_codegen:scan(Body),
couch_epi_codegen:generate(Name, Tokens).

decide_module(decide) ->
"
-export([inc/1]).
inc(A) ->
{decided, A + 1}.
";
decide_module(no_decision) ->
"
-export([inc/1]).
inc(_A) ->
no_decision.
".

decide_test() ->
ok = generate_module(decide, decide_module(decide)),
ok = generate_module(no_decision, decide_module(no_decision)),

DecideDef = {foo_app, [{decide, [{inc, 1}]}]},
NoDecissionDef = {bar_app, [{no_decision, [{inc, 1}]}]},

DecideFirstHandle = decide_first_handle,
ok = generate(DecideFirstHandle, [DecideDef, NoDecissionDef]),
?assertMatch([decide, no_decision], DecideFirstHandle:providers(inc, 1)),
?assertMatch({decided,4}, decide(DecideFirstHandle, anything, inc, [3], [])),

DecideSecondHandle = decide_second_handle,
ok = generate(DecideSecondHandle, [NoDecissionDef, DecideDef]),
?assertMatch([no_decision, decide], DecideSecondHandle:providers(inc, 1)),
?assertMatch({decided,4}, decide(DecideSecondHandle, anything, inc, [3], [])),

NoDecissionHandle = no_decision_handle,
ok = generate(NoDecissionHandle, [NoDecissionDef]),
?assertMatch([no_decision], NoDecissionHandle:providers(inc, 1)),
?assertMatch(no_decision, decide(NoDecissionHandle, anything, inc, [3], [])),

NoHandle = no_handle,
ok = generate(NoHandle, []),
?assertMatch([], NoHandle:providers(inc, 1)),
?assertMatch(no_decision, decide(NoHandle, anything, inc, [3], [])),

ok.

-endif.

0 comments on commit 807aa3f

Please sign in to comment.