Packages
ecto
0.2.6
3.14.1
3.14.0
3.13.6
3.13.5
3.13.4
3.13.3
3.13.2
3.13.1
3.13.0
3.12.6
3.12.5
3.12.4
3.12.3
3.12.2
3.12.1
3.12.0
3.11.2
3.11.1
3.11.0
3.10.3
3.10.2
3.10.1
3.10.0
3.9.6
3.9.5
3.9.4
3.9.3
3.9.2
3.9.1
3.9.0
3.8.4
3.8.3
3.8.2
3.8.1
3.8.0
3.7.2
3.7.1
3.7.0
3.6.2
3.6.1
3.6.0
3.5.8
3.5.7
3.5.6
3.5.5
3.5.4
3.5.3
3.5.2
3.5.1
3.5.0
3.5.0-rc.1
3.5.0-rc.0
3.4.6
3.4.5
3.4.4
3.4.3
3.4.2
3.4.1
3.4.0
3.3.4
3.3.3
3.3.2
3.3.1
3.3.0
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
3.0.0-rc.1
3.0.0-rc.0
2.2.12
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.2.0-rc.1
2.2.0-rc.0
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.1.0-rc.5
2.1.0-rc.4
2.1.0-rc.3
2.1.0-rc.2
2.1.0-rc.1
2.1.0-rc.0
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
2.0.0-rc.6
2.0.0-rc.5
2.0.0-rc.4
2.0.0-rc.3
2.0.0-rc.2
2.0.0-rc.1
2.0.0-rc.0
2.0.0-beta.2
2.0.0-beta.1
2.0.0-beta.0
1.1.9
1.1.8
1.1.7
1.1.6
1.1.5
1.1.4
1.1.3
1.1.2
1.1.1
1.1.0
1.0.7
1.0.6
1.0.5
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
0.16.0
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.1
0.13.0
0.12.1
0.12.0
0.12.0-rc
0.11.3
0.11.2
0.11.1
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
0.9.0
0.8.1
0.8.0
0.7.2
0.7.1
0.7.0
0.6.0
0.5.1
0.5.0
0.4.0
0.3.0
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.0
A toolkit for data mapping and language integrated query for Elixir
Current section
Files
Jump to
Current section
Files
lib/ecto/validator.ex
defmodule Ecto.Validator do
@moduledoc """
Validates a given struct or dict given a set of predicates.
Ecto.Validator.struct(user,
name: present() when on_create?(user),
age: present(message: "must be present"),
age: greater_than(18),
also: validate_other
)
Validations are passed as the second argument in the attribute-predicate
format. Each predicate can be filtered via the `when` operator. Note `when`
here is not limited to only guard expressions.
The predicates above are going to receive the attribute being validated
and its current value as argument. For example, the `present` predicate
above is going to be called as:
present(:name, user.name)
present(:age, user.age, message: "must be present")
The validator also handles a special key `:also`, which is used to pipe
to predicates without a particular attribute. Instead, such predicates
receive the struct as argument. In this example, `validate_other` will
be invoked as:
validate_other(user)
Note all predicates must return a keyword list, with the attribute error
as key and the validation message as value.
A handful of predicates can be found at `Ecto.Validator.Predicates`.
"""
@doc """
Validates a given dict given a set of predicates.
"""
@spec dict(Macro.t, Keyword.t) :: Macro.t
defmacro dict(value, opts) when is_list(opts) do
process opts, value, fn var, attr ->
quote do: Dict.get(unquote(var), unquote(attr))
end
end
@doc """
Validates a given dict, with binary keys, given a set of predicates.
"""
@spec bin_dict(Macro.t, Keyword.t) :: Macro.t
defmacro bin_dict(value, opts) when is_list(opts) do
process opts, value, fn var, attr ->
quote do: Dict.get(unquote(var), unquote(Atom.to_string(attr)))
end
end
@doc """
Validates a given struct given a set of predicates.
"""
@spec struct(Macro.t, Keyword.t) :: Macro.t
defmacro struct(value, opts) when is_list(opts) do
process opts, value, fn var, attr ->
quote do: Map.get(unquote(var), unquote(attr))
end
end
defp process([], _value, _getter), do: nil
defp process(opts, value, getter) do
var = quote do: var
validations =
Enum.reduce(opts, quote(do: %{}), &process_each(&1, &2, var, getter))
quote do
unquote(var) = unquote(value)
if map_size(errors = unquote(validations)) > 0 do
errors
end
end
end
defp process_each({:also, function}, acc, var, _getter) do
quotation = fn acc, predicate ->
quote do
Map.merge unquote(acc), unquote(predicate), fn _k, v1, v2 ->
v1 ++ v2
end
end
end
handle_ops function, acc, quotation, fn call ->
Macro.pipe(var, call, 0)
end
end
defp process_each({attr, function}, acc, var, getter) do
quotation = fn acc, predicate ->
quote do
acc = unquote(acc)
case unquote(predicate) do
nil -> acc
msg -> Map.update(acc, unquote(attr), [msg], &[msg|&1])
end
end
end
handle_ops function, acc, quotation, fn call ->
Macro.pipe(getter.(var, attr), call, 0)
end
end
defp handle_ops({:when, _, [left, right]}, acc, quotation, builder) do
quote do
if unquote(right) do
unquote(handle_and(left, acc, quotation, builder))
else
unquote(acc)
end
end
end
defp handle_ops(other, acc, quotation, builder) do
handle_and(other, acc, quotation, builder)
end
defp handle_and({:and, _, [left, right]}, acc, quotation, builder) do
handle_and(left, quotation.(acc, builder.(right)), quotation, builder)
end
defp handle_and(other, acc, quotation, builder) do
quotation.(acc, builder.(other))
end
end