#
View Source Ash.Type behaviour (ash v2.4.22)

Describes how to convert data to `Ecto.Type` and eventually into the database.

This behaviour is a superset of the `Ecto.Type` behavior, that also contains API level information, like what kinds of filters are allowed.

##

built-in-types

Built in types

- `:map` - `Ash.Type.Map`
- `:term` - `Ash.Type.Term`
- `:atom` - `Ash.Type.Atom`
- `:string` - `Ash.Type.String`
- `:integer` - `Ash.Type.Integer`
- `:float` - `Ash.Type.Float`
- `:duration_name` - `Ash.Type.DurationName`
- `:function` - `Ash.Type.Function`
- `:boolean` - `Ash.Type.Boolean`
- `:struct` - `Ash.Type.Struct`
- `:uuid` - `Ash.Type.UUID`
- `:binary` - `Ash.Type.Binary`
- `:date` - `Ash.Type.Date`
- `:time` - `Ash.Type.Time`
- `:decimal` - `Ash.Type.Decimal`
- `:ci_string` - `Ash.Type.CiString`
- `:naive_datetime` - `Ash.Type.NaiveDatetime`
- `:utc_datetime` - `Ash.Type.UtcDatetime`
- `:utc_datetime_usec` - `Ash.Type.UtcDatetimeUsec`
- `:url_encoded_binary` - `Ash.Type.UrlEncodedBinary`

##

composite-types

Composite Types

Currently, the only composite type supported is a list type, specified via: `{:array, Type}`. The constraints available are:

- `:items` (`term/0`) - Constraints for the elements of the list. See the contained type's docs for more.

- `:min_length` (`non_neg_integer/0`) - A minimum length for the items

- `:max_length` (`non_neg_integer/0`) - A maximum length for the items

- `:nil_items?` (`boolean/0`) - Whether or not the list can contain nil items The default value is `false`.

##

defining-custom-types

Defining Custom Types

Generally you add `use Ash.Type` to your module (it is possible to add `@behaviour Ash.Type` and define everything yourself, but this is more work and error-prone).

Overriding the `{:array, type}` behavior. By defining the `*_array` versions of `cast_input`, `cast_stored`, `dump_to_native` and `apply_constraints`, you can override how your type behaves as a collection. This is how the features of embedded resources are implemented. No need to implement them unless you wish to override the default behavior.

Simple example of a float custom type

    defmodule GenTracker.AshFloat do
      use Ash.Type

      @impl Ash.Type
      def storage_type, do: :float

      @impl Ash.Type
      def cast_input(value, _) do
        Ecto.Type.cast(:float, value)
      end

      @impl Ash.Type
      def cast_stored(value, _) do
        Ecto.Type.load(:float, value)
      end

      @impl Ash.Type
      def dump_to_native(value, _) do
        Ecto.Type.dump(:float, value)
      end
    end

All the Ash built-in types are implemented with `use Ash.Type` so they are good examples to look at to create your own `Ash.Type`.

###

short-names

Short names

You can define short `:atom_names` for your custom types by adding them to your Ash configuration:

    config :ash, :custom_types, [ash_float: GenTracker.AshFloat]

Doing this will require a recompilation of the `:ash` dependency which can be triggered by calling:

    $ mix deps.compile ash --force

# Link to this section Summary

## Types

constraint_error()
constraints()
error()
t()

## Callbacks

apply_constraints(term, constraints)
apply_constraints_array(list, constraints)
array_constraints()
cast_in_query?()

Useful for typed data layers (like ash_postgres) to instruct them not to attempt to cast input values.

cast_input(term, constraints)
cast_input_array(list, constraints)
cast_stored(term, constraints)
cast_stored_array(list, constraints)
constraints()
describe(constraints)
dump_to_embedded(term, constraints)
dump_to_embedded_array(list, constraints)
dump_to_native(term, constraints)
dump_to_native_array(list, constraints)
ecto_type()
embedded?()
equal?(term, term)
generator(constraints)
handle_change(old_term, new_term, constraints)
handle_change_array(old_term, new_term, constraints)
prepare_change(old_term, new_uncasted_term, constraints)
prepare_change_array( old_term, new_uncasted_term, constraints )
simple_equality?()
storage_type()

## Functions

apply_constraints(type, term, constraints)

Confirms if a casted value matches the provided constraints.

array_constraints(type)
ash_type_option(type)
ash_type?(module)

Returns true if the value is a builtin type or adopts the `Ash.Type` behaviour

builtin?(type)
cast_in_query?(type)
cast_input(type, term, constraints \\ [])

