Para behaviour (Para v0.3.0)
Para is an Elixir library that provides structured and declarative way to parse and validate parameters.
Para uses Ecto under the hood and therefore inherits most of its utilities such as changeset and built-in validators.
Usage
Let's imagine that you have a controller named Web.UserController
and
wanted to validate the parameters for its :create
and :update
actions.
First, let's define your parameters schema.
defmodule Web.UserPara do
use Para
validator :create do
required :name, :string
required :age, :integer
required :email, :string
optional :phone, :string
end
validator :update do
required :name, :string
required :age, :integer
required :email, :string
optional :phone, :string
end
end
This will generate two validate/2
functions for your module
with action name
and params
as arguments.
defmodule Web.UserController do
use Web, :controller
alias Web.UserPara
def create(conn, params) do
with {:ok, data} <- UserPara.validate(:create, params) do
# ...
end
end
def update(conn, params) do
with {:ok, data} <- UserPara.validate(:update, params) do
# ...
end
end
end
The validate/2
function will return either an {:ok, map}
or {:error, changeset}
tuple.
Inline validators
Inline validator is a convenient way to validate your fields. This is
especially useful when you need to perform some basic validation
using Ecto.Changeset
's built-in validators.
defmodule UserPara do
use Para
validator :update do
required :name, :string, validator: {:validate_length, [min: 3, max: 100]}
end
end
You can also use custom inline validators by supplying the function name
as an atom. Similar to most Ecto's built-in validators, the function will
receive changeset
, key
, and opts
as the arguments.
defmodule UserPara do
use Para
validator :update do
required :age, :string, validator: :validate_age
required :gender, :string, validator: {:validate_gender, [allow: :non_binary]}
end
def validate_age(changeset, key, opts) do
# ...
end
end
Callback validator
Sometimes, you might want to use custom validators or need to perform
additional data manipulations. For this, you can use the callback/1
macro.
The callback/1
macro will always be the last function to be called
after the validator has parsed and validated the parameters.
defmodule Web.UserPara do
use Para
validator :create do
required :name, :string
required :age, :integer
required :email, :string
optional :phone, :string
callback :create_validators
end
def create_validators(changeset, params) do
changeset
|> format_email(params)
|> format_phone(params)
|> validate_age()
end
def format_email(changeset, params) do
# ...
end
def format_phone(changeset, params) do
# ...
end
def validate_age(changeset) do
# ...
end
end
Link to this section Summary
Functions
Define a custom callback function that will be called to perform any additional manipulation to the changeset or parameters.
Define an embedded array of maps field.
Define an embedded map field
Define an optional field.
Define a required field.
Define a validator schema with an action name and field definitions.
Link to this section Types
data()
Specs
spec()
Specs
Specs
t() :: {:ok, map()} | {:error, Ecto.Changeset.t()}
Link to this section Callbacks
spec(atom, map)
Specs
Returns basic spec.
This is useful when you need to build a custom changeset, or when you just need the basic structure of your schema.
Also see: Ecto.Changeset.change/2
Examples
defmodule OrderPara do
use Para
validator :create do
required :title
required :data, {:array, :map}
end
end
def changeset(:new, params) do
spec = __MODULE__.spec(:create, params)
Ecto.Changeset.change(spec.data, spec.types)
end
validate(atom, map)
Specs
validate(atom(), map()) :: {:ok, data()} | {:error, Ecto.Changeset.t()}
Parse and validate parameters for a given action.
The function will cast all the returned map keys into atoms except for embedded map or list.
Examples
defmodule OrderPara do
use Para
validator :create do
required :title
required :data, {:array, :map}
end
end
# Validate action with parameters
OrderPara.validate(:create, %{
"title" => "test"
"data" => [%{"color" => "black", "material" => "cotton"}]
})
#=> {:ok, %{
title: "test"
data: [%{"color" => "black", "material" => "cotton"}]
}}
Link to this section Functions
Define a custom callback function that will be called to perform any additional manipulation to the changeset or parameters.
The callback function must accept two arguments namely changeset
and
params
and return an Ecto.Changeset
struct.
Examples
# Define callback function to be called
validator :create do
callback :validate_price
end
def validate_price(changeset, params) do
#...
end
Define an embedded array of maps field.
It accepts similar schema definition like validator/2
.
Examples
defmodule OrderPara do
use Para
validator :create do
embeds_many :items do
required :title
required :price, :float
end
end
end
Define an embedded map field
It accepts similar schema definition like validator/2
.
Examples
defmodule ParentPara do
use Para
validator :create do
embeds_one :child do
optional :name, :string
optional :age, :integer
end
end
end
Define an optional field.
Similar to required/3
, it also accepts the same Options
Define a required field.
Options
:default
- Assign a default value if the not set by input parameters:validator
- Define either one of the built-in Ecto.Changeset's validators or use your own custom inline validator. Refer: Custom inline validator:droppable
- Drop the field when the key doesn't exist in parameters. This is useful when you need to perform partial update by leaving out certain fields.
Custom inline validator
You can define your own validator as such:
def validate_country(changeset, field) do
# ...
end
Then use it as an inline validator for your field
validator :create do
required :country, :string, [validator: :validate_country]
end
You can also supply options with your custom inline validator
validator :create do
required :country, :string, [validator: {:validate_country, region: :asia}]
end
Define a validator schema with an action name and field definitions.
This will generate a new function called validate/2
with the action name
and params
as the arguments.
iex> defmodule UserPara do
...> use Para
...>
...> validator :create do
...> required :name
...> end
...> end
...>
...> UserPara.validate(:create, %{"name" => "Syamil MJ"})
{:ok, %{name: "Syamil MJ"}}