Search documentation of ash

# View Source Ash.Type behaviour (ash v3.4.30)

The `Ash.Type` behaviour is used to define a value type in Ash.

## Built in types

- `:map` - `Ash.Type.Map`
- `:keyword` - `Ash.Type.Keyword`
- `:term` - `Ash.Type.Term`
- `:atom` - `Ash.Type.Atom`
- `:string` - `Ash.Type.String`
- `:integer` - `Ash.Type.Integer`
- `:file` - `Ash.Type.File`
- `: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`
- `:uuid_v7` - `Ash.Type.UUIDv7`
- `: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`
- `:datetime` - `Ash.Type.DateTime`
- `:url_encoded_binary` - `Ash.Type.UrlEncodedBinary`
- `:union` - `Ash.Type.Union`
- `:module` - `Ash.Type.Module`
- `:vector` - `Ash.Type.Vector`

## Lists/Arrays

To specify a list of values, use `{:array, Type}`. Arrays are special, and have special constraints:

- `: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`.

- `:remove_nil_items?` (`boolean/0`) - Whether or not to remove the nil items from the list instead of adding errors. The default value is `false`.

- `:empty_values` (list of `term/0`) - A set of values that, if encountered, will be considered an empty list. The default value is `[""]`.

## 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}` behaviour. 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 behaviour. Your type is responsible for handling nil values in each callback as well.

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(nil, _), do: {:ok, nil}
      def cast_input(value, _) do
        Ecto.Type.cast(:float, value)
      end

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

      @impl Ash.Type
      def dump_to_native(nil, _), do: {:ok, nil}
      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

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

## Composite Types

Composite types are composite in the data layer. Many data layers do not support this, but some (like AshPostgres), do. To define a composite type, the following things should be true:

1. A casted value should be a map or struct, for example for a point: `%{x: 1, y: 2}`
2. The data layer must support composite types, and the data layer representation will be a tuple, i.e `{1, 2}`
3. Define `def composite?(_), do: true` in your composite type
4. Define the type & constraints of each item in the tuple, and its name in the map representation: `def composite_types(_), do: [{:x, :integer, []}, {:y, :integer, []}]`. You can also define a storage key for each item in the tuple, if the underlying type implementation has a different reference for an item, i.e `def composite_types(_), do: [{:x, :x_coord, :integer, []}, {:y, :y_coord, :integer, []}]`

With the above implemented, your composite type can be used in expressions, for example:

    Ash.Query.filter(expr(coordinates[:x] == 1))k

And you can also construct composite types in expressions, for example:

    calculate :coordinates, :composite_point, expr(composite_type(%{x: some_value, y: some_other_value}, Point))

## Constraints

Constraints are a way of validating an input type. This validation can be used in both attributes and arguments. The kinds of constraints you can apply depends on the type of data. You can find all types in `Ash.Type` . Each type has its own page on which the available constraints are listed. For example in `Ash.Type.String` you can find 5 constraints:

- `:max_length`
- `:min_length`
- `:match`
- `:trim?`
- `:allow_empty?`

You can also discover these constraints from iex:

    $ iex -S mix
    iex(1)> Ash.Type.String.constraints
    [
      max_length: [
        type: :non_neg_integer,
        doc: "Enforces a maximum length on the value"
      ],
      min_length: [
        type: :non_neg_integer,
        doc: "Enforces a minimum length on the value"
      ],
      match: [
        type: {:custom, Ash.Type.String, :match, []},
        doc: "Enforces that the string matches a passed in regex"
      ],
      trim?: [type: :boolean, doc: "Trims the value.", default: true],
      allow_empty?: [
        type: :boolean,
        doc: "If false, the value is set to `nil` if it's empty.",
        default: false
      ]
    ]

### Attribute example

To show how constraints can be used in a attribute, here is an example attribute describing a username:

    defmodule MyProject.MyDomain.Account do
      # ...

      code_interface do
        define :create, action: :create
      end

      actions do
        default [:create, :read, :update, :destroy]
      end

      attributes do
        uuid_primary_key :id

        attribute :username, :string do
          constraints [
            max_length: 20,
            min_length: 3,
            match: ~r/^[a-z_-]*$/,
            trim?: true,
            allow_empty?: false
          ]
        end
      end

      # ...
    end

