Skip to content

2p4b/blueprint

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

53 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Draft

Draft is a library for building structs with runtime type validation. Creating and validating structs has never been easier. Inspired by Ecto.Schema

Usage

Setup

To use Draft in your project, add this to your Mix dependencies:

{:draft, "~> 0.1.0"},

General usage

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

Inheritance

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

Inherit from single base

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}

Inherit from multiple base modules

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

overwrite field base definition

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}

Methods

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{...})

Advanced usage

Required fields

Defining required fields is simple required fields cannot be nil

There are multiple way of going about this

Make all fields required

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

Exclude some fields from being required

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

Make select fields required

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

Draft types

  • any
  • map
  • enum
  • atom
  • uuid
  • tuple
  • float
  • array
  • struct
  • number
  • string
  • boolean
  • integer
  • datetime

map

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

nested types

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

Draft validators

  • inclusion
  • exclusion
  • required
  • length
  • format
  • number
  • fields
  • struct
  • uuid
  • type
  • by
  • tld
  • pattern

required

validate required, field must have a value except nil

defmodule Typed do
    use Draft.Struct

    schema do
        field :name, :string,  required: true
    end
end

length

validate length

defmodule Typed do
    use Draft.Struct

    schema do
        field :name, :string,  length: [min: 2, max: 20]
    end
end

pattern

validate pattern

defmodule Typed do
    use Draft.Struct

    schema do
        field :email, :string, pattern: :email
    end
end

Customization

Draft.Type

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.Validator

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

Thats all there is to blueprint

Todo

  • Field documention
  • Validation documention

About

elixir schema and validation lib

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages