Skip to content

Instantly share code, notes, and snippets.

@jadeallenx
Created February 10, 2012 22:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jadeallenx/1793796 to your computer and use it in GitHub Desktop.
Save jadeallenx/1793796 to your computer and use it in GitHub Desktop.
Erlang parameter validation
% Post/Query string parameter validation.
%
% Mark Allen, mrallen1@yahoo.com
%
% Specs [{{name, "param_name"}, {required, true}, {type, function}}, ... ]
% Input [{"name", "value}, ...] just you'd get from post or query string parameters
-module(t).
-export([test/0, test1/0, test2/0]).
find_spec(P, S) ->
case S of
{{name, P}, R, V} ->
{{name, P}, R, V};
{{name, P}, V} ->
{{name, P}, {required, false}, V};
{_, _, _} ->
skip;
{_, _} ->
skip
end.
validate_required(I, S) ->
InputParams = [ Name || {Name, _Value} <- I ],
R = [ case Bool of
true ->
ParamName;
false ->
false
end || {{_, ParamName}, {required, Bool}, {_, _}} <- S],
ReqParams = lists:filter(fun(A) -> A =/= false end, R),
V = [ lists:member(N, InputParams) || N <- ReqParams ],
[ case Bool of
true ->
{ok, Name};
false ->
{missingreq, Name}
end || {Name, Bool} <- lists:zip(ReqParams, V) ].
validate_specification(S, Acc) ->
case S of
{{name, Name}, {required, Bool}, {Type, F}} ->
Member = lists:member(Type, [int, string, atom, list, tuple]),
if
is_list(Name), is_boolean(Bool), Member == true, is_function(F) ->
{ok, Name};
true ->
{badspec, Name}
end;
{{name, Name}, {Type, F}} ->
validate_specification({{name, Name}, {required, false}, {Type, F}}, Acc);
[H|T] ->
validate_specification(T, [validate_specification(H, Acc) | Acc]);
[] ->
lists:filter(fun(A) -> is_tuple(A) end, Acc)
end.
validate_params(Input, Spec) ->
[ case lists:filter(fun(A) -> A =/= skip end, [ find_spec(Name, S) || S <- Spec ]) of
[H|_T] ->
{{name, Name}, {required, _Bool}, {Type, F}} = H,
try
{ok, Coerce} = coerce_type(Type, Value),
case F(Coerce) of
true ->
{ok, Name, Coerce};
false ->
{badparam, Name, Value}
end
catch
_:_ ->
{badparam, Name, Value}
end;
[] ->
{missingspec, Name}
end || {Name, Value} <- Input ].
coerce_type(Type, Value) ->
case Type of
int when is_list(Value) ->
try
V = list_to_integer(Value),
{ok, V}
catch
_:_ ->
{badparam, Value}
end;
int when is_integer(Value) ->
{ok, Value};
string when is_list(Value) ->
{ok, Value};
binary when is_list(Value) ->
try
V = list_to_binary(Value),
{ok, V}
catch
_:_ ->
{badparam, Value}
end;
binary when is_binary(Value) ->
{ok, Value};
tuple when is_list(Value) ->
try
V = list_to_tuple(Value),
{ok, V}
catch
_:_ ->
{badparam, Value}
end;
tuple when is_tuple(Value) ->
{ok, Value};
list when is_list(Value) ->
{ok, Value}
end.
test() ->
Spec = [
{{name, "foo"}, {required, true}, {int, fun(A) -> A > 50 end}},
{{name, "bar"}, {required, false}, {string, fun(A) -> A =:= "quux" end}},
{{name, "guz"}, {required, crap}, {string, fun(A) -> A =:= "quux" end}},
{{name, gurz}, {required, crap}, {string, fun(A) -> A =:= "quux" end}},
{{name, "baz"}, {int, fun(A) -> A =:= 0 end}},
{{name, "bak"}, {crap, fun(A) -> A =:= 0 end}},
{{name, "bez"}, {string, crap}},
{{name, "vap"}, {int, fun(A) -> A < 0 end}}
% {{name, "zod"}},
% {{name, "zux"}, {int}},
% {{name, "faz"}, {fun(_A) -> true end}}
],
validate_specification(Spec, []).
test1() ->
Spec = [
{{name, "foo"}, {required, true}, {int, fun(A) -> A > 50 end}},
{{name, "bar"}, {required, false}, {string, fun(A) -> A =:= "quux" end}},
{{name, "baz"}, {int, fun(A) -> A =:= 0 end}},
{{name, "vap"}, {int, fun(A) -> A < 0 end}}
],
Input = [{"foo", "abc"}, {"bar", "quux"}, {"weasel", "bat"}],
validate_params(Input, Spec).
test2() ->
Spec = [
{{name, "foo"}, {required, true}, {int, fun(A) -> A > 50 end}},
{{name, "bar"}, {required, true}, {string, fun(A) -> A =:= "quux" end}},
{{name, "baz"}, {int, fun(A) -> A =:= 0 end}},
{{name, "vap"}, {int, fun(A) -> A < 0 end}}
],
Input = [{"foo", "abc"}, {"barf", "quux"}, {"baz", "bat"}],
validate_required(Input, Spec).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment