Permalink
Browse files

Sane implementation of data support

  • Loading branch information...
1 parent 7457f3e commit 496a97dee45f124f6a0fd22784bec80901081176 @gar1t committed Aug 3, 2011
Showing with 127 additions and 24 deletions.
  1. +52 −24 src/modlib_data.erl
  2. +75 −0 test/modlib_data_tests.erl
View
76 src/modlib_data.erl
@@ -21,9 +21,9 @@ optional(Name, Params, Default, Options) ->
end.
convert(Val, Options, Name) ->
- case convert_type(Val, Options) of
+ case apply_conversion(Val, Options) of
{ok, Converted} ->
- case check_range(Converted, Options) of
+ case apply_range(Converted, Options) of
{ok, InRange} -> InRange;
{error, Err} ->
throw({badreq, [Name, " is out of range (", Err, ")"]})
@@ -32,32 +32,60 @@ convert(Val, Options, Name) ->
throw({badreq, [Name, " is invalid (", Err, ")"]})
end.
-convert_type(Val, Options) ->
- case proplists:get_bool(int, Options) of
- true -> convert_integer(Val);
- false -> {ok, Val}
- end.
-
-check_range(Val, Options) ->
- case proplists:get_bool(positive, Options) of
- true -> check_positive(Val);
- false -> {ok, Val}
- end.
-
-check_positive(Num) when is_number(Num) ->
- case Num > 0 of
- true -> {ok, Num};
- false -> {error, "must be > 0"}
- end;
-check_positive(_Num) ->
- {error, "not a number"}.
-
-convert_integer(Val) ->
+apply_conversion(Val, []) -> {ok, Val};
+apply_conversion(Val, [int|_]) when is_list(Val) ->
try list_to_integer(Val) of
Int -> {ok, Int}
catch
error:badarg -> {error, "not an integer"}
- end.
+ end;
+apply_conversion(Val, [float|_]) when is_list(Val) ->
+ try list_to_float(Val) of
+ Float -> {ok, Float}
+ catch
+ error:badarg ->
+ try list_to_integer(Val) of
+ Int -> {ok, float(Int)}
+ catch
+ error:badarg ->
+ {error, "not an float"}
+ end
+ end;
+apply_conversion(Val, [bool|_]) when is_list(Val) ->
+ case string:to_lower(Val) of
+ "" -> {ok, false};
+ "0" -> {ok, false};
+ "false" -> {ok, false};
+ "no" -> {ok, false};
+ _ -> {ok, true}
+ end;
+apply_conversion(Val, [_|Rest]) ->
+ apply_conversion(Val, Rest).
+
+apply_range(Val, []) -> {ok, Val};
+apply_range(RHS, [{Op, LHS}|Rest]) ->
+ case apply_op(Op, RHS, LHS) of
+ true -> apply_range(RHS, Rest);
+ false -> {error, io_lib:format("must be ~s ~p", [Op, LHS])}
+ end;
+apply_range(Val, [_|Rest]) ->
+ apply_range(Val, Rest).
+
+apply_op('<', L, R) -> L < R;
+apply_op('<=', L, R) -> L =< R;
+apply_op('=<', L, R) -> L =< R;
+apply_op('>', L, R) -> L > R;
+apply_op('>=', L, R) -> L >= R;
+apply_op('/=', L, R) -> L /= R;
+apply_op('<>', L, R) -> L /= R;
+apply_op('=', L, R) -> L == R;
+apply_op('==', L, R) -> L == R;
+apply_op(in, Val, Options) -> lists:member(Val, Options);
+apply_op('or', L, Ops) ->
+ lists:any(fun({Op, R}) -> apply_op(Op, L, R);
+ (Other) -> exit({invalid_operator, Other})
+ end, Ops);
+apply_op(Other, _L, _R) -> exit({invalid_operator, Other}).
eval_default({M, F}) -> erlang:apply(M, F, []);
eval_default({M, F, A}) -> erlang:apply(M, F, A);
View
75 test/modlib_data_tests.erl
@@ -0,0 +1,75 @@
+-module(modlib_data_tests).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-import(modlib_data, [optional/4]).
+
+optional_test() ->
+ Params = [{"s", "Foo"},
+ {"i", "123"},
+ {"f", "123.456"},
+ {"b1", ""},
+ {"b2", "no"},
+ {"b3", "No"},
+ {"b4", "FALSE"},
+ {"b5", "0"},
+ {"b6", "Yes"}],
+
+ %% Defaults
+ ?assertEqual(nil, optional("foo", Params, nil, [])),
+ ?assertEqual(nil, optional("foo", Params, nil, [int])),
+
+ %% Strings are returns if type isn't specified
+ ?assertEqual("Foo", optional("s", Params, nil, [])),
+ ?assertEqual("123", optional("i", Params, nil, [])),
+ ?assertEqual("123.456", optional("f", Params, nil, [])),
+
+ %% Integers
+ ?assertEqual(123, optional("i", Params, nil, [int])),
+ ?assertThrow({badreq, _}, optional("s", Params, nil, [int])),
+ ?assertThrow({badreq, _}, optional("f", Params, nil, [int])),
+ ?assertThrow({badreq, _}, optional("b1", Params, nil, [int])),
+
+ %% Floats
+ ?assertEqual(123.456, optional("f", Params, nil, [float])),
+ ?assertEqual(123.0, optional("i", Params, nil, [float])),
+ ?assertThrow({badreq, _}, optional("s", Params, nil, [float])),
+ ?assertThrow({badreq, _}, optional("b1", Params, nil, [float])),
+
+ %% Booleans
+ ?assertEqual(false, optional("b1", Params, nil, [bool])),
+ ?assertEqual(false, optional("b2", Params, nil, [bool])),
+ ?assertEqual(false, optional("b3", Params, nil, [bool])),
+ ?assertEqual(false, optional("b4", Params, nil, [bool])),
+ ?assertEqual(false, optional("b5", Params, nil, [bool])),
+ ?assertEqual(true, optional("b6", Params, nil, [bool])),
+ ?assertEqual(true, optional("s", Params, nil, [bool])),
+ ?assertEqual(true, optional("i", Params, nil, [bool])),
+ ?assertEqual(true, optional("f", Params, nil, [bool])),
+
+ %% Range
+ ?assertEqual(123, optional("i", Params, nil, [int, {'>', 0}])),
+ ?assertEqual(123, optional("i", Params, nil, [int, {'==', 123}])),
+ ?assertEqual(123.0, optional("i", Params, nil, [float, {'<=', 123}])),
+ ?assertThrow({badreq, _}, optional("i", Params, nil, [int, {'<', 123}])),
+ ?assertEqual("Foo", optional("s", Params, nil, [{'=', "Foo"}])),
+ ?assertEqual("Foo", optional("s", Params, nil, [{'<>', "Bar"}])),
+ ?assertThrow({badreq, _}, optional("s", Params, nil, [{'=', "Boo"}])),
+ ?assertEqual("Foo", optional("s", Params, nil, [{in, ["Foo", "Bar"]}])),
+ ?assertThrow({badreq, _}, optional("s", Params, nil,
+ [{in, ["Bar", "Baz"]}])),
+ ?assertEqual("123", optional("i", Params, nil, [{'=<', "123"}])),
+ ?assertEqual("123", optional("i", Params, nil, [{'=<', "124"}])),
+ ?assertEqual(123.456, optional("f", Params, nil,
+ [float, {'>', 123}, {'<', 124}])),
+
+ ?assertEqual(123.456, optional("f", Params, nil,
+ [float, {'or', [{'<', 124}, {'>', 125}]}])),
+
+ %% Bad operators
+ ?assertExit({invalid_operator, badop},
+ optional("i", Params, nil, [{badop, "123"}])),
+ ?assertExit({invalid_operator, badop},
+ optional("i", Params, nil, [{'or', [{badop, "123"}]}])),
+
+ ok.

0 comments on commit 496a97d

Please sign in to comment.