An OTP library to validate data based on Ecto changeset library (Elixir).
This is a WIP library. The code may change at any time without notice.
Take this simple module:
-module(movie).
-export([changeset/1, changeset/2]).
-define(TYPES, #{name => binary, starring => binary}).
-define(PERMITTED, maps:keys(?TYPES)).
-define(REQUIRED, [name]).
changeset(Params) ->
changeset(#{}, Params).
changeset(Data, Params) ->
Changeset = changeset:cast({Data, ?TYPES}, Params, ?PERMITTED),
changeset:pipe(Changeset, [
changeset:validate_required(?REQUIRED)
% More validators here, e.g.:
% changeset:validate_change(name, fun(_Name) -> [] end)
% changeset:validate_format(name, "^[A-Z]")
% changeset:validate_member(starring, [<<"Mike">>, <<"Joe">>, <<"Robert">>])
% changeset:validate_not_member(starring, [<<"Me">])
]).
Now running
rebar3 shell
we can type the following:
% The name is missing, the changeset will be invalid
1> movie:changeset(#{}).
{changeset,[],
#{name => binary,starring => binary},
[name],
#{},#{},
[{name,{<<"is required">>,#{validation => is_required}}}],
[undefined,<<>>]}
% The name is not a binary, the changeset will be invalid
2> movie:changeset(#{name => foo}).
{changeset,[],
#{name => binary,starring => binary},
[name],
#{},
#{name => foo},
[{name,{<<"must be a binary">>,#{validation => is_binary}}}],
[undefined,<<>>]}
% The name is present and it's a binary, then the changeset will be valid
3> movie:changeset(#{name => <<"Erlang: The Movie">>}).
{changeset,[],
#{name => binary,starring => binary},
[name],
#{},
#{name => <<"Erlang: The Movie">>},
[],
[undefined,<<>>]}
% Get the valid changes
4> changeset:get_changes(v(3)).
#{name => <<"Erlang: The Movie">>}
Currently, this is the changeset record
-record(changeset,
{ fields = [] :: [field()]
, types = #{} :: #{field() := type()}
, required = [] :: [field()]
, data = #{} :: #{field() => term()}
, changes = #{} :: #{field() => term()}
, errors = [] :: [error()]
, empty_values = [undefined, <<>>] :: nonempty_list()
}).
and this are the available field types:
-type type() :: atom
| binary
| bitstring
| boolean
| float
| function
| {function, arity()}
| integer
| list
| map
| pid
| port
| record
| {record, Name :: atom()}
| {record, Name :: atom(), Size :: non_neg_integer()}
| reference
| tuple
.
The types are auto validated by the cast function.