Packages

Write once global flags for Erlang and Elixir

Current section

Files

Jump to
global_flags src global_flags.erl
Raw

src/global_flags.erl

%% @doc Write once global flags for Erlang and Elixir.
%%
%% This module provides a very simple API for managing global
%% flags and conditional execution based on those flags. This
%% is designed for setups where you need quick flag checks
%% before execution, but don't want to waste a process, start
%% an ETS table, and cannot rely on the process dictionary.
%%
%% It works by using a super simple atom table check, which
%% makes the check almost instant (and without having to rely
%% on any prior state).
%%
%% Flags cannot be unset after being set, due to the inability
%% to purge from the atom table. If you want such ability, you
%% likely want to deal with ETS or some other storage.
-module(global_flags).
-compile(inline).
%% Public API
-export([is_set/1, once/2, set/1, with/2, without/2]).
%% ===================================================================
%% Public API
%% ===================================================================
%% @doc Checks if a global flag is set.
-spec is_set(binary() | list()) -> boolean().
is_set(Flag) when is_binary(Flag) ->
try binary_to_existing_atom(Flag, utf8) of
_ -> true
catch
_:_ -> false
end;
is_set(Flag) when is_list(Flag) ->
try list_to_existing_atom(Flag) of
_ -> true
catch
_:_ -> false
end.
%% @doc Runs a function a single time, based on the provided flag.
-spec once(binary() | list(), fun(() -> any())) -> any | {error, flag_state}.
once(Flag, Fun) when
is_binary(Flag); is_list(Flag),
is_function(Fun, 0)
->
case is_set(Flag) of
true -> {error, flag_state};
false ->
Result = Fun(),
set(Flag),
Result
end.
%% @doc Sets a global flag, typically only used internally.
-spec set(binary() | list()) -> ok.
set(Flag) when is_binary(Flag) ->
_ = binary_to_atom(Flag, utf8),
ok;
set(Flag) when is_list(Flag) ->
_ = list_to_atom(Flag),
ok.
%% @doc Runs a function only if the provided flag is set.
-spec with(binary() | list(), fun(() -> any())) -> any | {error, flag_state}.
with(Flag, Fun) when
is_binary(Flag); is_list(Flag),
is_function(Fun, 0)
->
case is_set(Flag) of
false -> {error, flag_state};
true -> Fun()
end.
%% @doc Runs a function only if the provided flag is not set.
-spec without(binary() | list(), fun(() -> any())) -> any | {error, flag_state}.
without(Flag, Fun) when
is_binary(Flag); is_list(Flag),
is_function(Fun, 0)
->
case is_set(Flag) of
true -> {error, flag_state};
false -> Fun()
end.
%% ===================================================================
%% Private test cases
%% ===================================================================
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
set_test() ->
Flag = "test_set",
?assertNot(global_flags:is_set(Flag)),
?assert(global_flags:set(Flag) =:= ok),
?assert(global_flags:is_set(Flag)),
ok.
once_test() ->
Flag = "test_once",
Once1 = global_flags:once(Flag, fun() ->
woohoo
end),
Once2 = global_flags:once(Flag, fun() ->
woohoo
end),
?assert(Once1 =:= woohoo),
?assert(Once2 =:= {error, flag_state}).
with_test() ->
Flag = "test_with",
Handle = fun() -> woohoo end,
With1 = global_flags:with(Flag, Handle),
global_flags:set(Flag),
With2 = global_flags:with(Flag, Handle),
?assert(With1 =:= {error, flag_state}),
?assert(With2 =:= woohoo).
without_test() ->
Flag = "test_without",
Handle = fun() -> woohoo end,
Without1 = global_flags:without(Flag, Handle),
global_flags:set(Flag),
Without2 = global_flags:without(Flag, Handle),
?assert(Without1 =:= woohoo),
?assert(Without2 =:= {error, flag_state}).
-endif.