%%% @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 Var if exists; otherwise return Def. %% @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 App 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 App environment vars from file with name File located in app priv_dir. %% @end -spec load_config(atom(), string()) -> ok. load_config(App, File) -> load_config(App, File, []). %% @doc %% Set application App environment vars from file with name File 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.