Skip to content
This repository has been archived by the owner on Jan 12, 2023. It is now read-only.

Commit

Permalink
Fix test and cleanup code
Browse files Browse the repository at this point in the history
  • Loading branch information
jonasrichard committed Jul 2, 2015
1 parent a0239ac commit 84a5694
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 56 deletions.
119 changes: 89 additions & 30 deletions src/ejson_decode.erl
Original file line number Diff line number Diff line change
Expand Up @@ -79,50 +79,109 @@ extract_fields([], _, _) ->
extract_fields([Field | F], AttrList, Opts) ->
case ejson_util:get_field_name(Field) of
undefined ->
%% The skip rule: we haven't included that field in json,
%% so we cannot extract any value for it.
[undefined | extract_fields(F, AttrList, Opts)];
BareField ->
Bf = if is_atom(BareField) ->
list_to_binary(atom_to_list(BareField));
atom_to_binary(BareField, utf8);
true ->
list_to_binary(BareField)
end,
case lists:keyfind(Bf, 1, AttrList) of
false ->
{error, {no_value_for, Field}};
%% No value for field, check if we have default
case default_value(Field) of
undefined ->
{error, {no_value_for, Field}};
DefVal ->
[DefVal | extract_fields(F, AttrList, Opts)]
end;
{_, Value} ->
%% Extract value based on Field rule
Extracted = extract_value(Field, Value, Opts),
[Extracted | extract_fields(F, AttrList, Opts)]
end
end.

extract_value({list, _}, Value, Opts) ->
[case V of
_ when is_number(V) ->
V;
_ ->
decode1(V, Opts)
end || V <- Value];
extract_value({list, _, Type}, Value, Opts) ->
T = list_to_binary(atom_to_list(Type)),
%% Add record meta info to each element of the list
[decode1([{<<"__rec">>, T} | V], Opts) || V <- Value];
extract_value({binary, _}, Value, _Opts) ->
Value;
extract_value({string, _}, Value, _Opts) ->
unicode:characters_to_list(Value);
extract_value({atom, _}, Value, _Opts) ->
list_to_atom(binary_to_list(Value));
extract_value({proplist, _}, Value, _Opts) ->
[{list_to_atom(binary_to_list(Prop)), Val}
|| {Prop, Val} <- Value, Prop =/= <<"__type">>];
extract_value({field_fun, _, _, {M, F}}, Value, _Opts) ->
erlang:apply(M, F, [Value]);
extract_value({field_fun, _, _, DecFun}, Value, _Opts) ->
DecFun(Value);
extract_value({rec_fun, _, _}, _Value, _Opts) ->
extract_value(Rule, Value, Opts) ->
case Rule of
{atom, _} ->
extract_atom(Value);
{atom, _, _} ->
extract_atom(Value);
{binary, _} ->
Value;
{binary, _, _} ->
Value;
{string, _} ->
extract_string(Value);
{string, _, _} ->
extract_string(Value);
{list, _} ->
extract_list(Value, [], Opts);
{list, _, FieldOpts} ->
extract_list(Value, FieldOpts, Opts);
{field_fun, _, _EncFun, DecFun} ->
extract_field_fun(Value, DecFun, Value, Opts);
{rec_fun, _, _} ->
undefined;
{proplist, _} ->
%% TODO proper conversion here!
undefined;
{const, _, _} ->
undefined;
_AttrName when is_list(Value) ->
decode(Value, Opts);
_AttrName ->
%% number and boolean case
Value
end.

extract_atom(null) ->
undefined;
extract_value({const, _, _}, _Value, _Opts) ->
extract_atom(Value) ->
binary_to_atom(Value, utf8).

extract_string(null) ->
undefined;
extract_string(Value) ->
unicode:characters_to_list(Value, utf8).

extract_list(null, _FieldOpts, _Opts) ->
undefined;
extract_value(_, Value, _Opts) ->
Value.
extract_list(Value, FieldOpts, Opts) ->
case proplists:get_value(type, FieldOpts) of
undefined ->
%% No target type for list element, it can be an attrlist
%% or a primitive value
lists:map(
fun(V) when is_list(V) ->
{ok, D} = decode(V, Opts),
%% TODO make an error case and gives back error
D;
(V) ->
V
end, Value);
Type ->
[decode1(V, Opts, Type) || V <- Value]
end.

extract_field_fun(Value, {M, F}, Value, Opts) ->
try erlang:apply(M, F, [Value]) of
Val ->
Val
%%decode(Val, Opts)
catch
E:R ->
{error, {field_run, {M, F}, {E, R}}}
end.

