Current section

Files

Jump to
kill_bill src kb_resource.erl
Raw

src/kb_resource.erl

%%
%% Copyright 2013 Joaquim Rocha
%%
%% 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(kb_resource).
-include("kill_bill.hrl").
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
%% ====================================================================
%% API functions
%% ====================================================================
-export([start_link/1, stop/1, get_resource/2, add_locale/3]).
start_link(Config) ->
gen_server:start_link(?MODULE, [Config], []).
stop(Server) ->
gen_server:cast(Server, {stop}).
get_resource(Server, Locales) ->
gen_server:call(Server, {resource, Locales}).
add_locale(Server, Locale, Resource) ->
gen_server:cast(Server, {add_locale, Locale, Resource}).
%% ====================================================================
%% Behavioural functions
%% ====================================================================
init([Config]) ->
{ok, load_resources(Config, dict:new())}.
handle_call({resource, Locales}, From, State) ->
run(Locales, From, State),
{noreply, State}.
handle_cast({add_locale, Locale, Resource}, State) ->
Store = dict:store(Locale, Resource, State),
{noreply, Store};
handle_cast({stop}, State) ->
{stop, normal, State}.
handle_info(Info, State) ->
error_logger:info_msg("handle_info(~p)\n", [Info]),
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% ====================================================================
%% Internal functions
%% ====================================================================
load_resources(Config, Store) ->
BaseName = proplists:get_value(base_name, Config, "message"),
FileExtension = proplists:get_value(file_extension, Config, ".txt"),
FileDirDefault = "./resource",
FileDir = case lists:keyfind(priv_dir, 1, Config) of
{_, App, Path} ->
case code:priv_dir(App) of
{error, bad_name} ->
error_logger:error_msg("~p: Can't resolve the priv_dir of application ~p\n", [?MODULE, App]),
FileDirDefault;
PrivDir -> PrivDir ++ "/" ++ Path
end;
false -> proplists:get_value(dir, Config, FileDirDefault)
end,
Extension = "." ++ kb_util:remove_if_starts_with(FileExtension, "."),
Wildcard = BaseName ++ "*" ++ Extension,
Files = filelib:wildcard(Wildcard, FileDir),
add_resources(FileDir, BaseName, Extension, Files, Store).
add_resources(_Dir, _Basename, _Extension, [], Store) -> Store;
add_resources(Dir, Basename, Extension, [Filename|Tail], Store) ->
FullName = filename:join(Dir, Filename),
case file:script(FullName) of
{ok, ValueList} ->
Locale = get_locale_from_filename(Basename, Extension, Filename),
NStore = dict:store(Locale, convert_to_dict(ValueList), Store),
add_resources(Dir, Basename, Extension, Tail, NStore);
{error, {Line, _Mod, _Term}} ->
error_logger:error_msg("KB: Error in line [~p] of file ~s\n", [Line, Filename]),
add_resources(Dir, Basename, Extension, Tail, Store);
{error, Reason} ->
error_logger:error_msg("KB: Error [~p] reading file ~s\n", [Reason, Filename]),
add_resources(Dir, Basename, Extension, Tail, Store)
end.
convert_to_dict(ValueList) ->
Dict = dict:new(),
load_dict(ValueList, Dict).
load_dict([], Dict) -> Dict;
load_dict([{Key, Value}|Tail], Dict) ->
load_dict(Tail, dict:store(Key, Value, Dict)).
get_locale_from_filename(Basename, Extension, Filename) ->
NoBasename = string:substr(Filename, string:len(Basename) + 1),
NoExtension = string:substr(NoBasename, 1, string:len(NoBasename) - string:len(Extension)),
case NoExtension of
[] -> ?ANY_LOCALE;
_ ->
CleanLocation = kb_util:remove_if_starts_with(NoExtension, "_"),
case string:str(CleanLocation, "_") of
0 -> {list_to_binary(CleanLocation), ?NO_COUNTRY_IN_LOCALE};
Pos ->
L = string:substr(CleanLocation, 1, Pos - 1),
C = string:substr(CleanLocation, Pos + 1),
{list_to_binary(L), list_to_binary(string:to_upper(C))}
end
end.
run(Locales, From, Store) ->
Server = self(),
Fun = fun() ->
Reply = case dict:size(Store) of
0 -> dict:new();
1 -> find(Server, ?ANY_LOCALE, Store);
_ -> find(Server, Locales, Store)
end,
gen_server:reply(From, Reply)
end,
spawn(Fun).
find(_Server, ?ANY_LOCALE, Store) ->
case dict:find(?ANY_LOCALE, Store) of
{ok, Value} -> Value;
error -> dict:new()
end;
find(Server, [], Store) -> find(Server, ?ANY_LOCALE, Store);
find(Server, [Locale|Tail], Store) ->
case dict:find(Locale, Store) of
{ok, Value} -> Value;
error ->
{L, _} = Locale,
case dict:find({L, ?NO_COUNTRY_IN_LOCALE}, Store) of
{ok, Value} ->
kb_resource:add_locale(Server, Locale, Value),
Value;
error ->
find(Server, Tail, Store)
end
end.