Draft is a library for building structs with runtime type validation. Creating and validating structs has never been easier. Inspired by Ecto.Schema
To use Draft in your project, add this to your Mix dependencies:
{:draft, "~> 0.1.0"},
To define a Simple blueprint struct
defmodule StructType do
use Draft.Struct
# Define your struct.
schema do
#Define a field with type string
field :name, :string
field :id, :uuid
#Define a field with default value
field :age, :number, default: 10
field :amount, :float
end
end
Nested blueprints
defmodule Nested do
use Draft.Struct
schema do
field :value, :number
end
end
defmodule Typed do
use Draft.Struct
schema do
# Nested field
field :nested, Nested, default: nil
field :name, :string, default: "my name"
end
end
Draft structs can inherit fields from other schema, their types and validation rules, using the :extends
option
:extends
Draft module or list of blueprint modules for inheriting from muliple bases
defmodule Super do
use Draft.Struct
schema do
field :super, :number
end
end
defmodule Base do
schema extends: Super do
field :base, :number
end
end
defmodule Child do
use Draft.Struct
schema extends: Base do
field :child, :number
end
end
%Child{super: 1, base: 2, child: 3}
when inheriting from multiple bases, the next module in the list always overwrites any previously defined fields
schema extends: [Base, Super]
the module Super
will overwrite any fields already defined in Base
defmodule Super do
use Draft.Struct
schema do
field :super, :number
end
end
defmodule Base do
use Draft.Struct
schema do
field :base, :number
end
end
defmodule Child do
use Draft.Struct
schema extends: [Base, Super] do
field :child, :number
end
end
%Child{super: 1, base: 2, child: 3}
Sometime users may wish to overwrite certain fields with custom rules or type,
this can be done with the overwrite
field option
defmodule Super do
use Draft.Struct
schema do
field :super, :number
end
end
defmodule Base do
use Draft.Struct
schema do
field :base, :number
end
end
defmodule Child do
use Draft.Struct
schema extends: [Base, Super] do
field :base, :string, overwrite: true
end
end
%Child{super: 1, base: "2", child: 3}
Draft defines a constructor new
method to create
struct from map or list. Note: the new method will throw if
validation of field type fails
data = StructType.new(%{...})
Draft defines a constructor cast
method to struct but unlike
the new
method is returns the usual {:ok, value}
or {:error, reason}
{:ok, data} = StructType.cast(%{...})
{:error, reason} = StructType.cast(123)
Draft defines a constructor from_struct
just like new
but made to look like
the familiar Map.from_struct
is uses the new
method and will throw if
validation fails
data = StructType.from_struct(%StructType{...})
Draft defines a dump
method to dump the data to simple Map that can be easily
serializable
{:ok, data} = StructType.dump(%StructType{...})
Defining required fields is simple required fields cannot be nil
There are multiple way of going about this
defmodule StructType do
use Draft.Struct
# This will make all fields required.
schema [required: true] do
field :id, :uuid
field :name, :string
field :age, :number
field :amount, :float
end
end
By making the default value of a field nil
that field becomes nullable
defmodule StructType do
use Draft.Struct
# This will make all fields required.
schema [required: true] do
field :id, :uuid
field :name, :string
field :age, :number
# This field will can by nill
field :amount, :float, default: nil
end
end
By making the default value of a field nil
that field becomes nullable
defmodule StructType do
use Draft.Struct
# This will make all fields nill by default except id.
schema do
# Ensure id is required
field :id, :uuid, required: true
field :name, :string
field :age, :number
field :amount, :float
end
end
- any
- map
- enum
- atom
- uuid
- tuple
- float
- array
- struct
- number
- string
- boolean
- integer
- datetime
defmodule Typed do
use Draft.Struct
@mapping [
name: [:string, length: [min: 5, max: 10]],
value: [:number, required: false]
]
schema do
# Define map with fields
field :map_type, :map, fields: @mapping
end
end
defmodule Nested do
use Draft.Struct
schema do
field :value, :number
end
end
defmodule Typed do
use Draft.Struct
schema do
field :nested_array, :array, type: Nested, default: []
end
end
- inclusion
- exclusion
- required
- length
- format
- number
- fields
- struct
- uuid
- type
- by
- tld
- pattern
validate required, field must have a value except nil
defmodule Typed do
use Draft.Struct
schema do
field :name, :string, required: true
end
end
validate length
defmodule Typed do
use Draft.Struct
schema do
field :name, :string, length: [min: 2, max: 20]
end
end
validate pattern
defmodule Typed do
use Draft.Struct
schema do
field :email, :string, pattern: :email
end
end
Draft types all implement the Draft.Type.Behaviour
defining a new type
must implement this behaviour
defmodule CustomInteger do
@behaviour Draft.Type.Behaviour
def cast(value, options) when is_integer(value) do
{:ok, value}
end
def cast(value, options) do
{:error, ["value must be integer"]}
end
def dump(data, options) do
{:ok, data}
end
end
Draft types all implement the Draft.Type.Behaviour
defining a new type
must implement this behaviour
defmodule CustomValidator do
@behaviour Draft.Validator.Behaviour
# validate a value given a context and options
# defined in field definition
def validate(value, context, options) do
{:ok, value}
end
# vaidate return error tuple when value
# value fails validation
def validate(value, context, options) do
{:error, ["reason"]}
end
end
Draft types and validators can be defined or overwritten using config
config :types, Draft,
map: CustomMapImpl,
custom_integer: CustomInteger,
typename1: CustomType,
typename2: CustomTypeImpl2
config :validators, Draft,
validatorname: CustomValidator,
seondvalidator: CustomSecondValidatorImpl
use custom types and validators with Draft
defmodule CustomType do
use Draft.Struct
# Define your struct.
schema do
#Define a field with type string
field :name, :string, validatorname: [...opts]
#Define a field with custom integer type
field :age, :custom_integer, default: 10
end
end
- Field documention
- Validation documention