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

Link to this callback

conform(data)

View Source (since 0.1.0)
@callback conform(data :: map()) :: {:ok, map()} | {:error, list()}

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"]
Link to this callback

conform(data, keys)

View Source (since 0.1.0)
@callback conform(data :: map(), keys :: list()) :: {:ok, map()} | {:error, list()}
@callback conform(data :: map(), schema :: Types.Map) :: {:ok, map()} | {:error, list()}
Link to this callback

conform(data, schema, keyword)

View Source (since 0.1.0)
@callback conform(data :: map(), schema :: Types.Map, keyword()) ::
  {:ok, map()} | {:error, list()}

Functions

Link to this macro

rule(name, input, list)

View Source (since 0.1.0) (macro)

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"]
Link to this macro

schema(list)

View Source (since 0.1.0) (macro)

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}}
Link to this macro

schema(name, list)

View Source (since 0.1.0) (macro)

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"]
Link to this macro

schema(name, opts, list)

View Source (since 0.1.0) (macro)