Ecto v2.2.0-rc.0 Ecto.Type behaviour View Source
Defines functions and the Ecto.Type behaviour for implementing
custom types.
A custom type expects 4 functions to be implemented, all documented and described below. We also provide two examples of how custom types can be used in Ecto to augment existing types or providing your own types.
Example
Imagine you want to support your id field to be looked up as a permalink. For example, you want the following query to work:
permalink = "10-how-to-be-productive-with-elixir"
from p in Post, where: p.id == ^permalinkIf id is an integer field, Ecto will fail in the query above
because it cannot cast the string to an integer. By using a
custom type, we can provide special casting behaviour while
still keeping the underlying Ecto type the same:
defmodule Permalink do
  @behaviour Ecto.Type
  def type, do: :integer
  # Provide our own casting rules.
  def cast(string) when is_binary(string) do
    case Integer.parse(string) do
      {int, _} -> {:ok, int}
      :error   -> :error
    end
  end
  # We should still accept integers
  def cast(integer) when is_integer(integer), do: {:ok, integer}
  # Everything else is a failure though
  def cast(_), do: :error
  # When loading data from the database, we are guaranteed to
  # receive an integer (as databases are strict) and we will
  # just return it to be stored in the schema struct.
  def load(integer) when is_integer(integer), do: {:ok, integer}
  # When dumping data to the database, we *expect* an integer
  # but any value could be inserted into the struct, so we need
  # guard against them.
  def dump(integer) when is_integer(integer), do: {:ok, integer}
  def dump(_), do: :error
endNow we can use our new field above as our primary key type in schemas:
defmodule Post do
  use Ecto.Schema
  @primary_key {:id, Permalink, autogenerate: true}
  schema "posts" do
    ...
  end
endLink to this section Summary
Types
Custom types are represented by user-defined modules
Primitive Ecto types (handled by Ecto)
An Ecto type, primitive or custom
Functions
Checks if the given atom can be used as base type
Casts a value to the given type
Checks if the given atom can be used as composite type
Dumps a value to the given type
Loads a value with the given type
Checks if a given type matches with a primitive type that can be found in queries
Checks if we have a primitive type
Retrieves the underlying schema type for the given, possibly custom, type
Callbacks
Casts the given input to the custom type
Dumps the given term into an Ecto native type
Loads the given term into a custom type
Returns the underlying schema type for the custom type
Link to this section Types
Custom types are represented by user-defined modules.
Primitive Ecto types (handled by Ecto).
An Ecto type, primitive or custom.
Link to this section Functions
Checks if the given atom can be used as base type.
iex> base?(:string)
true
iex> base?(:array)
false
iex> base?(Custom)
falseCasts a value to the given type.
cast/2 is used by the finder queries and changesets
to cast outside values to specific types.
Note that nil can be cast to all primitive types as data stores allow nil to be set on any column.
iex> cast(:any, "whatever")
{:ok, "whatever"}
iex> cast(:any, nil)
{:ok, nil}
iex> cast(:string, nil)
{:ok, nil}
iex> cast(:integer, 1)
{:ok, 1}
iex> cast(:integer, "1")
{:ok, 1}
iex> cast(:integer, "1.0")
:error
iex> cast(:id, 1)
{:ok, 1}
iex> cast(:id, "1")
{:ok, 1}
iex> cast(:id, "1.0")
:error
iex> cast(:float, 1.0)
{:ok, 1.0}
iex> cast(:float, 1)
{:ok, 1.0}
iex> cast(:float, "1")
{:ok, 1.0}
iex> cast(:float, "1.0")
{:ok, 1.0}
iex> cast(:float, "1-foo")
:error
iex> cast(:boolean, true)
{:ok, true}
iex> cast(:boolean, false)
{:ok, false}
iex> cast(:boolean, "1")
{:ok, true}
iex> cast(:boolean, "0")
{:ok, false}
iex> cast(:boolean, "whatever")
:error
iex> cast(:string, "beef")
{:ok, "beef"}
iex> cast(:binary, "beef")
{:ok, "beef"}
iex> cast(:decimal, Decimal.new(1.0))
{:ok, Decimal.new(1.0)}
iex> cast(:decimal, Decimal.new("1.0"))
{:ok, Decimal.new(1.0)}
iex> cast({:array, :integer}, [1, 2, 3])
{:ok, [1, 2, 3]}
iex> cast({:array, :integer}, ["1", "2", "3"])
{:ok, [1, 2, 3]}
iex> cast({:array, :string}, [1, 2, 3])
:error
iex> cast(:string, [1, 2, 3])
:errorChecks if the given atom can be used as composite type.
iex> composite?(:array)
true
iex> composite?(:string)
falseDumps a value to the given type.
Opposite to casting, dumping requires the returned value to be a valid Ecto type, as it will be sent to the underlying data store.
iex> dump(:string, nil)
{:ok, nil}
iex> dump(:string, "foo")
{:ok, "foo"}
iex> dump(:integer, 1)
{:ok, 1}
iex> dump(:integer, "10")
:error
iex> dump(:binary, "foo")
{:ok, "foo"}
iex> dump(:binary, 1)
:error
iex> dump({:array, :integer}, [1, 2, 3])
{:ok, [1, 2, 3]}
iex> dump({:array, :integer}, [1, "2", 3])
:error
iex> dump({:array, :binary}, ["1", "2", "3"])
{:ok, ["1", "2", "3"]}A dumper function may be given for handling recursive types.
Loads a value with the given type.
iex> load(:string, nil)
{:ok, nil}
iex> load(:string, "foo")
{:ok, "foo"}
iex> load(:integer, 1)
{:ok, 1}
iex> load(:integer, "10")
:errorA loader function may be given for handling recursive types.
Checks if a given type matches with a primitive type that can be found in queries.
iex> match?(:string, :any)
true
iex> match?(:any, :string)
true
iex> match?(:string, :string)
true
iex> match?({:array, :string}, {:array, :any})
true
iex> match?(Ecto.UUID, :uuid)
true
iex> match?(Ecto.UUID, :string)
falseChecks if we have a primitive type.
iex> primitive?(:string)
true
iex> primitive?(Another)
false
iex> primitive?({:array, :string})
true
iex> primitive?({:array, Another})
trueRetrieves the underlying schema type for the given, possibly custom, type.
iex> type(:string)
:string
iex> type(Ecto.UUID)
:uuid
iex> type({:array, :string})
{:array, :string}
iex> type({:array, Ecto.UUID})
{:array, :uuid}
iex> type({:map, Ecto.UUID})
{:map, :uuid}Link to this section Callbacks
Casts the given input to the custom type.
This callback is called on external input and can return any type,
as long as the dump/1 function is able to convert the returned
value back into an Ecto native type. There are two situations where
this callback is called:
- When casting values by Ecto.Changeset
- When passing arguments to Ecto.Query
Dumps the given term into an Ecto native type.
This callback is called with any term that was stored in the struct and it needs to validate them and convert it to an Ecto native type.
Loads the given term into a custom type.
This callback is called when loading data from the database and
receive an Ecto native type. It can return any type, as long as
the dump/1 function is able to convert the returned value back
into an Ecto native type.
Returns the underlying schema type for the custom type.
For example, if you want to provide your own date
structures, the type function should return :date.
Note this function is not required to return Ecto primitive types, the type is only required to be known by the adapter.
