Configuration

Herein lies the key to writing a schema. Here is how we would rewrite the Example given by Ecto.Schema:

defmodule App.User do
#######
# We don't need this anymore
#######
#
#use Ecto.Schema
#
#schema "users" do
#  field :name, :string
#  field :age, :integer, default: 0
#  field :password, :string, redact: true
#  has_many :posts, Post
#end


  use Sorcery.Schema, %{
    meta: %{},
    fields: %{
        name:      %{t: :string},
        age:       %{t: :integer, default: 0},
        password:  %{t: :string,  redact: true},
        # No need for a has_many. In Sorcery, it is always the child that references the parent.
    }
  }


end

For a more complete example, look at this:

defmodule Src.Schemas.Types do
  use Sorcery.Schema, fields: %{
    bool_mandatory: %{t: :boolean, optional?: false},
    bool_optional: %{t: :boolean, optional?: true},
    int_mandatory: %{t: :integer, optional?: false, min: 0, max: 10},
    int_optional: %{t: :integer, optional?: true, min: -10, max: 10},
    float_mandatory: %{t: :float, optional?: false, min: 0.0, max: 10.0},
    float_optional: %{t: :float, optional?: true, min: -10.0, max: 10.0},
    string_mandatory: %{t: :string, optional?: false, min: 10, max: 20},
    string_optional: %{t: :string, optional?: true, min: 10, max: 20},
    list_mandatory: %{t: :list, coll_of: :integer, min: 10, max: 20},
    list_optional:  %{t: :list, coll_of: :string, optional?: true,  min: 10, max: 20},
    list_inner: %{t: :list, coll_of: :string, inner: %{min: 15, max: 25}, min: 5, max: 10},
    map_mandatory: %{t: :map, optional?: false},
    map_optional: %{t: :map, optional?: true},
  }
end

Optional?

In the above example, notice how we explicitly declare the :optional? key in every field. If this is set to true, and if no value is provided while generating the struct, then the field will use the FieldType default. For most fields (:integers, :strings, etc) this is nil. For lists and maps, it's an empty list or map respectively. For example an :integer field with optional?: true will only ever be an integer. But an :integer field with optional?: false might be either an integer or nil.

Meta Map

Before getting into the meat of it, notice the empty map called :meta up there? We can use it to set some schema-wide configuration.

:metatypesDefaultDescription
:optional?booleantrueWhether every field allows nil. Individual fields can override. Maps and lists default to false.

Fields by type

:boolean

typesOptional?DefaultDescription
:defaultbooleantruenilIf no value is given, this will be used.
:optional?booleantruenilWhether field is allowed to be nil

:string

typesOptional?DefaultDescription
:minintegertrue0field must have at least this many characters.
:maxintegertrue25field must have no more than this many characters.
:defaultstringtruenilIf no value is given, this will be used.
:optional?booleantruetrueWhether field is allowed to be nil

:integer

typesOptional?DefaultDescription
:minintegertrue-10_000field be at least this big.
:maxintegertrue10_000field be no more than this big.
:defaultintegertruenilIf no value is given, this will be used.
:optional?booleantruetrueWhether field is allowed to be nil

:float

typesOptional?DefaultDescription
:minfloattrue-10_000.0field be at least this big.
:maxfloattrue10_000.0field be no more than this big.
:defaultintegertruenilIf no value is given, this will be used.
:optional?booleantruetrueWhether field is allowed to be nil

:list

typesOptional?DefaultDescription
:minintegertrue0list must have at least this many items
:maxintegertrue10list must have at most this many items
:defaultintegertruenilIf no value is given (and optional?: true), then nil.
:optional?booleantruefalseWhether field is allowed to be nil

:map

typesOptional?DefaultDescription
:defaultintegertruenilIf no value is given (and optional?: true), then nil.
:optional?booleantruefalseWhether field is allowed to be nil

We don't both generating content inside maps. Too much complexity, and too far from the core goals of Sorcery.

:fk

This is still under construction. For now I would suggest using :integer instead.