Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

yanked out of private repos, currently looking too much like an amput…

…ation
  • Loading branch information...
commit 29c9a1a3ef58eff61ba7f46b62787d169d89128e 0 parents
Justin Kirby authored
4 README
@@ -0,0 +1,4 @@
+I will eventually flesh this out. This is a quick yank out of a larger
+private repos. So forgive the crudeness.
+
+I have included exprecs.erl in the src for convenience.
5 TODO.org
@@ -0,0 +1,5 @@
+* TODO move unit tests to file under tests dir
+* TODO flesh out a handy readme file based on upcoming blog post.
+ The blog post has been written. this git repos is here so it can
+ point to something
+
267 src/json_rec.erl
@@ -0,0 +1,267 @@
+%%%-------------------------------------------------------------------
+%%% @author Justin Kirby <jkirby@voalte.com>
+%%% @copyright (C) 2011,
+%%% @doc
+%%% Assuming a record of `-record(simple, {one, two})' in mod_fake
+%%% Usage example:
+%%% ```
+%%% Rec = mod_fake:new(<<"simple">>),
+%%% Json = mochijson2:decode("{'one':1,'two':2}"),
+%%% SimpleRec = json_rec:to_rec(Json,mod_fake,Rec)
+%%%
+%%% '''
+%%%
+%%% The above code will take the json and transform it into the
+%%% specified record. Trying to match the field of the record with the
+%%% key in the json. If a match fails, then json_rec will fall back to
+%%% using proplists
+%%%
+%%% The module MUST export module:new/1. new/1 should take a binary and return a record. Example:
+%%% ```
+%%% -module(mod_fake).
+%%% -export([new/1]).
+%%% -record(simple, {one,two}).
+%%% new(<<"simple">>) -> #simple{};
+%%% new(_) -> undefined.
+%%% '''
+%%%
+%%% @end
+%%%-------------------------------------------------------------------
+-module(json_rec).
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+-record(simple, {
+ one,
+ two
+ }).
+
+-record(simplet2l, {
+ two
+ }).
+
+-record(deep, {
+ simple,
+ second = []
+ }).
+
+
+-compile({parse_transform, exprecs}).
+-export_records([simple,simplet2l,deep]).
+
+-endif.
+
+-include("vest_types.hrl").
+-export([to_rec/3,
+ to_json/2
+ ]).
+
+to_json(Record, Module) ->
+ Fields = Module:'#info-'(fields,Record),
+ Pl = rec_keys(Fields,Record,Module,[]),
+ {struct, Pl}.
+
+rec_keys([], _Record, _Module, Acc) -> Acc;
+rec_keys([Field|Rest],Record,Module,Acc) ->
+ Value = Module:'#get-'(Field,Record),
+ Key = list_to_binary(atom_to_list(Field)),
+ JsonValue = field_value(Value,Module,[]),
+ rec_keys(Rest, Record, Module,[{Key,JsonValue}|Acc]).
+
+field_value(Value, Module, _Acc) when is_tuple(Value) ->
+ case Module:rec(Value) of
+ true ->
+ to_json(Value,Module);
+ false ->
+ Value
+ end;
+field_value([],_Module, Acc) -> Acc;
+field_value([Value|Rest], Module, Acc) ->
+ NewValue = case field_value(Value,Module,[]) of
+ IsRec when is_tuple(IsRec),
+ is_atom(element(1,Value)) ->
+ %% this returned a record, so get the first element fromthe rec tuple and do: {struct, atom
+ {struct, [{list_to_binary(atom_to_list(element(1,Value))),IsRec}]};
+ IsTuple when is_tuple(IsTuple) ->
+ tuple_to_list(IsTuple);
+ NotRec ->
+ NotRec
+ end,
+ field_value(Rest, Module,[NewValue|Acc]);
+field_value(Value,_Module,_Acc) ->
+ Value.
+
+
+
+
+%% @spec to_rec(_Json, Module, Record) -> tuple()
+%% @doc
+%% Take the result from mochijson2:decode/1 and transform it into a
+%% record, or proplist.
+%%
+%% _Json MUST the result of mochijson2:decode/1.
+%% Module is a module that refers to a specific module which exports new/1.
+%% Rec is the initial empty record #record_name{} or `module:new(<<"record_name">>)'
+%%
+%% NOTE: it is up to you to export and define module:new/1
+-spec to_rec(_Json :: json_dict(), Module :: atom(), undefined) ->
+ proplist();
+ (_Json :: json_dict(), Module :: atom(), Rec :: tuple() ) ->
+ Rec :: tuple().
+
+to_rec({struct, Pl} = _Json, Module, undefined) ->
+ pl(Pl, Module);
+
+to_rec({struct, Pl} = _Json, Module, Rec) ->
+ keys_rec(Pl, Module, Rec).
+
+keys_rec([], _Module, Rec) -> Rec;
+keys_rec([{Key, {struct, Pl}}|Rest], Module, Rec) ->
+ Field = list_to_atom(binary_to_list(Key)),
+ Value = case Module:new(Key) of
+ undefined ->
+ %% this is not a sub record, so just pl it
+ pl(Pl,Module);
+ SubRec ->
+ %% we have a new record, go back go the topproplist
+ to_rec({struct,Pl}, Module, SubRec)
+ end,
+ UpRec = Module:'#set-'([{Field,Value}],Rec),
+ keys_rec(Rest, Module, UpRec);
+
+keys_rec([{Key, Value}|Rest], Module, Rec) ->
+ Field = list_to_atom(binary_to_list(Key)),
+ NewValue = to_value(Value,Module),
+ NewRec = Module:'#set-'([{Field,NewValue}],Rec),
+ keys_rec(Rest,Module,NewRec).
+
+pl(P, Module) ->
+ pl(P,Module,[]).
+pl([],_M,[H]) -> H;
+pl([],_M,Acc) -> Acc;
+pl([{Key, {struct,Pl}}|Rest], Module, Acc) ->
+ Value = case Module:new(Key) of
+ undefined ->
+ {Key, pl(Pl, Module, [])};
+ Rec ->
+ to_rec({struct, Pl}, Module, Rec)
+ end,
+ pl(Rest, Module, [Value|Acc]);
+pl([{Key,Value}|Rest], Module, Acc) ->
+ pl(Rest, Module, [{Key,Value}|Acc]).
+
+to_value(V, Module) ->
+ to_value(V, Module, []).
+
+to_value({struct, Pl}, Module, _Acc) ->
+ pl(Pl,Module);
+to_value([], _Module, Acc) -> Acc;
+to_value([H|T],Module, Acc) ->
+ to_value(T,Module,[to_value(H,Module,[])|Acc]);
+to_value(V,_Module,_Acc) -> V.
+
+
+
+
+
+
+-ifdef(TEST).
+
+new(<<"simple">>) ->
+ '#new-simple'();
+new(<<"simplet2l">>) ->
+ '#new-simplet2l'();
+new(<<"deep">>) ->
+ '#new-deep'();
+new(_RecName) -> undefined.
+
+rec(#simple{}) -> true;
+rec(#simplet2l{}) -> true;
+rec(#deep{}) -> true;
+rec(_Rec) -> false.
+
+
+simple_json_data() ->
+ ["{\"one\":1,\"two\":2}",
+ #simple{ one = 1, two = 2}].
+
+simple_json_t2l_data() ->
+ ["{\"two\":[1,2,3]}",
+ #simplet2l{ two = {1,2,3}}].
+unknown_json_data() ->
+ ["{\"one\":1,\"two\":2}",
+ [{<<"two">>, 2},{<<"one">>,1}]].
+
+deep_json_data() ->
+ Simple = "{\"simple\":{\"one\":1,\"two\":2}",
+ Deep = Simple++"}",
+ [Deep,
+ #deep{ simple = #simple{ one = 1, two = 2}
+ }].
+
+deep_deep_json_data() ->
+ Simple = "\"simple\":{\"one\":1,\"two\":2}",
+ Deep = "{"++Simple++",\"second\":[{"++Simple ++ "},{" ++ Simple ++ "},{" ++ Simple ++"}]}",
+ [Deep,
+ #deep{ simple = #simple{ one = 1, two = 2},
+ second = [ #simple{ one = 1, two = 2},
+ #simple{ one = 1, two = 2},
+ #simple{ one = 1, two = 2}
+ ]
+ }
+ ].
+
+
+simple_test() ->
+ [Json, Rec] = simple_json_data(),
+ NewRec = ?MODULE:to_rec(mochijson2:decode(Json),?MODULE,?MODULE:new(<<"simple">>)),
+ ?assertEqual(Rec, NewRec).
+
+deep_test() ->
+ [Json, Rec] = deep_json_data(),
+ NewRec = ?MODULE:to_rec(mochijson2:decode(Json),?MODULE,?MODULE:new(<<"deep">>)),
+ ?assertEqual(Rec, NewRec).
+
+deep_deep_test() ->
+ [Json, Rec] = deep_deep_json_data(),
+ New = ?MODULE:to_rec(mochijson2:decode(Json),?MODULE,?MODULE:new(<<"deep">>)),
+ ?assertEqual(Rec, New).
+
+unknown_test() ->
+ [Json, Rec] = unknown_json_data(),
+ New = ?MODULE:to_rec(mochijson2:decode(Json),?MODULE,?MODULE:new(<<"unknown">>)),
+ ?assertEqual(Rec, New).
+
+to_json_simple_test() ->
+ [_Json, Rec] = simple_json_data(),
+
+ Conv = ?MODULE:to_json(Rec, ?MODULE),
+ Sjson= lists:flatten(mochijson2:encode(Conv)),
+
+ New = ?MODULE:to_rec(mochijson2:decode(Sjson),?MODULE,?MODULE:new(<<"simple">>)),
+ ?assertEqual(Rec,New).
+
+to_json_deep_test() ->
+ [_Json, Rec] = deep_json_data(),
+ Conv = ?MODULE:to_json(Rec,?MODULE),
+ Sjson = lists:flatten(mochijson2:encode(Conv)),
+
+
+ New = ?MODULE:to_rec(mochijson2:decode(Sjson), ?MODULE, ?MODULE:new(<<"deep">>)),
+ ?assertEqual(Rec,New).
+
+to_json_deep_deep_test() ->
+ [_Json, Rec] = deep_deep_json_data(),
+ Conv = ?MODULE:to_json(Rec,?MODULE),
+ Sjson = lists:flatten(mochijson2:encode(Conv)),
+ New = ?MODULE:to_rec(mochijson2:decode(Sjson), ?MODULE, ?MODULE:new(<<"deep">>)),
+ ?assertEqual(Rec,New).
+
+to_json_tuple_to_list_test() ->
+ [Json, Rec] = simple_json_t2l_data(),
+ Conv = ?MODULE:to_json(Rec,?MODULE),
+ Sjson = lists:flatten(mochijson2:encode(Conv)),
+ ?assertEqual(Json,Sjson).
+
+-endif.
15 src/json_rec_model.erl
@@ -0,0 +1,15 @@
+-module(rest_model).
+
+-export([behaviour_info/1]).
+
+behaviour_info(callbacks) ->
+ [{init,1},
+ {new,1},
+ {rec,1}
+ ];
+behaviour_info(_Other) ->
+ undefined.
+
+
+
+
75 src/json_rec_model.example
@@ -0,0 +1,75 @@
+%% -*- mode: erlang -*-
+%%%-------------------------------------------------------------------
+%%% @author <jkirby@voalte.com>
+%%% @copyright (C) 2011,
+%%% @doc
+%%%
+%%% This is an example of how the json_rec_model behaviour can be
+%%% used.
+%%%
+%%% comments inline should explain the rest.
+%%%
+%%% @end
+%%% Created : 1 Sep 2011 by <jkirby@voalte.com>
+%%%-------------------------------------------------------------------
+-module(json_rec_model_example).
+
+
+%% specifiy that your module is a json_rec_model. much like gen_server
+-behaviour(json_rec_model).
+
+
+%% the three functions required export
+-export([
+ init/1,
+ new/1,
+ rec/1
+ ]).
+
+%% define your record(s) that this module will work with
+-record(example, {
+ foo = 1,
+ bar = 2
+ }).
+
+-record(fizzbuzz, {
+ answer = 42
+ }).
+
+%% make these records accessible via exprecs. This is necessary in
+%% order to let json_rec move from json to #rec{} and back again for
+%% you
+-compile({parse_transform, exprecs}).
+-export_records([example, fizzbuzz]).
+
+
+
+%% when json_rec is recursing down json and encounters a dict,
+%% {struct, <<"name">>, []}, it will try to determine if that is a
+%% defined record. json_rec will call new(<<"name">>) and use that
+%% with exprecs magic to fill in the record.
+%%
+%% if you do not want to deal with json_rec do new(_) -> undefined.
+new(<<"example">>) ->
+ '#new-example'();
+
+new(<<"fizzbuzz">>) ->
+ '#new-fizzbuzz'();
+
+new(_) -> undefined.
+
+
+
+%% when json_rec is converting your data to json, it will try to
+%% determine if the tuple is a defined record. If it is, that
+%% information is used to build json dicts, otherwise the json becomes
+%% a json key:value.
+rec(#example{}) ->
+ true;
+
+rec(#fizzbuzz{}) ->
+ true;
+
+rec(_) ->
+ false.
+
Please sign in to comment.
Something went wrong with that request. Please try again.