Ash.Subject (ash v3.5.33)
View SourceProvides 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.
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
@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.
@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.
@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.
@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.
Function type for before transaction hooks.
Receives an action input and returns a modified action input or an error.
@type t() :: Ash.Changeset.t() | Ash.Query.t() | Ash.ActionInput.t()
Functions
@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 toerrors
- Error or list of errors to add
@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 tocallback
- Function that processes the resultopts
- Options including:prepend?
(ignored for Query)
@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 resultopts
- 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
before_transaction/3
for hooks that run before the transaction startsaround_transaction/3
for hooks that wrap the entire transactionafter_action/3
for hooks that run after the action (inside transaction, success only)
@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 callbackopts
- 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_transaction/3
andafter_transaction/3
for simpler hooksaround_action/2
for wrapping just the action execution
@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 tocallback
- Function that takes and returns the subjectopts
- Options including:prepend?
to add at beginning
@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
after_transaction/3
for hooks that run after the transaction completesaround_transaction/3
for hooks that wrap the entire transactionbefore_action/3
for hooks that run before the action (inside transaction)
@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 fromarguments
- Single argument name or list of argument names to delete
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 fromargument
- The argument name (atom or string)
Gets an argument value from the subject.
Supports both atom and string argument names.
Parameters
subject
- The subject to get argument fromargument
- The argument name (atom or string)
Gets an argument value from the subject
Supports both atom and string argument names.
Parameters
subject
- The subject to get argument fromargument
- The argument name (atom or string)default
- The default value to return if the argument is not found
@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 fromname
- The argument or attribute name (atom or string)default
- The default value to return if the argument or attribute is not found
Puts a key-value pair into the subject's context.
Parameters
subject
- The subject to update context onkey
- The context keyvalue
- The value to store
Sets a single argument on the subject.
Parameters
subject
- The subject to set argument onargument
- The argument name (atom or string)value
- The value to set
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 onarguments
- Map of argument names to values
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 oncontext
- Map of context data to merge
@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 argumentsget_argument/2-3
for retrieving argument values- Action argument definitions with
public?: false
@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