Casts input (e.g. unknown) data to an instance of the type, or errors

cast_stored(type, term, constraints \\ [])

Casts a value from the data store to an instance of the type, or errors

constraints(type)
constraints(source, type, constraints)
describe(type, constraints)
dump_to_embedded(type, term, constraints \\ [])

Casts a value from the Elixir type to a value that can be embedded in another data structure.

dump_to_native(type, term, constraints \\ [])

Casts a value from the Elixir type to a value that the data store can persist

ecto_type(type)

Returns the ecto compatible type for an Ash.Type.

embedded_type?(type)
equal?(type, left, right)

Determines if two values of a given type are equal.

generator(type, constraints)
get_type(value)
handle_change(type, old_value, new_value, constraints)

Process the old casted values alongside the new casted values.

prepare_change(type, old_value, new_value, constraints)

Process the old casted values alongside the new uncasted values.

simple_equality?(type)

Determines if a type can be compared using ==

storage_type(type)

Returns the underlying storage type (the underlying type of the ecto type of the ash type)

# Link to this section Types

Link to this type

# constraint_error()

View Source

    @type constraint_error() :: String.t() | {String.t(), Keyword.t()}

Link to this type

# constraints()

View Source

    @type constraints() :: Keyword.t()

Link to this type

# error()

View Source

    @type error() :: :error | {:error, String.t() | Keyword.t()}

Link to this type

# t()

View Source

    @type t() :: atom() | {:array, atom()}

# Link to this section Callbacks

Link to this callback

# apply_constraints(term, constraints)

View Source

    @callback apply_constraints(term(), constraints()) ::
      {:ok, new_value :: term()}
      | :ok
      | {:error, constraint_error() | [constraint_error()]}

Link to this callback

# apply_constraints_array(list, constraints)

View Source (optional)

    @callback apply_constraints_array([term()], constraints()) ::
      {:ok, new_values :: [term()]}
      | :ok
      | {:error, constraint_error() | [constraint_error()]}

Link to this callback

# array_constraints()

View Source (optional)

    @callback array_constraints() :: constraints()

Link to this callback

# cast_in_query?()

View Source

    @callback cast_in_query?() :: boolean()

Useful for typed data layers (like ash_postgres) to instruct them not to attempt to cast input values.

You generally won't need this, but it can be an escape hatch for certain cases.

Link to this callback

# cast_input(term, constraints)

View Source

    @callback cast_input(term(), constraints()) :: {:ok, term()} | error()

Link to this callback

# cast_input_array(list, constraints)

View Source (optional)

    @callback cast_input_array([term()], constraints()) :: {:ok, [term()]} | error()

Link to this callback

# cast_stored(term, constraints)

View Source

    @callback cast_stored(term(), constraints()) :: {:ok, term()} | error()

Link to this callback

# cast_stored_array(list, constraints)

View Source (optional)

    @callback cast_stored_array([term()], constraints()) :: {:ok, [term()]} | error()

Link to this callback

# constraints()

View Source

    @callback constraints() :: constraints()

Link to this callback

# describe(constraints)

View Source

    @callback describe(constraints()) :: String.t() | nil

Link to this callback

# dump_to_embedded(term, constraints)

View Source (optional)

    @callback dump_to_embedded(term(), constraints()) :: {:ok, term()} | :error

Link to this callback

# dump_to_embedded_array(list, constraints)

View Source (optional)

    @callback dump_to_embedded_array([term()], constraints()) :: {:ok, term()} | error()

Link to this callback

# dump_to_native(term, constraints)

View Source

    @callback dump_to_native(term(), constraints()) :: {:ok, term()} | error()

Link to this callback

# dump_to_native_array(list, constraints)

View Source (optional)

    @callback dump_to_native_array([term()], constraints()) :: {:ok, term()} | error()

Link to this callback

# ecto_type()

View Source

    @callback ecto_type() :: Ecto.Type.t()

Link to this callback

# embedded?()

View Source

    @callback embedded?() :: boolean()

Link to this callback

# equal?(term, term)

View Source

    @callback equal?(term(), term()) :: boolean()

Link to this callback

# generator(constraints)

View Source (optional)

    @callback generator(constraints()) :: Enumerable.t()

Link to this callback

# handle_change(old_term, new_term, constraints)

View Source

    @callback handle_change(old_term :: term(), new_term :: term(), constraints()) ::
      {:ok, term()} | error()

Link to this callback

# handle_change_array(old_term, new_term, constraints)

View Source (optional)

    @callback handle_change_array(old_term :: [term()], new_term :: [term()], constraints()) ::
      {:ok, term()} | error()

