exvalibur v0.10.0 Exvalibur View Source
Exvalibur
is the generator for blazingly fast validators of maps based on sets of predefined rules.
Generally speaking, one provides a list of rules in a format of a map:
rules = [
%{matches: %{currency_pair: "EURUSD"},
conditions: %{rate: %{min: 1.0, max: 2.0}}},
%{matches: %{currency_pair: "USDEUR"},
conditions: %{rate: %{min: 1.2, max: 1.3}}},
]
and calls Exvalibur.validator!/2
. The latter will produce a validator module
with as many clauses of validate/1
function as we have rules above (plus one
handling-all clause.) Once generated, the validate/1
function of the module
generated might be called directly on the input data, providing blazingly fast
validation based completely on pattern matching and guards.
One should privide at least one match or condition:
iex> rules = [%{matches: %{currency_pair: "EURUSD"}}]
...> Exvalibur.validator!(rules, module_name: Exvalibur.MatchValidator)
...> Exvalibur.MatchValidator.validate(%{currency_pair: "EURUSD", rate: 1.5})
{:ok, %{currency_pair: "EURUSD"}}
iex> rules = [%{conditions: %{rate: %{eq: 1.5}}}]
...> Exvalibur.validator!(rules, module_name: Exvalibur.ConditionValidator)
...> Exvalibur.ConditionValidator.validate(%{currency_pair: "EURGBP", rate: 1.5})
{:ok, %{rate: 1.5}}
iex> rules = [%{foo: :bar}]
...> try do
...> Exvalibur.validator!(rules, module_name: Exvalibur.RaisingValidator)
...> rescue
...> e in [Exvalibur.Error] ->
...> e.reason
...> end
%{empty_rule: %{foo: :bar}}
Exvalibur.validator!/2
Produces the validator module given the set of rules.
Options
module_name :: binary()
the name of the module to produce; when omitted, it will be looked up in current application optionsmerge :: boolean()
when true, the existing rules are taken from the module (if exists) and being merged against current rulesflow :: boolean()
when true, the underlying module generator usesFlow
to process an input
Example
iex> rules = [
...> %{matches: %{currency_pair: "EURUSD"},
...> conditions: %{rate: %{min: 1.0, max: 2.0}}}]
...> Exvalibur.validator!(rules, module_name: Exvalibur.Validator)
...> Exvalibur.Validator.validate(%{currency_pair: "EURUSD", rate: 1.5})
{:ok, %{currency_pair: "EURUSD", rate: 1.5}}
iex> Exvalibur.Validator.validate(%{currency_pair: "EURGBP", rate: 1.5})
:error
iex> Exvalibur.Validator.validate(%{currency_pair: "EURUSD", rate: 0.5})
:error
iex> rules = [
...> %{matches: %{currency_pair: "EURGBP"},
...> conditions: %{rate: %{min: 1.0, max: 2.0}}}]
...> Exvalibur.validator!(rules, module_name: Exvalibur.Validator)
...> Exvalibur.Validator.validate(%{currency_pair: "EURGBP", rate: 1.5})
{:ok, %{currency_pair: "EURGBP", rate: 1.5}}
iex> Exvalibur.Validator.validate(%{currency_pair: "EURUSD", rate: 1.5})
{:ok, %{currency_pair: "EURUSD", rate: 1.5}}
Unknown conditions
iex> rules = [
...> %{matches: %{currency_pair: "EURGBP"},
...> conditions: %{rate: %{perfect: true}}}]
...> try do
...> Exvalibur.validator!(rules, module_name: Exvalibur.Validator)
...> rescue
...> e in [Exvalibur.Error] ->
...> e.reason
...> end
%{unknown_guard: :perfect}
When an unknown guard is passed to the rules conditions, compile-time error is produced
Return value
Generated validate/1
function returns either :error
or {:ok, map()}
.
In a case of successful validation, the map contained values that were indeed validated.
Note that in the following example the value for any
key is not returned.
iex> rules = [
...> %{matches: %{currency_pair: "EURGBP"},
...> conditions: %{
...> rate: %{min: 1.0, max: 2.0},
...> source: %{one_of: ["FOO", "BAR"]}}}]
...> Exvalibur.validator!(rules, module_name: Exvalibur.Validator, merge: false)
...> Exvalibur.Validator.validate(%{currency_pair: "EURGBP", any: 42, rate: 1.5, source: "FOO"})
{:ok, %{currency_pair: "EURGBP", rate: 1.5, source: "FOO"}}
iex> Exvalibur.Validator.validate(%{currency_pair: "EURUSD", any: 42, rate: 1.5, source: "BAH"})
:error
Module-based validators
iex> defmodule Validator do
...> use Exvalibur, rules: [
...> %{
...> matches: %{currency_pair: <<"EUR", _ :: binary>>},
...> conditions: %{foo: %{min: 0, max: 100}},
...> guards: %{num: num > 0 and num < 100}}]
...> end
iex> Validator.validate(%{currency_pair: "EURUSD", foo: 50, num: 50})
{:ok, %{currency_pair: "EURUSD", foo: 50, num: 50}}
iex> Validator.validate(%{currency_pair: "USDEUR", foo: 50, num: 50})
:error
iex> Validator.validate(%{currency_pair: "EURUSD", foo: -50, num: 50})
:error
iex> Validator.validate(%{currency_pair: "EURUSD", foo: 50, num: -50})
:error
Custom validations
With module-based validators, one might add custom validations to the implementation:
iex> defmodule CustomValidator do
...> use Exvalibur, rules: [
...> %{conditions: %{foo: %{min: 0, max: 10}}}]
...>
...> @impl Exvalibur.Validatable
...> def custom_validate(%{foo: foo}),
...> do: if foo == 42, do: {:ok, %{foo: 42}}, else: :error
...> end
iex> CustomValidator.validate(%{foo: 5})
{:ok, %{foo: 5}}
iex> CustomValidator.validate(%{foo: 50})
:error
iex> CustomValidator.validate(%{foo: 42})
{:ok, %{foo: 42}}