From b3a34f16dd3e2fbfc04f93434b54a4e7d950891f Mon Sep 17 00:00:00 2001 From: Richard Jonas Date: Fri, 31 Jan 2014 17:21:44 +0100 Subject: [PATCH] rec_fun, field_fun and const added --- src/ejson.erl | 52 +++++++++++++++++++----------- test/ejson_basic_test.erl | 52 +++++++++++++++++------------- test/ejson_list_test.erl | 58 ++++++++++++++++++++++++++++------ test/ejson_preprocess_test.erl | 44 ++++++++++++++++++++++++++ test/ejson_skip_test.erl | 18 +++++++++++ 5 files changed, 175 insertions(+), 49 deletions(-) create mode 100644 test/ejson_preprocess_test.erl create mode 100644 test/ejson_skip_test.erl diff --git a/src/ejson.erl b/src/ejson.erl index c6604ac..ac86ee0 100644 --- a/src/ejson.erl +++ b/src/ejson.erl @@ -78,19 +78,13 @@ conv(Tuple, Options) when is_tuple(Tuple) -> %% Convert each values lists:reverse( lists:foldl( - fun({{list, Name}, Value}, Acc) -> - [{list_to_binary(Name), conv_list(Value, Options)} | Acc]; - ({{proplist, Name}, Value}, Acc) -> - [{list_to_binary(Name), conv_proplist(Value, Options)} | Acc]; - ({skip, _Value}, Acc) -> - Acc; - ({{pre, Name, {Mod, Fun}}, _Value}, Acc) -> - Value = erlang:apply(Mod, Fun, [Tuple]), - [{list_to_binary(Name), conv(Value, Options)} | Acc]; - ({{const, Name, Value}, _ReplacedValue}, Acc) -> - [{list_to_binary(Name), conv(Value, Options)} | Acc]; - ({Name, Value}, Acc) -> - [{list_to_binary(Name), conv(Value, Options)} | Acc] + fun({Rule, Value}, Acc) -> + case apply_rule(Rule, Tuple, Value, Options) of + {AttrName, AttrValue} -> + [{list_to_binary(AttrName), AttrValue} | Acc]; + undefined -> + Acc + end end, [], zip(FieldNames, Vals))); @@ -110,9 +104,29 @@ conv(Pid, _) when is_pid(Pid) -> list_to_binary(pid_to_list(Pid)). -conv_list(List, Options) -> - [conv(L, Options) || L <- List]. - +conv_list(List, Options) when is_list(List) -> + [conv(L, Options) || L <- List]; +conv_list(List, _Options) -> + throw({error, {not_a_list, List}}). + +apply_rule({list, Name}, _Record, Value, Options) -> + List = conv_list(Value, Options), + {Name, List}; +apply_rule({proplist, Name}, _Record, Value, Options) -> + PropList = conv_proplist(Value, Options), + {Name, PropList}; +apply_rule(skip, _Record, _Value, _Options) -> + undefined; +apply_rule({field_fun, Name, {M, F}}, _Record, Value, Options) -> + Value2 = erlang:apply(M, F, [Value]), + {Name, conv(Value2, Options)}; +apply_rule({rec_fun, Name, {M, F}}, Record, _Value, Options) -> + Value2 = erlang:apply(M, F, [Record]), + {Name, conv(Value2, Options)}; +apply_rule({const, Name, Const}, _Record, _Value, Options) -> + {Name, conv(Const, Options)}; +apply_rule(Name, _Record, Value, Options) -> + {Name, conv(Value, Options)}. %%----------------------------------------------------------------------------- %% @doc @@ -122,7 +136,7 @@ conv_list(List, Options) -> %% each key, convert the atom key into camel-cased name. %% @end %%----------------------------------------------------------------------------- -conv_proplist(List, Options) -> +conv_proplist(List, Options) when is_list(List) -> Keys = proplists:get_keys(List), lists:map( fun(Key) -> @@ -133,7 +147,9 @@ conv_proplist(List, Options) -> {camel_case(Key), conv_list(Vals, Options)} end end, - Keys). + Keys); +conv_proplist(List, _Options) -> + throw({error, {not_a_proplist, List}}). camel_case(Atom) -> list_to_binary(lists:reverse(camel_case(atom_to_list(Atom), []))). diff --git a/test/ejson_basic_test.erl b/test/ejson_basic_test.erl index 8202542..3ae080b 100644 --- a/test/ejson_basic_test.erl +++ b/test/ejson_basic_test.erl @@ -2,63 +2,71 @@ -ifdef(TEST). +-import(ejson, [camel_case/1, conv/2, json_prop/2, zip/2]). + -include_lib("eunit/include/eunit.hrl"). zip_test() -> - ?assertEqual([{undefined, 1}], ejson:zip([], [1])), - ?assertEqual([{2, 1}, {3, undefined}], ejson:zip([2, 3], [1])), - ?assertEqual([], ejson:zip([], [])), - ?assertEqual([{1, 2}, {3, 4}], ejson:zip([1, 3], [2, 4])). + ?assertEqual([{undefined, 1}], zip([], [1])), + ?assertEqual([{2, 1}, {3, undefined}], zip([2, 3], [1])), + ?assertEqual([], zip([], [])), + ?assertEqual([{1, 2}, {3, 4}], zip([1, 3], [2, 4])). camel_case_test() -> - ?assertEqual(<<"simple">>, ejson:camel_case(simple)), - ?assertEqual(<<"xAxis">>, ejson:camel_case(x_axis)). + ?assertEqual(<<"simple">>, camel_case(simple)), + ?assertEqual(<<"xAxis">>, camel_case(x_axis)). conv_atom_test() -> Options = [{weather, ["month", "value"]}], - J = ejson:conv({weather, january, snowing}, Options), + J = conv({weather, january, snowing}, Options), - ?assertEqual(<<"january">>, ejson:json_prop(J, "month")), - ?assertEqual(<<"snowing">>, ejson:json_prop(J, "value")). + ?assertEqual(<<"january">>, json_prop(J, "month")), + ?assertEqual(<<"snowing">>, json_prop(J, "value")). conv_pid_test() -> Options = [{procs, ["sup"]}], Bin = list_to_binary(pid_to_list(self())), - J = ejson:conv({procs, self()}, Options), + J = conv({procs, self()}, Options), + + ?assertEqual(Bin, json_prop(J, "sup")). + +undef_test() -> + Options = [{nullable, ["object"]}], - ?assertEqual(Bin, ejson:json_prop(J, "sup")). + J = conv({nullable, undefined}, Options), + ?assertEqual(null, json_prop(J, "object")). bool_test() -> Options = [{bool, ["value"]}], - J1 = ejson:conv({bool, true}, Options), - J2 = ejson:conv({bool, false}, Options), + J1 = conv({bool, true}, Options), + J2 = conv({bool, false}, Options), - ?assertEqual(true, ejson:json_prop(J1, "value")), - ?assertEqual(false, ejson:json_prop(J2, "value")). + ?assertEqual(true, json_prop(J1, "value")), + ?assertEqual(false, json_prop(J2, "value")). number_test() -> Options = [{complex, ["r", "i"]}], - J = ejson:conv({complex, -35, 809}, Options), + J = conv({complex, -35, 809}, Options), - ?assertEqual(-35, ejson:json_prop(J, "r")), - ?assertEqual(809, ejson:json_prop(J, "i")). + ?assertEqual(-35, json_prop(J, "r")), + ?assertEqual(809, json_prop(J, "i")). binary_test() -> Options = [{text, ["value"]}], - J = ejson:conv({text, <<"A binary as is">>}, Options), + J = conv({text, <<"A binary as is">>}, Options), - ?assertEqual(<<"A binary as is">>, ejson:json_prop(J, "value")). + ?assertEqual(<<"A binary as is">>, json_prop(J, "value")). string_test() -> Options = [{message, ["text"]}], - J = ejson:conv({message, "Texting something"}, Options), + J = conv({message, "Texting something"}, Options), - ?assertEqual(<<"Texting something">>, ejson:json_prop(J, "text")). + ?assertEqual(<<"Texting something">>, json_prop(J, "text")). -endif. diff --git a/test/ejson_list_test.erl b/test/ejson_list_test.erl index 1dcbb8e..fe9baab 100644 --- a/test/ejson_list_test.erl +++ b/test/ejson_list_test.erl @@ -2,6 +2,8 @@ -ifdef(TEST). +-import(ejson, [conv/2, json_prop/2]). + -include_lib("eunit/include/eunit.hrl"). list_test() -> @@ -12,19 +14,18 @@ list_test() -> Book2 = {book, "TDD - the easy way", 760}, Person = {person, "Sam", [Book1, Book2]}, - J = ejson:conv(Person, Options), + J = conv(Person, Options), - ?assertEqual(<<"Sam">>, ejson:json_prop(J, "name")), + ?assertEqual(<<"Sam">>, json_prop(J, "name")), - Bs = ejson:json_prop(J, "books"), + Bs = json_prop(J, "books"), [B1, B2 ] = Bs, ?assertEqual(2, length(Bs)), - ?assertEqual(<<"Introduction to clean coding">>, - ejson:json_prop(B1, "title")), - ?assertEqual(251, ejson:json_prop(B1, "numberOfPages")), - ?assertEqual(<<"TDD - the easy way">>, ejson:json_prop(B2, "title")), - ?assertEqual(760, ejson:json_prop(B2, "numberOfPages")). + ?assertEqual(<<"Introduction to clean coding">>, json_prop(B1, "title")), + ?assertEqual(251, json_prop(B1, "numberOfPages")), + ?assertEqual(<<"TDD - the easy way">>, json_prop(B2, "title")), + ?assertEqual(760, json_prop(B2, "numberOfPages")). proplist_test() -> Square = {shape, square, [{a, 10}]}, @@ -32,6 +33,45 @@ proplist_test() -> Rect = {shape, rect, [{x_left, 10}, {y_left, 15}, {x_right, 50}, {y_right, 30}]}, - Options = [{shape, ["type", {proplist, "data"}]}]. + Options = [{shape, ["type", {proplist, "data"}]}, + {shapes, [{list, "shapes"}]}], + + Shapes = conv({shapes, [Square, Circle, Rect]}, Options), + + Ss = json_prop(Shapes, "shapes"), + ?assertEqual(3, length(Ss)), + + [S1, S2, S3] = Ss, + + ?assertEqual(<<"square">>, json_prop(S1, "type")), + S1d = json_prop(S1, "data"), + ?assertEqual(10, json_prop(S1d, "a")), + + ?assertEqual(<<"circle">>, json_prop(S2, "type")), + S2d = json_prop(S2, "data"), + ?assertEqual(5, json_prop(S2d, "radius")), + + ?assertEqual(<<"rect">>, json_prop(S3, "type")), + S3d = json_prop(S3, "data"), + ?assertEqual(10, json_prop(S3d, "xLeft")), + ?assertEqual(15, json_prop(S3d, "yLeft")), + ?assertEqual(50, json_prop(S3d, "xRight")), + ?assertEqual(30, json_prop(S3d, "yRight")). + +list_error_test() -> + Opts = [{book, ["title", {list, "authors"}]}], + + ?assertThrow({error, {not_a_list, _}}, conv({book, "Title", 12}, Opts)). + +proplist_error_test() -> + Opts = [{canvas, [{proplist, "props"}]}], + + ?assertThrow({error, {not_a_proplist, _}}, conv({canvas, 12}, Opts)). + +many_prop_test() -> + Opts = [{canvas, [{proplist, "props"}]}], + + J = conv({canvas, [{a, 2}, {a, 3}]}, Opts), + ?assertEqual([2, 3], json_prop(json_prop(J, "props"), "a")). -endif. diff --git a/test/ejson_preprocess_test.erl b/test/ejson_preprocess_test.erl new file mode 100644 index 0000000..dc6b7ed --- /dev/null +++ b/test/ejson_preprocess_test.erl @@ -0,0 +1,44 @@ +-module(ejson_preprocess_test). + +-ifdef(TEST). + +-import(ejson, [conv/2, json_prop/2]). + +-include_lib("eunit/include/eunit.hrl"). + +field_fun_enc_test() -> + Opts = [{rect, [{field_fun, "a", {?MODULE, rect_field_conv}}, + {field_fun, "b", {?MODULE, rect_field_conv}}] + }], + + J = conv({rect, 3, 5}, Opts), + + ?assertEqual(30, json_prop(J, "a")), + ?assertEqual(50, json_prop(J, "b")). + +rec_fun_enc_test() -> + Opts = [{rect, ["a", "b", {rec_fun, "area", {?MODULE, rect_area}}]}], + + J= conv({rect, 8, 12}, Opts), + + ?assertEqual(8, json_prop(J, "a")), + ?assertEqual(12, json_prop(J, "b")), + ?assertEqual(96, json_prop(J, "area")). + +const_test() -> + Opts = [{dummy, [{const, "file", {file, "erl", "sh"}}]}, + {file, ["name", "ext"]}], + + J = conv({dummy, "whatever"}, Opts), + File = json_prop(J, "file"), + + ?assertEqual(<<"erl">>, json_prop(File, "name")), + ?assertEqual(<<"sh">>, json_prop(File, "ext")). + +rect_field_conv(A) -> + A * 10. + +rect_area({rect, A, B}) -> + A * B. + +-endif. diff --git a/test/ejson_skip_test.erl b/test/ejson_skip_test.erl new file mode 100644 index 0000000..50e3e9c --- /dev/null +++ b/test/ejson_skip_test.erl @@ -0,0 +1,18 @@ +-module(ejson_skip_test). + +-ifdef(TEST). + +-import(ejson, [conv/2, json_prop/2]). + +-include_lib("eunit/include/eunit.hrl"). + +skip_test() -> + Opts = [{request, [skip, "path", skip, "method"]}], + + J = conv({request, "Socket info", "/index.html", self(), "GET"}, Opts), + + ?assertEqual(<<"/index.html">>, json_prop(J, "path")), + ?assertEqual(<<"GET">>, json_prop(J, "method")), + ?assertEqual(2, length(J)). + +-endif.