Current section

Files

Jump to
cuttlefish src cuttlefish_translation.erl
Raw

src/cuttlefish_translation.erl

%% -------------------------------------------------------------------
%%
%% cuttlefish_translation: models a cuttlefish translation
%%
%% Copyright (c) 2013 Basho Technologies, Inc. All Rights Reserved.
%%
%% This file is provided to you 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(cuttlefish_translation).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
-record(translation, {
mapping::string(),
func::fun() | undefined
}).
-type translation() :: #translation{}.
-type translation_fun() :: fun(([proplists:property()]) -> any()).
-type raw_translation() :: {translation, string(), translation_fun()} | {translation, string()}.
-export_type([translation/0]).
-export([
parse/1,
parse_and_merge/2,
is_translation/1,
mapping/1,
func/1,
replace/2]).
-spec parse(raw_translation()) -> translation() | cuttlefish_error:error().
parse({translation, Mapping}) ->
#translation{
mapping = Mapping
};
parse({translation, Mapping, Fun}) ->
#translation{
mapping = Mapping,
func = Fun
};
parse(X) ->
{error, {translation_parse, X}}.
%% This assumes it's run as part of a foldl over new schema elements
%% in which case, there's only ever one instance of a key in the list
%% so keyreplace works fine.
-spec parse_and_merge(
raw_translation(), [translation()]) -> [translation()].
parse_and_merge({translation, Mapping}, Translations) ->
lists:keydelete(Mapping, #translation.mapping, Translations);
parse_and_merge({translation, Mapping, _} = TranslationSource, Translations) ->
NewTranslation = parse(TranslationSource),
case lists:keyfind(Mapping, #translation.mapping, Translations) of
false ->
[ NewTranslation | Translations];
_OldMapping ->
lists:keyreplace(Mapping, #translation.mapping, Translations, NewTranslation)
end.
-spec is_translation(any()) -> boolean().
is_translation(T) -> is_tuple(T) andalso element(1, T) =:= translation.
-spec mapping(translation()) -> string().
mapping(T) -> T#translation.mapping.
-spec func(translation()) -> fun().
func(T) -> T#translation.func.
-spec replace(translation(), [translation()]) -> [translation()].
replace(Translation, ListOfTranslations) ->
Exists = lists:keymember(mapping(Translation), #translation.mapping, ListOfTranslations),
case Exists of
true ->
lists:keyreplace(mapping(Translation), #translation.mapping, ListOfTranslations, Translation);
_ ->
[Translation | ListOfTranslations]
end.
-ifdef(TEST).
-define(XLATE(X), lists:flatten(cuttlefish_error:xlate(X))).
parse_test() ->
TranslationDataStruct = {
translation,
"mapping",
fun(X) -> X*2 end
},
Translation = parse(TranslationDataStruct),
?assertEqual("mapping", Translation#translation.mapping),
F = Translation#translation.func,
?assertEqual(4, F(2)),
ok.
getter_test() ->
Translation = #translation{
mapping = "mapping",
func = fun(X) -> X*2 end
},
?assertEqual("mapping", mapping(Translation)),
Fun = func(Translation),
?assertEqual(4, Fun(2)),
ok.
replace_test() ->
Element1 = #translation{
mapping = "mapping18",
func = fun(X) -> X*2 end
},
?assertEqual(4, (Element1#translation.func)(2)),
Element2 = #translation{
mapping = "mapping1",
func = fun(X) -> X*4 end
},
?assertEqual(8, (Element2#translation.func)(2)),
SampleTranslations = [Element1, Element2],
Override = #translation{
mapping = "mapping1",
func = fun(X) -> X*5 end
},
?assertEqual(25, (Override#translation.func)(5)),
NewTranslations = replace(Override, SampleTranslations),
?assertEqual([Element1, Override], NewTranslations),
ok.
parse_and_merge_test() ->
Sample1 = #translation{
mapping = "mapping1",
func = fun(X) -> X*3 end
},
?assertEqual(6, (Sample1#translation.func)(2)),
Sample2 = #translation{
mapping = "mapping2",
func = fun(X) -> X*4 end
},
?assertEqual(8, (Sample2#translation.func)(2)),
SampleTranslations = [Sample1, Sample2],
NewTranslations = parse_and_merge(
{translation, "mapping1", fun(X) -> X * 10 end},
SampleTranslations),
F = func(hd(NewTranslations)),
?assertEqual(50, F(5)),
ok.
parse_error_test() ->
{ErrorAtom, ErrorTuple} = parse(not_a_raw_translation),
?assertEqual(error, ErrorAtom),
?assertEqual(
"Poorly formatted input to cuttlefish_translation:parse/1 : not_a_raw_translation",
?XLATE(ErrorTuple)),
ok.
parse_empty_test() ->
TranslationDataStruct = {
translation,
"mapping"
},
Translation = parse(TranslationDataStruct),
?assertEqual("mapping", Translation#translation.mapping),
F = Translation#translation.func,
?assertEqual(undefined, F),
ok.
parse_and_merge_empty_test() ->
Sample1 = #translation{
mapping = "mapping1",
func = fun(X) -> X*3 end
},
?assertEqual(6, (Sample1#translation.func)(2)),
Sample2 = #translation{
mapping = "mapping2",
func = fun(X) -> X*4 end
},
?assertEqual(8, (Sample2#translation.func)(2)),
SampleTranslations = [Sample1, Sample2],
NewTranslations = parse_and_merge(
{translation, "mapping1"},
SampleTranslations),
F = func(hd(NewTranslations)),
?assertEqual(1, length(NewTranslations)),
?assertEqual(40, F(10)),
ok.
is_translation_test() ->
?assert(not(is_translation(not_a_translation))),
T = #translation{
mapping = "mapping1",
func = fun(X) -> X*3 end
},
?assertEqual(6, (T#translation.func)(2)),
?assert(is_translation(T)),
ok.
-endif.