%% Get the default value from a field rule
default_value({Type, _, Opts}) when Type =:= atom orelse
Type =:= binary orelse
Type =:= list orelse
Type =:= string ->
proplists:get_value(default, Opts);
default_value(_) ->
undefined.
3 changes: 1 addition & 2 deletions src/ejson_encode.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@
-export([encode/2]).

%% TODO: conditional macro for atom_to_binary
-define(BIN(Name), if is_atom(Name) -> list_to_binary(atom_to_list(Name));
-define(BIN(Name), if is_atom(Name) -> atom_to_binary(Name, utf8);
true -> list_to_binary(Name)
%%{error, {atom_expected, Name}}
end).

%%------------------------------------------------------------------------------
Expand Down
22 changes: 10 additions & 12 deletions src/ejson_util.erl
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,17 @@ get_fields(RecordName, Opts) ->

get_field_name(skip) ->
undefined;
get_field_name({list, Field}) ->
get_field_name({Type, Field}) when Type =:= atom orelse
Type =:= binary orelse
Type =:= list orelse
Type =:= proplist orelse
Type =:= string ->
Field;
get_field_name({list, Field, _Type}) ->
Field;
get_field_name({binary, Field}) ->
Field;
get_field_name({string, Field}) ->
Field;
get_field_name({atom, Field}) ->
Field;
get_field_name({proplist, Field}) ->
Field;
get_field_name({const, Field, _}) ->
get_field_name({Type, Field, _FieldOpts}) when Type =:= atom orelse
Type =:= binary orelse
Type =:= const orelse
Type =:= list orelse
Type =:= string ->
Field;
get_field_name({field_fun, Field, _EncFun, _DecFun}) ->
Field;
Expand Down
8 changes: 5 additions & 3 deletions test/ejson_basic_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ list_test_() ->
[?_assertEqual({ok, Expected}, ejson_encode:encode(Expected, []))
|| Expected <- Cases].

enc_fun(Num) -> Num * 1000.
dec_fun(Num) -> Num div 1000.

field_fun_test_() ->
Enc = fun(Num) -> Num * 1000 end,
Dec = fun(Num) -> Num div 1000 end,
Opts = [{time, {field_fun, "jsTime", Enc, Dec}}],
Opts = [{time, {field_fun, "jsTime", {?MODULE, enc_fun},
{?MODULE, dec_fun}}}],
Record = {time, 2300},
{ok, E} = ejson_encode:encode(Record, Opts),
{ok, D} = ejson_decode:decode(E, Opts),
Expand Down
10 changes: 6 additions & 4 deletions test/ejson_default.erl
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
-json({person, {string, "name"},
{string, "address", [{default, "No address"}]}}).

default_test_() ->
default_test() ->
B = {book, "Treasure Island", [{person, "Robert Louis Stevenson"}], 1911},
Enc = to_json(B),
Dec = from_json(Enc),
?debugVal(Dec).
{ok, Enc} = to_json(B),
?debugVal(Enc),
{ok, Dec} = from_json(Enc),
?debugVal(Dec),
{book, _, [{person, _, undefined}], 1911} = Dec.
5 changes: 1 addition & 4 deletions test/ejson_prop.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
-include_lib("proper/include/proper.hrl").
-include_lib("eunit/include/eunit.hrl").

-type record_name() :: atom().

all_test() ->
?assertEqual([], proper:module(?MODULE, [{to_file, user}])).

Expand Down Expand Up @@ -171,8 +169,7 @@ pro_proplist_enc_dec() ->
end).

prop_camel_case() ->
?FORALL(Name,
?SUCHTHAT(R, record_name(), ejson_util:is_name_convertable(R)),
?FORALL(Name, symb_name(),
begin
CC = ejson_util:atom_to_binary_cc(Name),
ejson_util:binary_to_atom_cc(CC) =:= Name
Expand Down
2 changes: 1 addition & 1 deletion test/ejson_typed_test.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
-include_lib("eunit/include/eunit.hrl").

specified_rec_type_test() ->
Opts = [{book, {string, title}, {list, "authors", author}},
Opts = [{book, {string, title}, {list, "authors", [{type, author}]}},
{author, {string, name}}],
Rec = {book, "History of Rome", [{author, "John Smith"},
{author, "Bob Doe"}]},
Expand Down

0 comments on commit 84a5694

Please sign in to comment.