Contrak (Contrak v0.3.0) View Source

Contrak helps to validate data with given schema:

task_schema = %{
  title: [type: :string, required: true, length: [min: 20]],
  description: :string,
  tag: [type: {:array, :string}, default: []]
  point: [type: :integer, number: [min: 0]],
  due_date: [type: NaiveDateTime, default: &seven_day_from_now/0] ,
  assignee: [type: {:array, User}, required: true],
  attachments: [
    type:
    {:array,
     %{
       name: [type: :string, required: true],
       url: :string
     }}
  ]
}

case Contrak.validate(data, task_schema) do
  {:ok, valid_data} ->
      # do your logic

  {:error, errors} ->
      IO.inspect(errors)
end

Contrak also do:

  • Support nested type
  • Clean not allowed fields

NOTES: Contract only validate data, not cast data

Schema definition

Contrak provide a simple schema definition: <field_name>: [type: <type>, required: <true|false>, default: <value|function>, [...validation]]

Shorthand form if you only check for type: <field_name>: <type>

  • type is all types supported by Valdi.validate_type, and extended to support nested type. Nested type is just another schema.

  • default: could be a function func/0 or a value. Default function is invoked for every time validate is called. If not set, default value is nil

  • required: if set to true, validate if a field does exist and not nil. Default is false.

  • validations: all validation support by Valdi, if value is nil then all validation is skip

For more details, please check document for Contrak.Schema

Validations

Contrak uses Valdi to validate data. So you can use all validation from Valdi. This is list of available validation for current version of Valdi:

  • Validate type
  • Validate required
  • Validate in|not_in enum
  • Valiate length for string, enumerable
  • Validate number
  • Validate string against regex pattern
  • Custom validation function

Please check Valdi document for more details

Another example

@update_user_contract %{
  user: [type: User, required: true],
  attributes: [type: %{
    email: [type: :string],
    status: [type: :string, in: ~w(active in_active)]
    age: [type: :integer, number: [min: 10, max: 80]],
  }, required: true]
}

def update_user(contract) do
  with {:ok, validated_data} do
     validated_data.user
     |> Ecto.Changeset.change(validated_data.attributes)
     |> Repo.update
  else
    {:error, errors} -> IO.inspect(errors)
  end
end

Link to this section Summary

Functions

Validate data against given schema

Link to this section Functions

Specs

validate(data :: map(), schema :: map()) ::
  {:ok, map()} | {:error, errors :: map()}

Validate data against given schema