Link to this callback

# prepare_change(old_term, new_uncasted_term, constraints)

View Source

    @callback prepare_change(old_term :: term(), new_uncasted_term :: term(), constraints()) ::
      {:ok, term()} | error()

Link to this callback

# prepare_change_array( old_term, new_uncasted_term, constraints )

View Source (optional)

    @callback prepare_change_array(
      old_term :: [term()],
      new_uncasted_term :: [term()],
      constraints()
    ) :: {:ok, term()} | error()

Link to this callback

# simple_equality?()

View Source

    @callback simple_equality?() :: boolean()

Link to this callback

# storage_type()

View Source

    @callback storage_type() :: Ecto.Type.t()

# Link to this section Functions

Link to this function

# apply_constraints(type, term, constraints)

View Source

    @spec apply_constraints(t(), term(), constraints()) ::
      {:ok, term()} | {:error, String.t()}

Confirms if a casted value matches the provided constraints.

Link to this function

# array_constraints(type)

View Source
Link to this function

# ash_type_option(type)

View Source
Link to this function

# ash_type?(module)

View Source

    @spec ash_type?(term()) :: boolean()

Returns true if the value is a builtin type or adopts the `Ash.Type` behaviour

Link to this function

# builtin?(type)

View Source
Link to this function

# cast_in_query?(type)

View Source
Link to this function

# cast_input(type, term, constraints \\ [])

View Source

    @spec cast_input(t(), term(), constraints() | nil) ::
      {:ok, term()} | {:error, Keyword.t()} | :error

Casts input (e.g. unknown) data to an instance of the type, or errors

Maps to `Ecto.Type.cast/2`

Link to this function

# cast_stored(type, term, constraints \\ [])

View Source

    @spec cast_stored(t(), term(), constraints() | nil) ::
      {:ok, term()} | {:error, keyword()} | :error

Casts a value from the data store to an instance of the type, or errors

Maps to `Ecto.Type.load/2`

Link to this function

# constraints(type)

View Source

    @spec constraints(t()) :: constraints()

Link to this function

# constraints(source, type, constraints)

View Source

    @spec constraints(Ash.Changeset.t() | Ash.Query.t(), t(), Keyword.t()) :: Keyword.t()

Link to this function

# describe(type, constraints)

View Source
Link to this function

# dump_to_embedded(type, term, constraints \\ [])

View Source

    @spec dump_to_embedded(t(), term(), constraints() | nil) ::
      {:ok, term()} | {:error, keyword()} | :error

Casts a value from the Elixir type to a value that can be embedded in another data structure.

Embedded resources expect to be stored in JSON, so this allows things like UUIDs to be stored as strings in embedded resources instead of binary.

Link to this function

# dump_to_native(type, term, constraints \\ [])

View Source

    @spec dump_to_native(t(), term(), constraints() | nil) ::
      {:ok, term()} | {:error, keyword()} | :error

Casts a value from the Elixir type to a value that the data store can persist

Maps to `Ecto.Type.dump/2`

Link to this function

# ecto_type(type)

View Source

    @spec ecto_type(t()) :: Ecto.Type.t()

Returns the ecto compatible type for an Ash.Type.

If you `use Ash.Type`, this is created for you. For builtin types this may return a corresponding ecto builtin type (atom)

Link to this function

# embedded_type?(type)

View Source
Link to this function

# equal?(type, left, right)

View Source

    @spec equal?(t(), term(), term()) :: boolean()

Determines if two values of a given type are equal.

Maps to `Ecto.Type.equal?/3`

Link to this function

# generator(type, constraints)

View Source

    @spec generator(
      module() | {:array, module()},
      constraints()
    ) :: Enumerable.t()

Link to this function

# get_type(value)

View Source

    @spec get_type(atom() | module() | {:array, atom() | module()}) ::
      atom() | module() | {:array, atom() | module()}

Link to this function

# handle_change(type, old_value, new_value, constraints)

View Source

Process the old casted values alongside the new casted values.

This is leveraged by embedded types to know if something is being updated or destroyed. This is not called on creates.

Link to this function

# prepare_change(type, old_value, new_value, constraints)

View Source

Process the old casted values alongside the new uncasted values.

This is leveraged by embedded types to know if something is being updated or destroyed. This is not called on creates.

Link to this function

# simple_equality?(type)

View Source

    @spec simple_equality?(t()) :: boolean()

Determines if a type can be compared using ==

Link to this function

# storage_type(type)

View Source

    @spec storage_type(t()) :: Ecto.Type.t()

Returns the underlying storage type (the underlying type of the ecto type of the ash type)