Ash.Subject (ash v3.5.33)

View Source

Provides a consistent API for common operations across Ash.Changeset, Ash.Query, and Ash.ActionInput. It allows you to write generic code that works with any of these types without needing to pattern match or special-case your logic.

Summary

Types

Function type for after action hooks.

Function type for after transaction hooks.

Function type for around transaction hooks.

Function type for before action hooks.

Function type for before transaction hooks.

t()

Functions

Adds an error or list of errors to the subject.

Adds a callback to be executed after the action.

Adds an after_transaction hook to the subject.

Adds an around_transaction hook to the subject.

Adds a callback to be executed before the action.

Adds a before_transaction hook to the subject.

Deletes one or more arguments from the subject.

Fetches an argument value from the subject.

Gets an argument value from the subject.

Gets an argument value from the subject

Gets an argument or attribute value from a subject

Puts a key-value pair into the subject's context.

Sets a single argument on the subject.

Sets multiple arguments on the subject.

Sets the context for the subject.

Sets a private argument value on the action input.

Sets multiple private arguments on the subject.

Types

after_action_fun()

@type after_action_fun() :: (t(), term() ->
                         {:ok, term()}
                         | {:ok, term(), [Ash.Notifier.Notification.t()]}
                         | {:error, any()})

Function type for after action hooks.

Receives the action input and the result of the action, and can return the result optionally with notifications, or an error.

after_transaction_fun()

@type after_transaction_fun() :: (t(), {:ok, term()} | {:error, any()} ->
                              {:ok, term()} | {:error, any()})

Function type for after transaction hooks.

Receives the action input and the result of the transaction, and returns the result (potentially modified) or an error.

around_transaction_fun()

@type around_transaction_fun() :: (t(), (t() -> {:ok, term()} | {:error, any()}) ->
                               {:ok, term()} | {:error, any()})

Function type for around transaction hooks.

Receives an action input and a callback function that executes the transaction, and returns the result of calling the callback or an error.

before_action_fun()

@type before_action_fun() :: (t() ->
                          t()
                          | {t(),
                             %{notifications: [Ash.Notifier.Notification.t()]}})

Function type for before action hooks.

Receives an action input and returns a modified action input, optionally with notifications.

before_transaction_fun()

@type before_transaction_fun() :: (t() -> t() | {:error, any()})

Function type for before transaction hooks.

Receives an action input and returns a modified action input or an error.

t()

Functions

add_error(subject, error)

@spec add_error(
  subject :: t(),
  error_input :: Ash.Error.error_input() | [Ash.Error.error_input()]
) :: t()

Adds an error or list of errors to the subject.

Supports all subject types (Changeset, Query, ActionInput) and maintains type consistency.

Parameters

  • subject - The subject to add errors to
  • errors - Error or list of errors to add

after_action(subject, callback, opts \\ [])

@spec after_action(
  subject :: t(),
  callback :: after_action_fun(),
  opts :: Keyword.t()
) :: t()

Adds a callback to be executed after the action.

Note: Query only supports 2-arity callbacks and ignores opts.

Parameters

  • subject - The subject to add callback to
  • callback - Function that processes the result
  • opts - Options including :prepend? (ignored for Query)

after_transaction(subject, callback, opts \\ [])

@spec after_transaction(
  subject :: t(),
  callback :: after_transaction_fun(),
  opts :: Keyword.t()
) :: t()

Adds an after_transaction hook to the subject.

After transaction hooks are executed after the database transaction completes, regardless of success or failure. They receive both the subject and the transaction result, allowing for cleanup operations, logging, or result modification.

Parameters

  • subject - The subject to add the hook to (Changeset, Query, or ActionInput)
  • callback - Function that takes the subject and result, returns modified result
  • opts - Options including :prepend? to add at beginning of hooks list

Examples

# Add cleanup after transaction
iex> changeset
...> |> Ash.Subject.after_transaction(fn changeset, result ->
...>   cleanup_temp_resources()
...>   result
...> end)

# Log transaction outcome
iex> query
...> |> Ash.Subject.after_transaction(fn query, result ->
...>   case result do
...>     {:ok, _} -> Logger.info("Query succeeded")
...>     {:error, reason} -> Logger.error("Query failed: #{inspect(reason)}")
...>   end
...>   result
...> end)

