Commit d795d53d authored by Philipp Huebner's avatar Philipp Huebner

New upstream version 1.0.9

parent 5ea2be39
# Version 1.0.9
* Add p1_options module
# Version 1.0.8
* Add p1_queue
......
%%%-------------------------------------------------------------------
%%% @author Evgeny Khramtsov <ekhramtsov@process-one.net>
%%% Created : 24 Apr 2017 by Evgeny Khramtsov <ekhramtsov@process-one.net>
%%%
%%%
%%% Copyright (C) 2002-2017 ProcessOne, SARL. All Rights Reserved.
%%%
%%% Licensed under the Apache License, Version 2.0 (the "License");
%%% you may not use this file except in compliance with the License.
%%% You may obtain a copy of the License at
%%%
%%% http://www.apache.org/licenses/LICENSE-2.0
%%%
%%% Unless required by applicable law or agreed to in writing, software
%%% distributed under the License is distributed on an "AS IS" BASIS,
%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%%% See the License for the specific language governing permissions and
%%% limitations under the License.
%%%
%%%-------------------------------------------------------------------
-module(p1_options).
-behaviour(gen_server).
%% API
-export([start/1, start_link/1, insert/4, delete/3, lookup/3, clear/1,
compile/1]).
%% For debug only
-export([dump/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-type scope() :: global | any().
-record(state, {tab :: atom()}).
%%%===================================================================
%%% API
%%%===================================================================
-spec start(atom()) -> ok | {error, already_started | any()}.
start(Tab) ->
case whereis(Tab) of
undefined ->
application:ensure_all_started(p1_utils),
Spec = {?MODULE, {?MODULE, start_link, [Tab]},
permanent, 5000, worker, [?MODULE]},
case supervisor:start_child(p1_utils_sup, Spec) of
{ok, _} -> ok;
{error, {already_started, _}} -> {error, already_started};
{error, _} = Err -> Err
end;
_ ->
{error, already_started}
end.
-spec start_link(atom()) -> {ok, pid()} | {error, any()}.
start_link(Tab) ->
gen_server:start_link({local, Tab}, ?MODULE, [Tab], []).
-spec insert(atom(), atom(), scope(), any()) -> ok.
insert(Tab, Opt, Scope, Val) ->
ets:insert(Tab, {{Opt, Scope}, Val}),
ok.
-spec delete(atom(), atom(), scope()) -> ok.
delete(Tab, Opt, Scope) ->
ets:delete(Tab, {Opt, Scope}),
ok.
-spec lookup(atom(), atom(), scope()) -> {ok, any()} | undefined.
lookup(Tab, Opt, Scope) ->
case ets:lookup(Tab, {Opt, Scope}) of
[] -> undefined;
[{_, Val}] -> {ok, Val}
end.
-spec clear(atom()) -> ok.
clear(Tab) ->
ets:delete_all_objects(Tab),
ok.
-spec compile(atom()) -> ok.
compile(Tab) ->
case gen_server:call(Tab, compile, timer:minutes(1)) of
ok -> ok;
{error, Reason} ->
error_logger:error_msg(
"Failed to compile configuration for ~p: ~s",
[format_error(Reason)]),
erlang:error({compile_failed, Tab})
end.
-spec dump(atom()) -> ok.
dump(Mod) ->
Exprs = get_exprs(Mod),
File = filename:join("/tmp", atom_to_list(Mod) ++ ".erl"),
case file:write_file(File, string:join(Exprs, io_lib:nl())) of
ok ->
erl_tidy:file(File, [{backups, false}]),
io:format("Dynamic module '~s' is written to ~s~n", [Mod, File]);
{error, Reason} ->
io:format("Failed to dump dynamic module '~s' to ~s: ~s~n",
[Mod, File, file:format_error(Reason)])
end.
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([Tab]) ->
catch ets:new(Tab, [named_table, public, {read_concurrency, true}]),
{ok, #state{tab = Tab}}.
handle_call(compile, From, #state{tab = Tab} = State) ->
do_compile(Tab, [From]),
{noreply, State};
handle_call(_Request, _From, State) ->
{noreply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
-spec do_compile(atom(), list()) -> ok.
do_compile(Tab, Callers) ->
receive
{'gen_call', Caller, compile} ->
do_compile(Tab, [Caller|Callers])
after 0 ->
Exprs = get_exprs(Tab),
Result = compile_exprs(Tab, Exprs),
lists:foreach(
fun(Caller) ->
gen_server:reply(Caller, Result)
end, lists:reverse(Callers))
end.
-spec get_exprs(atom()) -> [string()].
get_exprs(Mod) ->
OptMap = ets:foldl(
fun({{Opt, Scope}, Val}, Acc) ->
Vals = maps:get(Opt, Acc, []),
maps:put(Opt, [{Scope, Val}|Vals], Acc)
end, #{}, Mod),
Opts = maps:fold(
fun(Opt, Vals, Acc) ->
Default = case lists:keyfind(global, 1, Vals) of
{_, V} -> {ok, V};
false -> undefined
end,
[lists:flatmap(
fun({Scope, Val}) when {ok, Val} /= Default ->
io_lib:format(
"~p(~p) -> {ok, ~p};~n", [Opt, Scope, Val]);
(_) ->
""
end, Vals) ++ io_lib:format("~p(_) -> ~p.", [Opt, Default])
|Acc]
end, [], OptMap),
Known = maps:fold(
fun(Opt, _, Acc) ->
io_lib:format(
"is_known(~p) -> true;~n", [Opt]) ++ Acc
end, "", OptMap) ++ "is_known(_) -> false.",
Scopes = maps:fold(
fun(Opt, Vals, Acc) ->
io_lib:format(
"get_scope(~p) -> ~p;~n",
[Opt, [Scope || {Scope, _} <- Vals]]) ++ Acc
end, "", OptMap) ++ "get_scope(_) -> [].",
[io_lib:format("-module(~p).", [Mod]),
"-compile(export_all).",
Known, Scopes | Opts].
-spec compile_exprs(module(), [string()]) -> ok | {error, any()}.
compile_exprs(Mod, Exprs) ->
try
Forms = lists:map(
fun(Expr) ->
{ok, Tokens, _} = erl_scan:string(lists:flatten(Expr)),
{ok, Form} = erl_parse:parse_form(Tokens),
Form
end, Exprs),
{ok, Code} = case compile:forms(Forms, []) of
{ok, Mod, Bin} -> {ok, Bin};
{ok, Mod, Bin, _Warnings} -> {ok, Bin};
Error -> Error
end,
{module, Mod} = code:load_binary(Mod, "nofile", Code),
ok
catch _:{badmatch, {error, ErrInfo, _ErrLocation}} ->
{error, ErrInfo};
_:{badmatch, {error, _} = Err} ->
Err;
_:{badmatch, error} ->
{error, compile_failed}
end.
format_error({_Line, _Mod, _Term} = Reason) ->
"Syntax error at line " ++ file:format_error(Reason);
format_error(Reason) ->
atom_to_list(Reason).
{application, p1_utils,
[
{description, "Erlang utility modules from ProcessOne"},
{vsn, "1.0.8"},
{vsn, "1.0.9"},
{modules, []},
{registered, []},
{applications, [
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment