%%% BEGIN openflax/conf.erl %%% %%% %%% openflax - Open Source web server for Erlang/OTP %%% Copyright (c)2004 Cat's Eye Technologies. All rights reserved. %%% %%% Redistribution and use in source and binary forms, with or without %%% modification, are permitted provided that the following conditions %%% are met: %%% %%% Redistributions of source code must retain the above copyright %%% notice, this list of conditions and the following disclaimer. %%% %%% Redistributions in binary form must reproduce the above copyright %%% notice, this list of conditions and the following disclaimer in %%% the documentation and/or other materials provided with the %%% distribution. %%% %%% Neither the name of Cat's Eye Technologies nor the names of its %%% contributors may be used to endorse or promote products derived %%% from this software without specific prior written permission. %%% %%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND %%% CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, %%% INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF %%% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE %%% DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE %%% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, %%% OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, %%% PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, %%% OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON %%% ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, %%% OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY %%% OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE %%% POSSIBILITY OF SUCH DAMAGE. %% @doc OpenFlax configuration datatype module. %% %%
This module defines an abstract data type conf()
%% which represents a set of configuration settings.
conf()s are immutable. You must create a
%% new conf() from an existing one; you cannot change
%% it.
conf().
new() ->
new([]).
%% @spec new([{key(), value()}]) -> conf()
%% key() = atom()
%% value() = term()
%% @doc Creates a new conf() from a list of key-value pairs.
new(List) ->
dict:from_list(List).
%% @spec get_string(key(), conf()) -> string()
%% @doc Retrieves a string from a conf(). The returned value
%% is always converted to a flattened string.
get_string(Key, Conf) ->
case dict:find(Key, Conf) of
{ok, Value} ->
lists:flatten(openflax.string:from_term(Value));
error ->
""
end.
%% @spec get_value(key(), conf()) -> {ok, term()} | error
%% @doc Retrieves a value from a conf().
get_value(cfg_openflax_version, _) ->
{ok, openflax.app:version()};
get_value(Key, Conf) ->
dict:find(Key, Conf).
%% @spec put_value(key(), value(), conf()) -> conf()
%% @doc Updates a value in a conf(). Returns a new
%% conf() with the specified key representing a new value.
put_value(Key, Value, Conf) ->
dict:store(Key, Value, Conf).
%% @spec delete_value(key(), conf()) -> conf()
%% @doc Deletes a value from a conf().
%% Returns a new conf().
delete_value(Key, Conf) ->
dict:erase(Key, Conf).
%% @spec dump(conf()) -> [{key(), value()}]
%% @doc Retrieves all values from a conf() as a list of
%% key-value pairs.
dump(Conf) ->
dict:to_list(Conf).
%% @spec map(fun(), conf()) -> conf()
%% @doc Creates a new conf() by applying a fun/2 (Key, Value)
%% to each key-value pair in the conf(). The return value of the
%% fun is a new value to use for the key in the new conf().
map(Fun, Conf) ->
dict:map(Fun, Conf).
%% @spec fold(fun(), term(), conf()) -> term()
%% @doc Creates a new term by applying a fun/3 (Key, Value, Acc)
%% to each key-value pair in the conf(). The return value of
%% the fun is a new accumulator.
fold(Fun, Acc, Conf) ->
dict:fold(Fun, Acc, Conf).
%% @spec merge(A::conf(), B::conf()) -> conf()
%% @doc Creates a new conf() by merging two conf()s.
%% Mostly equivalent to merge(fun(K, V1, V2) -> V2 end, A, B),
%% except that cumulative settings are not wholly overridden, but rather,
%% accumulated.
merge(ConfA, ConfB) ->
merge(fun
(cfg_openflax_dispatch, V1, V2) -> % cumulative, so merge lists
V1 ++ V2;
(cfg_openflax_watchers, V1, V2) -> % cumulative, so merge lists
V1 ++ V2;
(_, _V1, V2) -> % otherwise, override
V2
end, ConfA, ConfB).
%% @spec merge(fun(), A::conf(), B::conf()) -> conf()
%% @doc Creates a new conf() by merging two conf()s
%% and applying a fun/3 (Key, ValueA, ValueB) when there
%% are colliding keys. The return value of the fun is
%% the value which will be used in the new conf().
merge(Fun, ConfA, ConfB) ->
dict:merge(Fun, ConfA, ConfB).
%% @spec load(ConfigModuleNames::[modulename()]) -> dict()
%% modulename() = atom()
%% @doc The configuration settings are loaded from the named config modules.
%% They are returned in a 'master dictionary' where the keys
%% are atoms (section names) and the values are conf()s.
load(ConfigModuleNames) ->
lists:foldl(fun(ConfigModuleName, Dict) ->
ConfigModule = case ConfigModuleName of
S when is_list(S) -> list_to_atom(S);
S -> S
end,
lists:foldl(fun({Section, ConfList}, Acc) ->
case dict:find(Section, Acc) of
{ok, ExtantConf} ->
dict:store(Section, merge(ExtantConf, new(ConfList)), Acc);
error ->
dict:store(Section, new(ConfList), Acc)
end
end, Dict, ConfigModule:conf())
end, dict:new(), ConfigModuleNames).
%%% END of openflax/conf.erl %%%