-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#1] field-level validators documented
- Loading branch information
Showing
7 changed files
with
111 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
# Validators | ||
|
||
`cloak` provides two ways of data validation: field-level and datastructure-level. | ||
|
||
## Field-level validation | ||
|
||
By default `cloak` threats all incoming values as valid. | ||
|
||
You may declare a function called `on_validate_FIELD_NAME` with the following spec: | ||
|
||
```erlang | ||
-spec on_validate_FIELD_NAME(Value :: term()) -> | ||
{ok, MaybeNewValue :: term()} | {error, Reason :: term()} | no_return(). | ||
``` | ||
|
||
Another words, your function should be of arity `1`, it should take an argument and validate it. If everything is okay, your function should return the `{ok, Value}` tuple, and `Value` will be used as a field value. If your function will return the `{error, Reason}` tuple, `cloak` will try to yield error log message (if [logging suppression](compile-time-options.md) is not enabled) and will throw [runtime error](runtime-errors.md). You also may throw an error yourself since `cloak` will do this for you anyway. In this case `cloak` will not yield a log message even with logging suppression disabled. | ||
|
||
Lets take a look at [priv_validated.erl](/test/priv/priv_validated.erl) test module. | ||
|
||
```erlang | ||
-module(priv_validated). | ||
-compile({parse_transform, cloak_transform}). | ||
|
||
-record(?MODULE, { | ||
a, | ||
a1, % no validator, required | ||
b = atom, | ||
b1 = undefined % no validator, optional | ||
}). | ||
|
||
|
||
on_validate_a(Value) when Value > 100 -> | ||
{ok, Value}; | ||
|
||
on_validate_a(_) -> | ||
error(badarg). | ||
|
||
|
||
on_validate_b(Value) when Value =/= invalid_atom -> | ||
{ok, Value}; | ||
|
||
on_validate_b(_) -> | ||
error(badarg). | ||
``` | ||
|
||
We have two couples of fields here: two required (`a`, `a1`) and two optional (`b`, `b1`). Fields `a` and `b` have validator functions declared for them (`on_validate_a/1` and `on_validate_b` respectively). Field `a` should be greater then `100`, and field `b` should be any value except `invalid_atom`. | ||
|
||
As usual, `cloak` will respect [field type](field-types.md) declaration and will throw an error if you will try to create a structure with missing required fields: | ||
|
||
```erlang | ||
1> priv_validated:new(#{a => 150}). | ||
** exception error: bad argument | ||
in function priv_validated:new_required/3 | ||
in call from priv_validated:new/1 | ||
``` | ||
|
||
Field `a1` have no validator declared, but it is still required. | ||
|
||
You may create a struct with any value of field `a1`, but field `a` is protected with validator function: | ||
|
||
```erlang | ||
2> priv_validated:new(#{a => 15, a1 => {any, term, [i, want]}}). | ||
** exception error: bad argument | ||
in function priv_validated:on_validate_a/1 (/Users/shizz/code/cloak/_build/test/lib/cloak/test/priv/priv_validated.erl, line 16) | ||
in call from priv_validated:a/2 | ||
in call from priv_validated:new_required/3 | ||
in call from priv_validated:new/1 | ||
``` | ||
|
||
Same thing happens with optional field that is protected with validator function: | ||
|
||
```erlang | ||
3> priv_validated:new(#{a => 150, a1 => {any, term, [i, want]}, b => invalid_atom}). | ||
** exception error: bad argument | ||
in function priv_validated:on_validate_b/1 (/Users/shizz/code/cloak/_build/test/lib/cloak/test/priv/priv_validated.erl, line 23) | ||
in call from priv_validated:b/2 | ||
in call from priv_validated:new_optional/3 | ||
in call from priv_validated:new/1 | ||
``` | ||
|
||
If you will provide valid data for your datastructure, `cloak` will return the struct: | ||
|
||
```erlang | ||
4> priv_validated:new(#{a => 150, a1 => {any, term, [i, want]}, b => some_atom}). | ||
{priv_validated,150,{any,term,[i,want]},some_atom,undefined} | ||
``` | ||
|
||
Note, that default values for optional fields are not validated by `cloak` in any case. In the example above field `b` have a validator with the restriction of `invalid_atom` value, but if you will set the default value of field `b` to `invalid_atom`, it will be set successfully if no field `b` value will exist in `Module:new/1` argument. | ||
|
||
You also may want to check [priv_validated.erl](/test/priv/priv_validated.erl) modified source code to understand what happens when you're declaring field-level validator: | ||
|
||
```erlang | ||
validate_a1(Var_value_0) -> | ||
{ok,Var_value_0}. | ||
validate_a(Var_value_0) -> | ||
on_validate_a(Var_value_0). | ||
validate_b1(Var_value_0) -> | ||
{ok,Var_value_0}. | ||
validate_b(Var_value_0) -> | ||
on_validate_b(Var_value_0). | ||
``` | ||
|
||
On every set operation `cloak` calls generated function `validate_FIELD_NAME/1`. This function has the same spec as user-declared validators should have. If `cloak` find user-dedeclared validator in module source code, it modifies the code of `validate_FIELD_NAME/1` function to pass the argument to user-declared validator. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
{erl_opts, [debug_info]}. | ||
{src_dirs, ["src", "examples"]}. | ||
{src_dirs, ["src"]}. | ||
{deps, []}. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters