View Source Drops.Contract behaviour (drops v0.1.0)
Drops.Contract can be used to extend your module with data validation capabilities.
Summary
Callbacks
Validates the given data
against the schema defined in the module.
Functions
Define validation rules that are applied to the data validated by the schema.
Define a default schema for the contract.
Define schemas for the contract.
Callbacks
Validates the given data
against the schema defined in the module.
Returns {:ok, validated_data}
.
Examples
iex> defmodule UserContract do
...> use Drops.Contract
...>
...> schema do
...> %{
...> required(:name) => type(:string),
...> required(:age) => type(:integer)
...> }
...> end
...> end
iex> {:error, errors} = UserContract.conform("oops")
iex> Enum.map(errors, &to_string/1)
["must be a map"]
iex> UserContract.conform(%{name: "Jane", age: 48})
{:ok, %{name: "Jane", age: 48}}
iex> {:error, errors} = UserContract.conform(%{name: "Jane", age: "not an integer"})
iex> Enum.map(errors, &to_string/1)
["age must be an integer"]
Functions
Define validation rules that are applied to the data validated by the schema.
Examples
iex> defmodule UserContract do
...> use Drops.Contract
...>
...> schema do
...> %{
...> required(:email) => maybe(:string),
...> required(:login) => maybe(:string)
...> }
...> end
...>
...> rule(:either_login_or_email, %{email: nil, login: nil}) do
...> {:error, "email or login must be present"}
...> end
...> end
iex> UserContract.conform(%{email: "jane@doe.org", login: nil})
{:ok, %{email: "jane@doe.org", login: nil}}
iex> UserContract.conform(%{email: nil, login: "jane"})
{:ok, %{email: nil, login: "jane"}}
iex> {:error, errors} = UserContract.conform(%{email: nil, login: nil})
iex> Enum.map(errors, &to_string/1)
["email or login must be present"]
Define a default schema for the contract.
Simple schema
iex> defmodule UserContract do
...> use Drops.Contract
...>
...> schema do
...> %{
...> required(:name) => type(:string),
...> required(:age) => type(:integer)
...> }
...> end
...> end
iex> UserContract.conform(%{name: "John", age: 21})
{:ok, %{name: "John", age: 21}}
Define schemas for the contract.
Nested atomized schema
iex> defmodule UserContract do
...> use Drops.Contract
...>
...> schema(atomize: true) do
...> %{
...> required(:user) => %{
...> required(:name) => type(:string, [:filled?]),
...> required(:age) => type(:integer),
...> required(:address) => %{
...> required(:city) => type(:string, [:filled?]),
...> required(:street) => type(:string, [:filled?]),
...> required(:zipcode) => type(:string, [:filled?])
...> }
...> }
...> }
...> end
...> end
iex> {:error, errors} = UserContract.conform(%{
...> "user" => %{
...> "name" => "John",
...> "age" => 21,
...> "address" => %{
...> "city" => "New York",
...> "street" => "",
...> "zipcode" => "10001"
...> }
...> }
...> })
iex> Enum.map(errors, &to_string/1)
["user.address.street must be filled"]
iex> UserContract.conform(%{
...> "user" => %{
...> "name" => "John",
...> "age" => 21,
...> "address" => %{
...> "city" => "New York",
...> "street" => "Central Park",
...> "zipcode" => "10001"
...> }
...> }
...> })
{:ok,
%{
user: %{
name: "John",
address: %{city: "New York", street: "Central Park", zipcode: "10001"},
age: 21
}
}}
Reusing schemas
iex> defmodule UserContract do
...> use Drops.Contract
...>
...> schema(:address) do
...> %{
...> required(:street) => string(:filled?),
...> required(:city) => string(:filled?),
...> required(:zip) => string(:filled?),
...> required(:country) => string(:filled?)
...> }
...> end
...>
...> schema do
...> %{
...> required(:name) => string(),
...> required(:age) => integer(),
...> required(:address) => @schemas.address
...> }
...> end
...> end
iex> UserContract.conform(%{
...> name: "John",
...> age: 21,
...> address: %{
...> street: "Main St.",
...> city: "New York",
...> zip: "10001",
...> country: "USA"
...> }
...> })
{:ok,
%{
name: "John",
address: %{
zip: "10001",
street: "Main St.",
city: "New York",
country: "USA"
},
age: 21
}}
iex> {:error, errors} = UserContract.conform(%{
...> name: "John",
...> age: "21",
...> address: %{
...> street: "Main St.",
...> city: "",
...> zip: "10001",
...> country: "USA"
...> }
...> })
iex> Enum.map(errors, &to_string/1)
["address.city must be filled", "age must be an integer"]