Permalink
Browse files

Code cleanup, specs and such

 + Add specs to all methods
 + Make dialyzer run without errors
 + Remove unused functions
 + Move all types into right place (mostly tavern_http:*)
  • Loading branch information...
1 parent f2d066a commit 0e36dde9b76ba018809c0ffedbaa7f415743bdbd @lafka committed Aug 26, 2012
View
@@ -1,18 +1,14 @@
--include("types.hrl").
-
-record(tavern, {
- module :: module(),
- handlers :: [{request_method(), module()}],
- methods :: [{request_method()}],
- provides :: [{mime_charset()}],
- consumes :: [{mime_charset()}],
- charset :: [{binary(), atom()}],
- accept :: [{binary(), binary()}],
- content_type :: binary(),
-%% last_modified :: undefined | {call, function()} | timestamp(),
-%% expires :: undefined | timestamp(),
- status :: 'Bad Request' | atom(),
- body :: undefined | tree()
+ module :: module(),
+ handlers = [] :: [{tavern_http:request_method(), atom()}] | [],
+ methods = [] :: [tavern_http:request_method()] | [],
+ provides = [] :: [tavern_http:mime()] | [],
+ consumes = [] :: [tavern_http:mime()] | [],
+ charset = <<"utf8">> :: binary(),
+ accept = {<<$*>>, <<$*>>} :: tavern_http:mime(),
+ content_type :: tavern_http:mime(),
+ status = 'Bad Request' :: atom(),
+ body = [] :: tavern_http:tree()
}).
-define(DEFAULT_CHARSET, <<"utf8">>).
View
@@ -1,16 +1,21 @@
-include_lib("tavern/include/rest.hrl").
+-include_lib("cowboy/include/http.hrl").
-export([init/3, handle/2, terminate/2, handle_default_options/2]).
+-spec init(Transport :: module(), Req :: #http_req{}, list()) -> {ok, #http_req{}, #tavern{}}.
init(Transport, Req, _Opts) ->
tavern_http:init(Transport, Req, [?MODULE]).
+-spec handle(#http_req{}, #tavern{})-> {ok, #http_req{}, #tavern{}}.
handle(Req, State) ->
tavern_http:handle(?MODULE, Req, State).
+-spec terminate(#http_req{}, #tavern{})-> ok.
terminate(Req, State) ->
tavern_http:terminate(?MODULE, Req, State).
+-spec handle_default_options(_, #tavern{}) -> {Status :: atom(), _, #tavern{}, binary()}.
handle_default_options(Req, #tavern{methods = Methods} = State) ->
F = fun(X, <<>>) -> atom_to_binary(X, utf8);
(X, Acc) -> X2 = atom_to_binary(X, utf8),
View
@@ -1,9 +0,0 @@
--type key() :: atom().
--type value() :: binary() | number() | string() | tree().
--type tree() :: [{key(), value()}].
--type tree(Root) :: [{Root, [{key(), value()}]}].
--type timestamp() :: non_neg_integer().
--type mime() :: {binary(), binary(), []}.
--type request_method() :: atom().
--type mime_charset() :: {binary(), binary(), binary()}.
--type mime_options() :: {binary(), binary(), [{binary(), binary()}]}.
View
@@ -1,11 +1,26 @@
-module(tavern_http).
+-include("rest.hrl").
+-include_lib("cowboy/include/http.hrl").
+
%% HTTP parse callbacks
-export([init/3, handle/3, terminate/3, status/1]).
--include("rest.hrl").
--include_lib("cowboy/include/http.hrl").
+-type key() :: atom().
+-type value() :: binary() | number() | string() | tree().
+-type tree() :: [{key(), value()}].
+-type request_method() :: atom().
+-type mime() :: {binary(), binary()}.
+-type mime_options() :: {binary(), binary(), [{binary(), binary()}]}.
+-type mime_charset() :: {binary(), binary(), binary()}.
+-type returnstatus() :: http_int_status() | atom().
+-type http_int_status() :: 100..599.
+
+-export_type([mime/0, tree/0, mime_charset/0, request_method/0, returnstatus/0,
+ mime_options/0]).
+
+-spec init(Transport :: module(), Req :: #http_req{}, [module()]) -> {ok, #http_req{}, #tavern{}}.
init(_Transport, Req, [Handler]) ->
Defaults = [
{methods, ['HEAD', 'GET', 'OPTIONS']}
@@ -17,9 +32,6 @@ init(_Transport, Req, [Handler]) ->
, {'DELETE', handle_delete}
, {'PATCH', handle_patch}
, {'OPTIONS', handle_default_options}
- , {unauthorized, fun handle_unauthorized/2}
- , {forbidden, fun handle_forbidden/2}
- , {error, fun handle_error/2}
]}
, {provides, [
{<<"text">>, <<"html">>, []}
@@ -47,6 +59,7 @@ init(_Transport, Req, [Handler]) ->
{_, State} = lists:mapfoldl( Fun, #tavern{}, Defaults),
{ok, Req, State#tavern{module = Handler}}.
+-spec handle(Handler :: module(), #http_req{}, #tavern{})-> {ok, #http_req{}, #tavern{}}.
handle(Module, Req, #tavern{} = State) ->
try
case tavern_req:validate_req(Req, State) of
@@ -71,11 +84,16 @@ handle(Module, Req, #tavern{} = State) ->
{ok, Resp2, State}
end.
+-spec terminate(Handler :: module(), #http_req{}, #tavern{})-> ok.
terminate(_Handler, _Req, _State) ->
ok.
+-spec handle_call(Handler :: module(), #http_req{}, #tavern{})-> {returnstatus(), #http_req{}, #tavern{}, tree()}.
handle_call(_, Req, #tavern{handlers = []} = State) ->
- handle_error(Req, State, <<"(error: #1002) no request handler found">>);
+ {'Internal Server Error', Req, State, [{error, [
+ {code, 1002}
+ , {message, <<"(error #1002) no request handler found">>}
+ ]}]};
handle_call(Module, Req, #tavern{handlers = Handlers} = State) ->
try
@@ -93,11 +111,18 @@ handle_call(Module, Req, #tavern{handlers = Handlers} = State) ->
"** State: ~p~n"
"** Stacktrace: ~p~n~n",
[Module, Class, Reason, Req, State, erlang:get_stacktrace()]),
- handle_error(Req, State)
+ {'Internal Server Error', Req, State, [{error, [
+ {code, 1000}
+ , {message, <<"(error #1000) an unexpected error occured">>}
+ ]}]}
end.
-handle_resp(Req, State, _Status, undefined) ->
- handle_error(Req, State, <<"empty response">>);
+-spec handle_resp(#http_req{}, #tavern{}, Status :: returnstatus(), Result :: tree())-> {ok, #http_req{}, #tavern{}}.
+handle_resp(Req, State, _Status, []) ->
+ handle_resp(Req, State, 'Internal Server Error', [{error, [
+ {code, 1001}
+ , {message, <<"(error #1001) empty response">>}
+ ]}]);
handle_resp(Req, State, Status, Payload) when is_atom(Status) ->
handle_resp(Req, State, status(Status), Payload);
@@ -108,22 +133,7 @@ handle_resp(Req, #tavern{accept = Accept} = State, Status, Payload) ->
{ok, Resp} = A,
{ok, Resp, State}.
-handle_unauthorized(Req, State) ->
- handle_error(Req, State, status('Unauthorized'), <<"authentication required">>).
-
-handle_forbidden(Req, State) ->
- handle_error(Req, State, status('Forbidden'), <<"unauthorized">>).
-
-handle_error(Req, State) ->
- handle_error(Req, State, <<"(error #1000) an unexpected error occured">>).
-
-handle_error(Req, State, Payload) ->
- handle_error(Req, State, status('Internal Server Error'), Payload).
-
-handle_error(Req, State, Status, Payload) ->
- handle_resp(Req, State, Status, [{error, [{message, Payload}]}]).
-
--spec status(atom()) -> non_neg_integer().
+-spec status(atom()) -> http_int_status().
status('Continue') -> 100;
status('Switching Protocols') -> 101;
status('Processing') -> 102;
View
@@ -1,34 +1,28 @@
-module(tavern_marshal).
--export([decode/2, encode/2, mimetypes/0]).
+-export([decode/2, encode/2]).
-include("rest.hrl").
--spec decode(MimeType :: binary() | atom(), Encoded :: iolist()) -> Payload :: tree().
+-spec decode(Mime :: tavern_http:mime() | binary(), Encoded :: iolist()) -> Payload :: {ok, tavern_http:tree()}.
decode(Mime, Payload) ->
Module = map_mime(Mime),
{ok, _} = Module:decode(Payload).
--spec encode(MimeType :: binary() | atom(), Payload :: tree()) -> Encoded :: iolist().
+-spec encode(Mime :: tavern_http:mime() | binary(), Payload :: tavern_http:tree()) -> {ok, Encoded :: iolist()}.
encode(_, Payload) when Payload == []; Payload == <<>>; Payload == {} ->
{ok, <<>>};
encode(Mime, Payload) ->
Module = map_mime(Mime),
{ok, _} = Module:encode(Payload).
--spec mimetypes() -> [{MimeType :: binary()}].
-mimetypes() ->
- [<<"text/html">>, <<"application/json">>, <<"application/xml">>, <<"text/plain">>].
-
--spec map_mime(MimeType :: atom() | binary()) -> module().
-map_mime(A) when A == undefined, A == <<>> -> map_mime(<<"text/plain">>);
-map_mime({Mime1, Mime2}) -> map_mime(<<Mime1/binary, $/, Mime2/binary>>);
-map_mime(Mime) when is_atom(Mime) -> map_mime(atom_to_binary(Mime, utf8));
-map_mime(<<"application/xml">>) -> tavern_marshal_xml;
-map_mime(<<"application/json">>) -> tavern_marshal_json;
-map_mime(<<"text/html">>) -> tavern_marshal_html;
-map_mime(<<"text/plain">>) -> tavern_marshal_plain.
+-spec map_mime(Mime :: binary() | tavern_http:mime()) -> tavern_marshal_xml | tavern_marshal_json | tavern_marshal_html | tavern_marshal_plain.
+map_mime({<<Mime1/binary>>, <<Mime2/binary>>}) -> map_mime(<<Mime1/binary, $/, Mime2/binary>>);
+map_mime(<<"application/xml">>) -> tavern_marshal_xml;
+map_mime(<<"application/json">>) -> tavern_marshal_json;
+map_mime(<<"text/html">>) -> tavern_marshal_html;
+map_mime(<<"text/plain">>) -> tavern_marshal_plain.
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
@@ -2,8 +2,10 @@
-export([decode/1, encode/1]).
+-spec decode(binary()) -> {error, not_implemented}.
decode(_Payload) ->
- erlang:error(not_implemented).
+ {error, not_implemented}.
+-spec encode(tavern_http:tree()) -> {error, not_implemented}.
encode(_Payload) ->
- erlang:error(not_implemented).
+ {error, not_implemented}.
@@ -2,14 +2,14 @@
-export([decode/1, encode/1]).
--spec decode(binary()) -> [{atom(), binary() | [{atom(), binary()}]}].
+-spec decode(binary()) -> {ok, Data :: tavern_http:tree()} | {error, Error :: atom()}.
decode(Payload) ->
case (catch mochijson2:decode(Payload)) of
{struct, _} -> {ok, encode_mochistruct(Payload)};
_ -> {error, 'invalid json payload'}
end.
--spec encode([{atom(), binary() | [{atom(), binary()}]}]) -> binary().
+-spec encode(tavern_http:tree()) -> {ok, Data :: iolist()} | {error, Error :: atom()}.
encode(Payload) ->
case (catch mochijson2:encode(Payload)) of
{'EXIT', _} -> {error, 'json serialization failed'};
@@ -2,8 +2,10 @@
-export([decode/1, encode/1]).
+-spec decode(binary()) -> {ok, Data :: tavern_http:tree()}.
decode(Payload) ->
- [Payload].
+ {ok, Payload}.
+-spec encode(tavern_http:tree()) -> {ok, Data :: iolist()}.
encode(Payload) ->
{ok, io_lib:format("~p~n", [Payload])}.
View
@@ -2,10 +2,9 @@
-export([decode/1, encode/1]).
--include("types.hrl").
-include_lib("eunit/include/eunit.hrl").
--spec decode(binary()) -> {ok, Data :: tree()} | {error, Error :: atom()}.
+-spec decode(binary()) -> {ok, Data :: tavern_http:tree()} | {error, Error :: atom()}.
decode(Payload) ->
case erlsom:simple_form(Payload) of
{ok, {_, _, NodeList}, _} ->
@@ -19,7 +18,7 @@ decode(Payload) ->
_ -> {error, 'xml unserialization failed'}
end.
--spec encode(tree()) -> {ok, Data :: binary()} | {error, Error :: atom()}.
+-spec encode(tavern_http:tree()) -> {ok, Data :: binary()} | {error, Error :: atom()}.
encode(Payload) ->
case (catch xmerl:export_simple(encode_list(Payload), xmerl_xml)) of
{'EXIT', _} -> {error, 'xml serialization failed'};
@@ -46,8 +45,6 @@ encode_value(V) -> [V].
-ifdef(TEST).
- -record(testrec, {abc, def}).
-
encode_single_level_test() ->
{ok, _} = encode([{level1, [{level2, "value"}]}]),
{ok, _} = encode([{level1, [{level2, "value"}, {level2, "value2"}]}]).
@@ -59,9 +56,6 @@ encode_value(V) -> [V].
{ok, _} = encode([{level1, [{level2, 1000000}]}]),
{ok, _} = encode([{level1, [{level2, "stringvalue"}]}]).
- encode_record_test() ->
- {error, _} = encode([{record, #testrec{abc=123, def=abc}}]).
-
encode_multilevel_test() ->
{ok, _} = encode([{level1, [{level2, [{level3, [{level4, <<"value">>}]}]}]}]).
Oops, something went wrong.

0 comments on commit 0e36dde

Please sign in to comment.