Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support get_env plugin methods #83

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 84 additions & 33 deletions src/gproc.erl
Original file line number Diff line number Diff line change
Expand Up @@ -448,36 +448,54 @@ do_get_env(Context, App, Key, Alternatives, Set) ->
set_env(Scope, App, Key, Value, Strategy)
when Scope==l, is_atom(App), is_atom(Key);
Scope==g, is_atom(App), is_atom(Key) ->
case is_valid_set_strategy(Strategy, Value) of
case is_valid_set_strategy(Strategy, Value, Scope) of
true ->
update_cached_env(Scope, App, Key, Value),
set_strategy(Strategy, App, Key, Value);
set_strategy(Strategy, App, Key, Value, Scope);
false ->
erlang:error(badarg)
end.

check_alternatives([{default, Val}|Alts], Scope, App, Key, _, Set) ->
check_alternatives(Alts, Scope, App, Key, Val, Set);
check_alternatives([H|T], Scope, App, Key, Def, Set) ->
case try_alternative(H, App, Key, Scope) of
check_alternatives(Alts, Scope, App, Key, Def, Set) ->
check_alternatives(Alts, Scope, App, Key, Def, Set, []).

check_alternatives([{default, Val}|Alts], Scope, App, Key, _, Set, Caches) ->
check_alternatives(Alts, Scope, App, Key, Val, Set, Caches);
check_alternatives([H|T], Scope, App, Key, Def, Set, Caches) ->
{Alt, Cache} = fetch_alternative(H, Scope, Set),
case try_alternative(Alt, App, Key, Scope) of
undefined ->
check_alternatives(T, Scope, App, Key, Def, Set);
check_alternatives(T, Scope, App, Key, Def, Set,
maybe_cache(Cache, Alt, Caches));
{ok, Value} ->
if Set ->
cache_env(Scope, App, Key, Value),
cache_env(Scope, App, Key, Value, Caches),
Value;
true ->
Value
end
end;
check_alternatives([], Scope, App, Key, Def, Set) ->
check_alternatives([], Scope, App, Key, Def, Set, Caches) ->
if Set ->
cache_env(Scope, App, Key, Def);
cache_env(Scope, App, Key, Def, Caches);
true ->
ok
end,
Def.

fetch_alternative({reg, Prop}, Scope, Set) ->
try get_value_shared({p, Scope, Prop}) of
{M, F} ->
{{reg, Prop, M, F}, false};
{M, F, cache} ->
{{reg, Prop, M, F}, Set}
catch
error:_ ->
{undefined, false}
end;
fetch_alternative(Alt, _, _) ->
{Alt, false}.

try_alternative(error, App, Key, Scope) ->
erlang:error(gproc_env, [App, Key, Scope]);
try_alternative(inherit, App, Key, Scope) ->
Expand Down Expand Up @@ -523,7 +541,9 @@ try_alternative({mnesia,Type,Key,Pos}, _, _, _) ->
[] -> undefined;
[Found] ->
{ok, element(Pos, Found)}
end.
end;
try_alternative({reg, Prop, Mod, Fun}, App, Key, _) ->
Mod:Fun(App, Key, Prop).

os_env_key(Key) ->
string:to_upper(atom_to_list(Key)).
Expand All @@ -536,33 +556,52 @@ lookup_env(Scope, App, Key, P) ->
{ok, Value}
end.

cache_env(Scope, App, Key, Value) ->
maybe_cache(true, Alt, Caches) ->
[Alt|Caches];
maybe_cache(false, _, Caches) ->
Caches.

cache_env(Scope, App, Key, Value, Caches) ->
?CATCH_GPROC_ERROR(
reg1({p, Scope, {gproc_env, App, Key}}, Value),
[Scope,App,Key,Value]).
cache_env1(Scope, App, Key, Value, Caches),
[Scope,App,Key,Value, Caches]).

cache_env1(Scope, App, Key, Value, Caches) ->
reg1({p, Scope, {gproc_env, App, Key}}, Value),
set_strategy(Caches, App, Key, Value, Scope).

update_cached_env(Scope, App, Key, Value) ->
case lookup_env(Scope, App, Key, self()) of
undefined ->
cache_env(Scope, App, Key, Value);
cache_env1(Scope, App, Key, Value, []);
{ok, _} ->
set_value({p, Scope, {gproc_env, App, Key}}, Value)
end.