# Modify successful results
iex> action_input
...> |> Ash.Subject.after_transaction(fn input, result ->
...>   case result do
...>     {:ok, data} -> {:ok, Map.put(data, :processed_at, DateTime.utc_now())}
...>     error -> error
...>   end
...> end)

Important Notes

  • These hooks run whether the transaction succeeds or fails
  • They run outside the transaction, so database operations here are not rolled back
  • The hook must return a result in the same format it received

See also

around_transaction(subject, callback, opts \\ [])

@spec around_transaction(
  subject :: t(),
  callback :: around_transaction_fun(),
  opts :: Keyword.t()
) :: t()

Adds an around_transaction hook to the subject.

Around transaction hooks wrap the entire transaction execution. They receive the subject and a callback function that executes the transaction. This allows adding logic both before and after the transaction while maintaining full control over its execution.

Parameters

  • subject - The subject to add the hook to (Changeset, Query, or ActionInput)
  • callback - Function that takes the subject and a callback, must call the callback
  • opts - Options including :prepend? to add at beginning of hooks list

Examples

# Add timing measurements
iex> changeset
...> |> Ash.Subject.around_transaction(fn changeset, callback ->
...>   start_time = System.monotonic_time(:millisecond)
...>   result = callback.(changeset)
...>   duration = System.monotonic_time(:millisecond) - start_time
...>   Logger.info("Transaction took #{duration}ms")
...>   result
...> end)

# Add retry logic for transient failures
iex> query
...> |> Ash.Subject.around_transaction(fn query, callback ->
...>   case callback.(query) do
...>     {:error, %{retryable?: true}} = error ->
...>       Logger.warn("Retrying after error: #{inspect(error)}")
...>       :timer.sleep(100)
...>       callback.(query)
...>     result ->
...>       result
...>   end
...> end)

# Wrap with custom error handling
iex> action_input
...> |> Ash.Subject.around_transaction(fn input, callback ->
...>   try do
...>     callback.(input)
...>   rescue
...>     exception ->
...>       Logger.error("Transaction failed: #{Exception.message(exception)}")
...>       {:error, Exception.message(exception)}
...>   end
...> end)

Warning

This is an advanced hook that controls transaction execution. You must call the callback function provided to your hook, and the return value must match the structure returned by the callback (typically {:ok, result} or {:error, reason}).

Failing to call the callback will prevent the transaction from executing at all.

See also

before_action(subject, callback, opts \\ [])

@spec before_action(
  subject :: t(),
  callback :: before_action_fun(),
  opts :: Keyword.t()
) :: t()

Adds a callback to be executed before the action.

Parameters

  • subject - The subject to add callback to
  • callback - Function that takes and returns the subject
  • opts - Options including :prepend? to add at beginning

before_transaction(subject, callback, opts \\ [])

@spec before_transaction(
  subject :: t(),
  callback :: before_transaction_fun(),
  opts :: Keyword.t()
) :: t()

Adds a before_transaction hook to the subject.

Before transaction hooks are executed before the database transaction begins. They receive the subject and must return either a modified subject or an error tuple. These hooks are useful for validation, authorization checks, or preparatory logic that should run outside of the transaction.

Parameters

  • subject - The subject to add the hook to (Changeset, Query, or ActionInput)
  • callback - Function that takes the subject and returns the subject or {:error, reason}
  • opts - Options including :prepend? to add at beginning of hooks list

Examples

# Add validation before transaction starts
iex> changeset
...> |> Ash.Subject.before_transaction(fn changeset ->
...>   if valid_state?(changeset) do
...>     changeset
...>   else
...>     {:error, "Invalid state for this operation"}
...>   end
...> end)

# Add logging for all subject types
iex> subject
...> |> Ash.Subject.before_transaction(fn subject ->
...>   Logger.info("Starting transaction for #{inspect(subject.resource)}")
...>   subject
...> end)

# Prepend a hook to run first
iex> query
...> |> Ash.Subject.before_transaction(check_permissions, prepend?: true)

See also

delete_argument(subject, argument_or_arguments)

@spec delete_argument(
  subject :: t(),
  argument_or_arguments :: atom() | String.t() | [atom() | String.t()]
) :: t()

Deletes one or more arguments from the subject.

Parameters

  • subject - The subject to delete arguments from
  • arguments - Single argument name or list of argument names to delete

fetch_argument(subject, argument)

@spec fetch_argument(subject :: t(), argument :: atom() | String.t()) ::
  {:ok, term()} | :error

Fetches an argument value from the subject.

Returns {:ok, value} if the argument exists, :error otherwise. Supports both atom and string argument names.

Parameters

  • subject - The subject to fetch argument from
  • argument - The argument name (atom or string)

get_argument(subject, argument)

@spec get_argument(subject :: t(), argument :: atom() | String.t()) :: term()

Gets an argument value from the subject.

Supports both atom and string argument names.

Parameters

  • subject - The subject to get argument from
  • argument - The argument name (atom or string)

get_argument(subject, argument, default \\ nil)

@spec get_argument(subject :: t(), argument :: atom() | String.t(), default :: term()) ::
  term()

Gets an argument value from the subject

Supports both atom and string argument names.

Parameters

  • subject - The subject to get argument from
  • argument - The argument name (atom or string)
  • default - The default value to return if the argument is not found

get_argument_or_attribute(subject, argument_or_attribute, default \\ nil)

@spec get_argument_or_attribute(
  subject :: t(),
  argument_or_attribute :: atom() | String.t(),
  default :: term()
) :: term() | nil

Gets an argument or attribute value from a subject

For Changesets, this will return the argument if it exists, otherwise the attribute. For Query and ActionInput, this only retrieves arguments.

Parameters

  • subject - The subject to get value from
  • name - The argument or attribute name (atom or string)
  • default - The default value to return if the argument or attribute is not found

get_attribute(subject, attribute)

@spec get_attribute(subject :: t(), attribute :: atom()) :: term()

put_context(subject, key, value)

@spec put_context(
  subject :: t(),
  key :: atom(),
  value :: term()
) :: t()

Puts a key-value pair into the subject's context.

Parameters

  • subject - The subject to update context on
  • key - The context key
  • value - The value to store

set_argument(subject, argument, value)

@spec set_argument(subject :: t(), argument :: atom() | String.t(), value :: term()) ::
  t()

Sets a single argument on the subject.

Parameters

  • subject - The subject to set argument on
  • argument - The argument name (atom or string)
  • value - The value to set

set_arguments(subject, map)

@spec set_arguments(subject :: t(), arguments :: map()) :: t()

Sets multiple arguments on the subject.

Takes a map of argument names to values and sets them all.

Parameters

  • subject - The subject to set arguments on
  • arguments - Map of argument names to values

set_context(subject, map)

@spec set_context(subject :: t(), context :: map()) :: t()

Sets the context for the subject.

Merges the provided map into the subject's existing context. For Changeset and Query, delegates to their specific implementations.

Parameters

  • subject - The subject to set context on
  • context - Map of context data to merge

set_private_argument(subject, argument, value)

@spec set_private_argument(
  subject :: Ash.Changeset.t() | Ash.ActionInput.t(),
  argument :: atom() | String.t(),
  value :: term()
) :: t()

Sets a private argument value on the action input.

Only supports Ash.Changeset and Ash.ActionInput subjects.

Private arguments are not exposed in the public API and can only be set internally. This function will only work for arguments marked as public?: false in the action definition.

Examples

# Set a private argument (assuming :internal_flag is private)
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:example, %{})
...> |> Ash.Subject.set_private_argument(:internal_flag, true)
...> |> Ash.Subject.get_argument(:internal_flag)
true

# Attempting to set a public argument as private will error
iex> input = MyApp.Post
...> |> Ash.ActionInput.for_action(:example, %{})
...> |> Ash.Subject.set_private_argument(:public_arg, "value")
iex> input.valid?
false

# Use in action implementations for internal state
iex> MyApp.Post
...> |> Ash.ActionInput.for_action(:complex_workflow, %{data: "user_data"})
...> |> Ash.Subject.set_private_argument(:workflow_step, 1)

See also

  • set_argument/3 for setting public arguments
  • get_argument/2-3 for retrieving argument values
  • Action argument definitions with public?: false

set_private_arguments(subject, map)

@spec set_private_arguments(
  subject :: Ash.Changeset.t() | Ash.ActionInput.t(),
  arguments :: map()
) :: t()

Sets multiple private arguments on the subject.

Only supports Ash.Changeset and Ash.ActionInput subjects.

Takes a map of argument names to values and sets them all as private arguments.

Parameters

  • subject - The subject to set private arguments on (Changeset or ActionInput)
  • arguments - Map of argument names to values