If, when creating or updating this attribute, one of the constraints are not met, an error will be given telling you which constraint was broken. See below:

    iex(1)> MyProject.MyDomain.Account.create!(%{username: "hi"})

    ** (Ash.Error.Invalid) Invalid Error

    * Invalid value provided for username: length must be greater than or equal to 3.

    "hi"

    iex(2)> MyProject.MyDomain.Account.create!(%{username: "Hello there this is a long string"})

    ** (Ash.Error.Invalid) Invalid Error

    * Invalid value provided for username: length must be less than or equal to 20.

    "Hello there this is a long string"

    iex(3)> MyProject.MyDomain.Account.create!(%{username: "hello there"})
    ** (Ash.Error.Invalid) Invalid Error

    * Invalid value provided for username: must match the pattern ~r/^[a-z_-]*$/.

    "hello there"

    iex(4)> MyProject.MyDomain.Account.create!(%{username: ""})
    ** (Ash.Error.Invalid) Invalid Error

    * attribute title is required

It will give you the resource as usual on successful requests:

    iex(5)> MyProject.MyDomain.Account.create!(%{username: "hello"})
    #MyProject.MyDomain.Account<
      __meta__: #Ecto.Schema.Metadata<:loaded, "account">,
      id: "7ba467dd-277c-4916-88ae-f62c93fee7a3",
      username: "hello",
      ...
    >

# Summary

## Types

constraint_error()
constraints()
error()
load_context()
merge_load_context()
rewrite()
rewrite_data()
t()

## Callbacks

apply_atomic_constraints(new_value, constraints)
apply_atomic_constraints_array(new_value, constraints)
apply_constraints(term, constraints)
apply_constraints_array(list, constraints)
array_constraints()
can_load?(constraints)
cast_atomic(new_value, constraints)
cast_atomic_array(new_value, constraints)
cast_in_query?(constraints)
cast_input(term, constraints)
cast_input_array(list, constraints)
cast_stored(term, constraints)
cast_stored_array(list, constraints)
composite?(constraints)
composite_types(constraints)
constraints()
custom_apply_constraints_array?()
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)
evaluate_operator(term)

The implementation for any overloaded implementations.

generator(constraints)
get_rewrites(merged_load, calculation, path, constraints)
handle_change(old_term, new_term, constraints)
handle_change_array(old_term, new_term, constraints)
handle_change_array?()
include_source(constraints, t)
init(constraints)

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

load(values, load, constraints, context)
loaded?(value, path_to_load, constraints, opts)
matches_type?(term, constraints)
merge_load(left, right, constraints, context)
operator_overloads()

A map of operators with overloaded implementations.

prepare_change(old_term, new_uncasted_term, constraints)
prepare_change_array(old_term, new_uncasted_term, constraints)
prepare_change_array?()
rewrite(value, list, constraints)
simple_equality?()
storage_type()
storage_type(constraints)

## Functions

apply_atomic_constraints(type, term, constraints)
apply_constraints(type, term, constraints)

Confirms if a casted value matches the provided constraints.

array_constraints(type)

Gets the array constraints for a type

ash_type?(module)

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

builtin?(type)

Returns true if the type is an ash builtin type

can_load?(type, constraints \\ [])

Returns true if the type supports nested loads

cast_atomic(type, term, constraints)
cast_in_query?(type, constraints \\ [])

Returns `true` if the type should be cast in underlying queries

cast_input(type, term, constraints \\ nil)

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

composite?(type, constraints)

Returns true if the type is a composite type

composite_types(type, constraints)

Returns the wrapped composite types

constraints(type)

Returns the constraint schema for a type

describe(type, constraints)

Calls the type's `describe` function with the given constraints

determine_types(types, values)

Determine types for a given function or operator.

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)

Returns true if the type is an embedded resource

equal?(type, left, right)

Determines if two values of a given type are equal.

generator(type, constraints)

Returns the StreamData generator for a given type

get_rewrites(type, merged_load, calculation, path, constraints)

Gets the load rewrites for a given type, load, calculation and path.

get_type(value)

Gets the type module for a given short name or module

handle_change(type, old_value, new_value, constraints)

Process the old casted values alongside the new casted values.

handle_change_array?(type)

Handles the change of a given array of values for an attribute change. Runs after casting.

include_source(type, changeset_or_query, constraints)
init(type, constraints)

Initializes the constraints according to the underlying type

load(type, value, loads, constraints, context)
loaded?(type, values, load, constraints, opts \\ [])
matches_type?(type, value, constraints \\ [])

Detects as a best effort if an arbitrary value matches the given type

merge_load(type, left, right, constraints, context)
prepare_change(type, old_value, new_value, constraints)

Process the old casted values alongside the new uncasted values.

prepare_change_array?(type)

Prepares a given array of values for an attribute change. Runs before casting.

rewrite(type, value, rewrites, constraints)

Applies rewrites to a given value.

short_names()

Returns the list of available type short names

simple_equality?(type)

Determines if a type can be compared using the `==` operator.

storage_type(type, constraints \\ [])

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

# 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

# load_context()

View Source

    @type load_context() :: %{
      domain: Ash.Domain.t(),
      actor: term() | nil,
      tenant: term(),
      tracer: [Ash.Tracer.t()] | Ash.Tracer.t() | nil,
      authorize?: boolean() | nil
    }

