Skip to content

bluzky/skema

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

18 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Skema

Phoenix request params validation library.

Build Status Coverage Status Hex Version docs

Why Skema

- Reduce code boilerplate 
- Shorter schema definition
- Default function which generate value each casting time
- Custom validation functions
- Custom parse functions

Installation

Available in Hex, the package can be installed by adding skema to your list of dependencies in mix.exs:

def deps do
  [
    {:skema, "~> 0.1"}
  ]
end

Usage

Process order

Cast data -> validate casted data -> transform data

use Skema
defschema IndexParams do
    field :keyword, :string
    field :status, :string, required: true
    field :group_id, :integer, number: [greater_than: 0]
    field :name, :string
end

def index(conn, params) do
    with {:ok, better_params} <- Skema.cast(params, IndexParams) do
        # do anything with your params
    else
        {:error, errors} -> # return params error
    end
end

Define schema

use Skema

defschema IndexParams do
    field :keyword, :string
    field :status, :string, required: true
    field :group_id, :integer, number: [greater_than: 0]
    field :name, :string
end

Define a field using macro @spec field(field_name :: atom(), type :: term(), opts \\ [])

  • type: Skema support same data type as Ecto. I borrowed code from Ecto

Supported options:

  • default: default value or default function
  • cast_func: custom cast function
  • number, format, length, in, not_in, func, required, each are available validations
  • from: use value from another field
  • as: alias key you will receive from Skema.cast if casting is succeeded

Default value

You can define a default value for a field if it's missing from the params.

field :status, :string, default: "pending"

Or you can define a default value as a function. This function is evaluated when Skema.cast gets invoked.

field :date, :utc_datetime, default: &Timex.now/0

Custom cast function

You can define your own casting function, skema provide cast_func option. Your cast_func must follows this spec

1. Custom cast fuction accept value only

fn(any) :: {:ok, any} | {:error, binary} | :error
def my_array_parser(value) do
    if is_binary(value) do
        ids = 
            String.split(value, ",")
            |> Enum.map(&String.to_integer(&1))
        
        {:ok, ids}
    else
        {:error, "Invalid string"
    end
end

defschema Sample do
   field :user_id, {:array, :integer}, cast_func: &my_array_parser/1
end

This is a demo parser function.

Nested schema

With Skema you can parse and validate nested map and list easily

defschema Address do
    field :street, :string
    field :district, :string
    field :city, :string
end

defschema User do
    field :name, :string,
    field :email, :string, required: true
    field :addresses, {:array, Address}
end

Validation

Skema uses Valdi validation library. You can read more about Valdi here Basically it supports following validation

  • validate inclusion/exclusion

  • validate length for string and enumerable types

  • validate number

  • validate string format/pattern

  • validate custom function

  • validate required(not nil) or not

  • validate each array item

defschema Product do field :sku, :string, required: true, length: [min: 6, max: 20] field :name, :string, required: true, field :quantity, :integer, number: [min: 0], field :type: :string, in: ~w(physical digital), field :expiration_date, :naive_datetime, func: &my_validation_func/1, field :tags, {:array, :string}, each: [length: [max: 50]] end


### Dynamic required
- Can accept function or `{module, function}` tuple
- Only support 2 arity function


```elixir
def require_email?(value, data), do: is_nil(email.phone)

....

field :email, :string, required: {__MODULE__, :require_email?}

Validate array item

Support validate array item with :each option, each accept a list of validators

...
    field :values, {:array, :number}, each: [number: [min: 20, max: 50]]
...

Thank you

If you find a bug or want to improve something, please send a pull request. Thank you!

About

Define, cast & validate data again schema

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages