View Source Draft
Draft is a library for building typed structs with built-in validation support.
usage
Usage
setup
Setup
Add :draft
to your project's dependencies in mix.exs
:
{:draft, "~> 1.0"}
general-usage
General Usage
To define a simple Draft struct:
defmodule Book do
use Draft.Schema
# Define your struct.
schema required: true do
field :id, :string
field :title, :string, min: 1, max: 32
field :author_id, :string
field :isbn, :integer, min: 1_000_000_000, max: 9_999_999_999_999
end
end
construction
Construction
You can create a struct using new
, cast
, or from_struct
. Only type information is checked during construction.
new
new/1
raises an error for invalid types or missing required fields.
# Using a keyword list
book = Book.new(id: "1", title: "Elixir Draft Tutorial", author_id: "2", isbn: 22222222222)
# Using a map
book = Book.new(%{
id: "1",
title: "Elixir Draft Tutorial",
author_id: "2",
isbn: 22222222222
})
cast
cast/1
returns a result tuple: {:ok, struct}
or {:error, errors}
. errors
is a keyword list.
{:ok, book} = Book.cast(id: "1", title: "Elixir Draft Tutorial", author_id: "2", isbn: 22222222222)
from_struct
Use from_struct/1
and from_struct!/1
to create a struct from another struct. The bang version raises on errors.
defmodule Document do
use Draft.Schema
schema required: true do
field :id, :string
field :title, :string, min: 1, max: 32
field :author_id, :string
field :history, :list, type: :string, default: []
field :isbn, :integer, min: 1_000_000_000, max: 9_999_999_999_999
end
end
doc = Document.new(id: "1", title: "Elixir Doc", author_id: "2", isbn: 22222222222, history: [])
book = Book.from_struct!(doc)
{:ok, book} = Book.from_struct(doc)
required-fields
Required Fields
By default, all fields can be nil
. Use required: true
in the schema to make all fields required.
defmodule Person do
use Draft.Schema
schema required: true do
field :id, :uuid
field :name, :string
field :age, :number
field :amount, :float
end
end
Fields with default values are considered optional:
field :amount, :float, default: nil
You can make individual fields required:
field :id, :uuid, required: true
validation
Validation
Use Draft.errors(struct)
to validate a Draft struct. Errors are returned as a keyword list.
book = Book.new(id: "1", title: "Draft Errors", author_id: "2", isbn: 1)
[isbn: _] = Draft.errors(book)
inheritance
Inheritance
Draft supports inheritance via the :extends
option, including validation rules and types.
defmodule Book do
use Draft.Schema
schema required: true do
field :id, :string
field :title, :string, min: 1, max: 32
field :author_id, :string
field :isbn, :integer, min: 1_000_000_000, max: 9_999_999_999_999
end
end
defmodule Document do
use Draft.Schema
schema extends: Book do
field :history, :list, type: :string, default: []
end
end
Multiple inheritance:
defmodule HasAuthor do
schema required: true do
field :author_id, :string
end
end
defmodule HasISBN do
schema do
field :isbn, :integer, min: 1_000_000_000, max: 9_999_999_999_999
end
end
defmodule Book do
use Draft.Schema
schema required: true, extends: [HasAuthor, HasISBN] do
field :id, :string
field :title, :string, min: 1, max: 32
end
end
Overwriting fields:
defmodule Book do
use Draft.Schema
schema do
field :id, :string
end
end
defmodule Document do
use Draft.Schema
schema extends: Book do
field :id, :uuid, overwrite: true
end
end
# Invalid UUID
{:error, _} = Document.new(id: "1")
# Valid UUID
doc = Document.new(id: "00000000-0000-0000-0000-000000000000")
advanced-usage
Advanced Usage
map-fields
Map Fields
defmodule Typed do
use Draft.Schema
@mapping [
name: [:string, length: [min: 5, max: 10]],
value: [:number, required: false]
]
schema do
field :stats, :map, fields: @mapping
end
end
nested-types
Nested Types
defmodule Book do
use Draft.Schema
schema do
field :title, :string
end
end
defmodule Library do
use Draft.Schema
schema do
field :books, :list, type: Book, default: []
end
end
required-field-validation
Required Field Validation
field :name, :string, required: true
length-validation
Length Validation
field :name, :string, length: [min: 2, max: 20]
pattern-validation
Pattern Validation
field :password, :string, pattern: ~r/^[[:alnum:]]+$/
customization
Customization
Draft types must implement both Draft.Type.Behaviour
and Draft.Validator.Behaviour
.
custom-type-draft-type-behaviour
Custom Type (Draft.Type.Behaviour
)
defmodule ISBN.Type do
@behaviour Draft.Type.Behaviour
def cast(value, _opts) when is_integer(value) and value in 1_000_000_000..999_999_999_999, do:
{:ok, value}
def cast(_value, _opts), do:
{:error, ["value must be a valid ISBN"]}
def dump(value, _opts), do:
{:ok, value}
end
custom-validator-draft-validator-behaviour
Custom Validator (Draft.Validator.Behaviour
)
defmodule ISBN.Validator do
@behaviour Draft.Validator.Behaviour
def validate(value, opts), do: validate(value, nil, opts)
def validate(value, _context, _opts), do:
{:ok, value}
def validate(_value, _context, _opts), do:
{:error, ["reason"]}
end
configuration
Configuration
In your config/config.exs
:
config :types, Draft,
isbn: ISBN.Type
config :validators, Draft,
isbn: ISBN.Validator
usage-in-schema
Usage in Schema
field :isbn_number, :isbn
built-in-types
Built-in Types
any
map
enum
atom
uuid
list
tuple
float
struct
number
string
boolean
integer
datetime
built-in-validators
Built-in Validators
inclusion
exclusion
required
length
format
number
fields
struct
uuid
type
min
max
by
tld
pattern
todo
TODO
- [ ] Field documentation
- [ ] Validation documentation