Link to this type

# merge_load_context()

View Source

    @type merge_load_context() :: %{
      domain: Ash.Domain.t(),
      calc_name: term(),
      calc_load: term(),
      calc_path: [atom()],
      reuse_values?: boolean(),
      strict_loads?: boolean(),
      initial_data: term(),
      relationship_path: [atom()],
      authorize?: boolean()
    }

Link to this type

# rewrite()

View Source

    @type rewrite() :: {{[atom()], rewrite_data(), atom(), atom()}, source :: term()}

Link to this type

# rewrite_data()

View Source

    @type rewrite_data() ::
      {type :: :calc | :agg, rewriting_name :: atom(), rewriting_load :: atom()}
      | {:rel, rewriting_name :: atom()}

Link to this type

# t()

View Source

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

# Callbacks

Link to this callback

# apply_atomic_constraints(new_value, constraints)

View Source

    @callback apply_atomic_constraints(new_value :: Ash.Expr.t(), constraints()) ::
      :ok | {:ok, Ash.Expr.t()} | {:error, Ash.Error.t()}

Link to this callback

# apply_atomic_constraints_array(new_value, constraints)

View Source

    @callback apply_atomic_constraints_array(new_value :: Ash.Expr.t(), constraints()) ::
      :ok | {:ok, Ash.Expr.t()} | {:error, Ash.Error.t()}

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

# can_load?(constraints)

View Source

    @callback can_load?(constraints()) :: boolean()

Link to this callback

# cast_atomic(new_value, constraints)

View Source

    @callback cast_atomic(new_value :: Ash.Expr.t(), constraints()) ::
      {:atomic, Ash.Expr.t()} | {:error, Ash.Error.t()} | {:not_atomic, String.t()}

Link to this callback

# cast_atomic_array(new_value, constraints)

View Source

    @callback cast_atomic_array(new_value :: Ash.Expr.t(), constraints()) ::
      {:atomic, Ash.Expr.t()} | {:error, Ash.Error.t()} | {:not_atomic, String.t()}

Link to this callback

# cast_in_query?(constraints)

View Source

    @callback cast_in_query?(constraints()) :: boolean()

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

# composite?(constraints)

View Source

    @callback composite?(constraints()) :: boolean()

Link to this callback

# composite_types(constraints)

View Source

    @callback composite_types(constraints()) :: [
      {name, type, constraints()} | {name, storage_key, type, constraints()}
    ]
    when name: atom(), type: t(), storage_key: atom()

Link to this callback

# constraints()

View Source

    @callback constraints() :: constraints()

Link to this callback

# custom_apply_constraints_array?()

View Source

    @callback custom_apply_constraints_array?() :: boolean()

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

# evaluate_operator(term)

View Source (optional)

    @callback evaluate_operator(term()) :: {:known, term()} | :unknown | {:error, term()}

The implementation for any overloaded implementations.

Link to this callback

# generator(constraints)

View Source (optional)

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

Link to this callback

# get_rewrites(merged_load, calculation, path, constraints)

View Source (optional)

    @callback get_rewrites(
      merged_load :: term(),
      calculation :: Ash.Query.Calculation.t(),
      path :: [atom()],
      constraints :: Keyword.t()
    ) :: [rewrite()]

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

# handle_change_array?()

View Source

    @callback handle_change_array?() :: boolean()

Link to this callback

# include_source(constraints, t)

View Source (optional)

    @callback include_source(constraints(), Ash.Changeset.t()) :: constraints()

Link to this callback

# init(constraints)

View Source (optional)

    @callback init(constraints()) :: {:ok, constraints()} | {:error, Ash.Error.t()}

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

# load(values, load, constraints, context)

View Source (optional)

    @callback load(
      values :: [term()],
      load :: Keyword.t(),
      constraints :: Keyword.t(),
      context :: load_context()
    ) :: {:ok, [term()]} | {:error, Ash.Error.t()}

Link to this callback

# loaded?(value, path_to_load, constraints, opts)

View Source

    @callback loaded?(
      value :: term(),
      path_to_load :: [atom()],
      constraints :: Keyword.t(),
      opts :: Keyword.t()
    ) :: boolean()

Link to this callback

# matches_type?(term, constraints)

View Source

    @callback matches_type?(term(), constraints()) :: boolean()

Link to this callback

# merge_load(left, right, constraints, context)

View Source (optional)

    @callback merge_load(
      left :: term(),
      right :: term(),
      constraints :: Keyword.t(),
      context :: merge_load_context() | nil
    ) :: {:ok, term()} | {:error, error()} | :error

Link to this callback

# operator_overloads()

