lib/ash/resource/change/builtins.ex

defmodule Ash.Resource.Change.Builtins do
  @moduledoc """
  Built in changes that are available to all resources

  The functions in this module are imported by default in the actions section.
  """

  @doc """
  Relates the actor to the data being changed, as the provided relationship.
  Accepts the option `:allow_nil?`, which will not force an actor to be set.
  `:allow_nil?` defaults to `false`.
  """
  def relate_actor(relationship, opts \\ []) do
    opts =
      opts
      |> Keyword.put(:relationship, relationship)
      |> Keyword.put_new(:allow_nil?, false)

    {Ash.Resource.Change.RelateActor, opts}
  end

  @doc """
  Sets the attribute to the value provided.

  If a zero argument function is provided, it is called to determine the value.

  If a `arg(:arg_name)` is provided, the value will be read from the argument if supplied.
  If the argument specified is not given to the action, then nothing happens.
  """
  def set_attribute(attribute, value) do
    {Ash.Resource.Change.SetAttribute, attribute: attribute, value: value}
  end

  @doc """
  Sets the attribute to the value provided if the attribtue is not already being changed.

  If a zero argument function is provided, it is called to determine the value.

  Use `arg(:argument_name)` to use the value of the given argument. If the argument is not supplied then nothing happens.
  """
  def set_new_attribute(attribute, value) do
    {Ash.Resource.Change.SetAttribute, attribute: attribute, value: value, new?: true}
  end

  @doc """
  Clears a change off of the changeset before the action runs.

  Useful if a change is only used in validations but shouldn't ultimately be written to the data layer
  """
  def prevent_change(field) do
    {Ash.Resource.Change.PreventChange, field: field}
  end

  @doc """
  Calls `Ash.Changeset.manage_relationship/4` with the changeset and relationship provided, using the value provided for the named argument

  For example

  ```elixir
  change manage_relationship(:add_comments, :comments, on_missing: :ignore, on_match: :no_match, on_no_match: {:create, :add_comment_to_post}
  ```
  """
  def manage_relationship(argument, relationship_name \\ nil, opts) do
    relationship_name = relationship_name || argument

    {Ash.Resource.Change.ManageRelationship,
     [argument: argument, relationship: relationship_name, opts: opts]}
  end

  @doc """
  Merges the given query context. If an MFA is provided, it will be called with the changeset.

  The MFA should return `{:ok, context_to_be_merged}` or `{:error, term}`
  """
  @spec set_context(map | mfa) ::
          {atom, Keyword.t()}
  def set_context(context) do
    {Ash.Resource.Change.SetContext, context: context}
  end

  @doc """
  Passes the provided value into `changeset.api.load()`, after the action has completed.
  """
  def load(value) do
    {Ash.Resource.Change.Load, target: value}
  end

  @doc """
  Passes the provided value into `Ash.Changeset.select/3`
  """
  def select(value) do
    {Ash.Resource.Change.Select, target: value}
  end

  @doc """
  Passes the provided value into `Ash.Changeset.ensure_selected/2`
  """
  def ensure_selected(value) do
    {Ash.Resource.Change.Select, target: value, ensure?: true}
  end
end