Packages

Erlang common utils

Current section

Files

Jump to
ulitos src ulitos_app.erl
Raw

src/ulitos_app.erl

%%% @doc Utils function for work with applications
%%% @end
-module(ulitos_app).
-author("palkan").
%% API
-export([
ensure_started/1,
ensure_loaded/1,
stop_apps/1,
reload/1,
reload_modules/1,
get_var/3,
get_var/2,
set_var/3,
load_config/1,
load_config/2,
load_config/3
]).
%% @doc
%% Tries to start applications or check whether they have started.
%% @end
-spec ensure_started(list(atom()) | atom()) -> ok | error.
ensure_started([]) -> ok;
ensure_started(App) when is_atom(App) -> ensure_started([App]);
ensure_started([App | Apps]) ->
case application:start(App) of
ok -> ensure_started(Apps);
{error, {already_started, App}} -> ensure_started(Apps);
{error, {not_started, Dep}} -> ensure_started(Dep), ensure_started([App | Apps])
end.
%% @doc
%% Tries to load modules.
%% @end
-spec ensure_loaded(list(atom()) | atom()) -> ok | error.
ensure_loaded([]) -> ok;
ensure_loaded(Mod) when is_atom(Mod) -> ensure_started([Mod]);
ensure_loaded([Mod | Mods]) ->
{module, _} = code:ensure_loaded(Mod),
ensure_loaded(Mods).
%% @doc
%% Stop applications.
%% @end
-spec stop_apps(list(atom())|atom()) -> ok.
stop_apps([]) -> ok;
stop_apps(App) when is_atom(App) -> stop_apps([App]);
stop_apps([App | Apps]) ->
application:stop(App),
stop_apps(Apps).
%% @doc Compiles and reloads application modules
%% @end
-spec reload(atom()) -> ok.
reload(App) ->
application:load(App),
case application:get_key(App, modules) of
undefined ->
ok;
{ok, Modules} ->
reload_modules(lists:usort(Modules))
end.
%% @doc Reload specified modules
%% @end
-spec reload_modules(list(atom()) | atom()) -> ok.
reload_modules([]) -> ok;
reload_modules([?MODULE | T]) -> reload_modules(T);
reload_modules(M) when is_atom(M) -> reload_modules([M]);
reload_modules([H|T]) ->
reload_mod(H),
reload_modules(T).
%% @private
-spec reload_mod(atom()) -> true.
reload_mod(Module) when is_atom(Module) ->
error_logger:info_msg("[ulitos] Reload module: ~p", [Module]),
code:purge(Module),
code:load_file(Module),
true.
%% @doc
%% Return application environment variable called <code>Var</code> if exists; otherwise return <code>Def</code>.
%% @end
-spec get_var(atom(), atom(), any()) -> any().
get_var(App, Var, Def) ->
case application:get_env(App, Var) of
{ok, Val} -> Val;
_ -> Def
end.
-spec get_var(atom(), atom()) -> any() | undefined.
get_var(App, Var) ->
get_var(App, Var, undefined).
%% @doc
%% Set application environment variable.
%% @end
-spec set_var(atom(), atom(), any()) -> ok.
set_var(App, Var, Val) ->
application:set_env(App, Var, Val).
%% @doc
%% Set application <code>App</code> environment from default config ("priv/APP_NAME.config").
%% @end
-spec load_config(atom()) -> ok.
load_config(App) ->
File = atom_to_list(App)++".config",
load_config(App, File).
%% @doc
%% Set application <code>App</code> environment vars from file with name <code>File</code> located in app priv_dir.
%% @end
-spec load_config(atom(), string()) -> ok.
load_config(App, File) ->
load_config(App, File, []).
%% @doc
%% Set application <code>App</code> environment vars from file with name <code>File</code> located in some of Dirs or in priv_dir.
%% @end
-spec load_config(atom(), string(), list()) -> ok.
load_config(App, File, Dirs) ->
Path = code:priv_dir(App),
Env = load_file_config(Dirs ++ [Path], File),
[application:set_env(App, Key, Value) || {Key, Value} <- Env],
ok.
%% @private
-spec load_file_config(Paths::list(string()), File::string()) -> any().
load_file_config(Paths, File) ->
case file:path_consult(Paths, File) of
{ok, Env, _Path} -> Env;
_ -> []
end.
%% Tests
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-define(PATH(File), filename:join([code:lib_dir(ulitos), File])).
load_config_test_() ->
{"List dir tests",
{foreach,
fun config_setup/0,
fun config_cleanup/1,
[
fun load_config_by_app_t_/1,
fun load_config_by_name_t_/1,
fun load_config_by_name_and_paths_t_/1,
fun load_config_by_name_and_paths_2_t_/1
]
}
}.
unconsult(File, L) ->
{ok, S} = file:open(File, write),
lists:foreach( fun(X) -> io:format(S, "~p.~n",[X]) end, L),
file:close(S).
config_setup() ->
unconsult(?PATH("priv/ulitos.config"), [{test, 1}]),
ok = file:make_dir(?PATH("priv/a")),
unconsult(?PATH("priv/a/ulitos.config"), [{test, 2}]),
unconsult(?PATH("priv/ul.config"), [{test, 3}]),
unconsult(?PATH("priv/a/ul.config"), [{test, 4}]),
ok.
config_cleanup(_) ->
ulitos_file:recursively_del_dir(?PATH("priv/a")),
file:delete(?PATH("priv/ulitos.config")),
file:delete(?PATH("priv/ul.config")),
application:unset_env(ulitos, test),
ok.
load_config_by_app_t_(_)->
load_config(ulitos),
[
?_assertEqual(1, get_var(ulitos, test))
].
load_config_by_name_t_(_)->
load_config(ulitos, "ul.config"),
[
?_assertEqual(3, get_var(ulitos, test))
].
load_config_by_name_and_paths_t_(_)->
load_config(ulitos, "ul.config", [?PATH("priv/b"), ?PATH("priv/a")]),
[
?_assertEqual(4, get_var(ulitos, test))
].
load_config_by_name_and_paths_2_t_(_)->
load_config(ulitos, "ul.config", [?PATH("priv/b"), ?PATH("c")]),
[
?_assertEqual(3, get_var(ulitos, test))
].
reload_modules_test_() ->
{"Reload module code",
{foreach,
fun reload_setup/0,
fun reload_cleanup/1,
[
fun reload_modules_t_/1
]
}
}.
reload_setup() ->
code:add_patha(?PATH("")),
ok = file:write_file(?PATH("my_module.erl"), <<"-module(my_module).\n-export([say/0]).\nsay() -> hello.">>),
{ok, _} = compile:file(?PATH("my_module.erl"), {outdir, ?PATH("")}),
{module, my_module} = code:load_file(my_module),
ok.
reload_cleanup(_) ->
ok.
reload_modules_t_(_)->
Before = my_module:say(),
ok = file:write_file(?PATH("my_module.erl"), <<"-module(my_module).\n-export([say/0]).\nsay() -> hi.">>),
{ok, _} = compile:file(?PATH("my_module.erl"), {outdir, ?PATH("")}),
reload_modules(my_module),
After = my_module:say(),
[
?_assertEqual(hello, Before),
?_assertEqual(hi, After)
].
-endif.