View Source (optional)

    @callback operator_overloads() :: %{optional(atom()) => %{optional(term()) => module()}}

A map of operators with overloaded implementations.

These will only be honored if the type is placed in `config :ash, :known_types, [...Type]`

A corresponding `evaluate_operator/1` clause should match.

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

# prepare_change_array?()

View Source

    @callback prepare_change_array?() :: boolean()

Link to this callback

# rewrite(value, list, constraints)

View Source (optional)

    @callback rewrite(value :: term(), [rewrite()], constraints :: Keyword.t()) ::
      value :: term()

Link to this callback

# simple_equality?()

View Source

    @callback simple_equality?() :: boolean()

Link to this callback

# storage_type()

View Source (optional)

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

Link to this callback

# storage_type(constraints)

View Source

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

# Functions

Link to this function

# apply_atomic_constraints(type, term, constraints)

View Source

    @spec apply_atomic_constraints(t(), term(), constraints()) ::
      {:ok, Ash.Expr.t()} | {:error, Ash.Error.t()}

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

Gets the array constraints for a type

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

Returns true if the type is an ash builtin type

Link to this function

# can_load?(type, constraints \\ [])

View Source

    @spec can_load?(t(), Keyword.t()) :: boolean()

Returns true if the type supports nested loads

Link to this function

# cast_atomic(type, term, constraints)

View Source

    @spec cast_atomic(t(), term(), constraints()) ::
      {:atomic, Ash.Expr.t()}
      | {:ok, term()}
      | {:error, Ash.Error.t()}
      | {:not_atomic, String.t()}

Link to this function

# cast_in_query?(type, constraints \\ [])

View Source

Returns `true` if the type should be cast in underlying queries

Link to this function

# cast_input(type, term, constraints \\ nil)

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

# composite?(type, constraints)

View Source

    @spec composite?(
      t(),
      constraints()
    ) :: Enumerable.t()

Returns true if the type is a composite type

Link to this function

# composite_types(type, constraints)

View Source

    @spec composite_types(
      t(),
      constraints()
    ) :: Enumerable.t()

Returns the wrapped composite types

Link to this function

# constraints(type)

View Source

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

Returns the constraint schema for a type

Link to this function

# describe(type, constraints)

View Source

Calls the type's `describe` function with the given constraints

Link to this function

# determine_types(types, values)

View Source

Determine types for a given function or operator.

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

Returns true if the type is an embedded resource

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()

Returns the StreamData generator for a given type

Link to this function

# get_rewrites(type, merged_load, calculation, path, constraints)

View Source

Gets the load rewrites for a given type, load, calculation and path.

This is used for defining types that support a nested load statement. See the embedded type and union type implementations for examples of how to use this.

Link to this function

# get_type(value)

View Source

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

Gets the type module for a given short name or 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

# handle_change_array?(type)

View Source

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

Handles the change of a given array of values for an attribute change. Runs after casting.

Link to this function

# include_source(type, changeset_or_query, constraints)

View Source

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

Link to this function

# init(type, constraints)

View Source

    @spec init(t(), constraints()) :: {:ok, constraints()} | {:error, Ash.Error.t()}

Initializes the constraints according to the underlying type

Link to this function

# load(type, value, loads, constraints, context)

View Source

    @spec load(
      type :: t(),
      values :: [term()],
      load :: Keyword.t(),
      constraints :: Keyword.t(),
      context :: load_context()
    ) :: {:ok, [term()]} | {:error, Ash.Error.t()}

Link to this function

# loaded?(type, values, load, constraints, opts \\ [])

View Source

    @spec loaded?(
      type :: t(),
      value_or_values :: term(),
      path_to_load :: [atom()],
      constraints :: Keyword.t(),
      opts :: Keyword.t()
    ) :: boolean()

Link to this function

# matches_type?(type, value, constraints \\ [])

View Source

Detects as a best effort if an arbitrary value matches the given type

Link to this function

# merge_load(type, left, right, constraints, context)

View Source

    @spec merge_load(
      type :: t(),
      left :: term(),
      right :: term(),
      constraints :: Keyword.t(),
      context :: merge_load_context() | nil
    ) :: {:ok, [term()]} | :error | {:error, Ash.Error.t()}

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

# prepare_change_array?(type)

View Source

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

Prepares a given array of values for an attribute change. Runs before casting.

Link to this function

# rewrite(type, value, rewrites, constraints)

View Source

Applies rewrites to a given value.

This is used for defining types that support a nested load statement. See the embedded type and union type implementations for examples of how to use this.

Link to this function

# short_names()

View Source

Returns the list of available type short names

Link to this function

# simple_equality?(type)

View Source

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

Determines if a type can be compared using the `==` operator.

Link to this function

# storage_type(type, constraints \\ [])

View Source

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