is_valid_set_strategy([os_env|T], Value) ->
is_string(Value) andalso is_valid_set_strategy(T, Value);
is_valid_set_strategy([{os_env, _}|T], Value) ->
is_string(Value) andalso is_valid_set_strategy(T, Value);
is_valid_set_strategy([app_env|T], Value) ->
is_valid_set_strategy(T, Value);
is_valid_set_strategy([{mnesia,_Type,_Oid,_Pos}|T], Value) ->
is_valid_set_strategy(T, Value);
is_valid_set_strategy([], _) ->
is_valid_set_strategy([os_env|T], Value, Scope) ->
is_string(Value) andalso is_valid_set_strategy(T, Value, Scope);
is_valid_set_strategy([{os_env, _}|T], Value, Scope) ->
is_string(Value) andalso is_valid_set_strategy(T, Value, Scope);
is_valid_set_strategy([app_env|T], Value, Scope) ->
is_valid_set_strategy(T, Value, Scope);
is_valid_set_strategy([{mnesia,_Type,_Oid,_Pos}|T], Value, Scope) ->
is_valid_set_strategy(T, Value, Scope);
is_valid_set_strategy([{reg, Property}|T], Value, Scope) ->
try get_value_shared({property, Scope, Property}) of
{Mod, Fun} when is_atom(Mod), is_atom(Fun) ->
is_valid_set_strategy(T, Value, Scope);
{Mod, Fun, cache} when is_atom(Mod), is_atom(Fun) ->
is_valid_set_strategy(T, Value, Scope);
_ -> false
catch
error:_ -> false
end;
is_valid_set_strategy([], _, _) ->
true;
is_valid_set_strategy(_, _) ->
is_valid_set_strategy(_, _, _) ->
false.

set_strategy([H|T], App, Key, Value) ->
set_strategy([H|T], App, Key, Value, Scope) ->
case H of
app_env ->
application:set_env(App, Key, Value);
Expand All @@ -586,10 +625,21 @@ set_strategy([H|T], App, Key, Value) ->
Old
end,
mnesia:write(setelement(Pos, Rec, Value))
end)
end);
{reg, Property} ->
case get_value_shared({p, Scope, Property}) of
{Mod, Fun} when is_atom(Mod), is_atom(Fun) ->
Mod:Fun(App, Key, Value, Property);
{Mod, Fun, cache} when is_atom(Mod), is_atom(Fun) ->
Mod:Fun(App, Key, Value, Property);
Other ->
?THROW_GPROC_ERROR({invalid_env_reg, [Property, Other]})
end;
{reg, Property, Mod, Fun} ->
Mod:Fun(App, Key, Value, Property)
end,
set_strategy(T, App, Key, Value);
set_strategy([], _, _, Value) ->
set_strategy(T, App, Key, Value, Scope);
set_strategy([], _, _, Value, _) ->
Value.

is_string(S) ->
Expand Down Expand Up @@ -1016,10 +1066,11 @@ reg_shared1({T,_,_} = Key) when T==a; T==p; T==c ->
%%
%% Shared resources are all unique. They remain until explicitly unregistered
%% (using {@link unreg_shared/1}). The types of shared resources currently
%% supported are `counter' and `aggregated counter'. In listings and query
%% results, shared resources appear as other similar resources, except that
%% `Pid == shared'. To wit, update_counter({c,l,myCounter}, shared, 1) would
%% increment the shared counter `myCounter' with 1, provided it exists.
%% supported are `property', `counter' and `aggregated counter'.
%% In listings and query results, shared resources appear as other similar
%% resources, except that `Pid == shared'. To wit,
%% update_counter({c,l,myCounter}, shared, 1) would increment the shared
%% counter `myCounter' with 1, provided it exists.
%%
%% A shared aggregated counter will track updates in exactly the same way as
%% an aggregated counter which is owned by a process.
Expand Down
30 changes: 30 additions & 0 deletions test/envtest.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-module(envtest).
-compile(export_all).


init() ->
ets:new(?MODULE, [public,named_table,ordered_set]),
gproc:reg_shared({p,l,getenv}, {?MODULE, get_env}),
gproc:reg_shared({p,l,getenvc}, {?MODULE, get_env, cache}),
gproc:reg_shared({p,l,setenv}, {?MODULE, set_env}).

write(App, Key, Value) ->
ets:insert(?MODULE, {{App,Key}, Value}).

get_env(App, Key, Prop) ->
io:fwrite("~p:get_env(~p, ~p, ~p)~n", [?MODULE, App, Key, Prop]),
case ets:lookup(?MODULE, {App,Key}) of
[{_, Value}] ->
{ok, Value};
_ ->
undefined
end.

get_env(App, Key, Value, Prop) ->
io:fwrite("~p:get_env(~p, ~p, ~p, ~p) (CACHE!)~n",
[?MODULE, App, Key, Value, Prop]),
write(App, Key, Value).

set_env(App, Key, Value, Prop) ->
io:fwrite("~p:set_env(~p, ~p, ~p, ~p)~n", [?MODULE, App, Key, Value, Prop]),
write(App, Key, Value).