Packages

ash

3.19.2
3.29.3 3.29.2 3.29.1 3.29.0 3.28.0 3.27.8 3.27.7 3.27.6 3.27.5 3.27.4 3.27.3 3.27.2 3.27.1 3.27.0 3.26.0 3.25.2 3.25.1 3.25.0 retired 3.24.7 3.24.6 3.24.5 3.24.4 3.24.3 3.24.2 3.24.1 3.24.0 3.23.1 3.23.0 3.22.2 3.22.1 3.22.0 3.21.3 3.21.2 3.21.1 3.21.0 3.20.0 3.19.3 3.19.2 3.19.1 3.19.0 3.18.0 3.17.1 3.17.0 3.16.0 3.15.0 3.14.1 3.14.0 retired 3.13.2 3.13.1 3.13.0 3.12.0 3.11.3 3.11.2 3.11.1 3.11.0 3.10.1 3.10.0 3.9.0 3.8.0 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0 retired 3.6.3 retired 3.6.2 3.6.1 3.6.0 3.5.43 3.5.42 3.5.41 3.5.40 3.5.39 3.5.38 3.5.37 3.5.36 3.5.35 3.5.34 3.5.33 3.5.32 3.5.31 3.5.30 3.5.29 3.5.28 3.5.27 3.5.26 3.5.25 3.5.24 3.5.23 3.5.22 3.5.21 3.5.20 3.5.19 3.5.18 3.5.17 3.5.16 3.5.15 3.5.14 3.5.13 3.5.12 3.5.11 3.5.10 3.5.9 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.74 retired 3.4.73 3.4.72 3.4.71 3.4.70 3.4.69 3.4.68 3.4.67 3.4.66 3.4.65 3.4.64 3.4.63 3.4.62 3.4.61 3.4.60 3.4.59 3.4.58 3.4.57 3.4.56 3.4.55 3.4.54 3.4.53 3.4.52 3.4.51 3.4.50 3.4.49 3.4.48 3.4.47 3.4.46 3.4.45 3.4.44 3.4.43 3.4.42 3.4.41 3.4.40 3.4.39 3.4.38 3.4.37 3.4.36 3.4.35 3.4.34 3.4.33 3.4.32 3.4.31 3.4.30 3.4.29 3.4.28 3.4.27 3.4.26 3.4.25 3.4.24 3.4.23 3.4.22 3.4.21 3.4.20 3.4.19 3.4.18 3.4.17 3.4.16 3.4.15 3.4.14 3.4.13 3.4.12 3.4.11 3.4.10 3.4.9 3.4.8 3.4.7 3.4.6 3.4.5 3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.3 3.3.2 3.3.1 3.3.0 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 3.2.0 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.16 3.0.15 3.0.14 3.0.13 3.0.12 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.4 3.0.3 3.0.2 3.0.1 3.0.0 3.0.0-rc.46 3.0.0-rc.45 3.0.0-rc.44 3.0.0-rc.43 3.0.0-rc.42 3.0.0-rc.41 3.0.0-rc.40 3.0.0-rc.39 3.0.0-rc.38 3.0.0-rc.37 3.0.0-rc.36 3.0.0-rc.35 3.0.0-rc.34 3.0.0-rc.33 3.0.0-rc.32 3.0.0-rc.31 3.0.0-rc.29 3.0.0-rc.28 3.0.0-rc.27 3.0.0-rc.26 3.0.0-rc.25 3.0.0-rc.24 3.0.0-rc.23 3.0.0-rc.22 3.0.0-rc.21 3.0.0-rc.20 3.0.0-rc.19 3.0.0-rc.18 3.0.0-rc.17 3.0.0-rc.16 3.0.0-rc.15 3.0.0-rc.14 3.0.0-rc.13 3.0.0-rc.12 3.0.0-rc.11 3.0.0-rc.10 3.0.0-rc.9 3.0.0-rc.8 3.0.0-rc.7 3.0.0-rc.6 3.0.0-rc.5 3.0.0-rc.4 3.0.0-rc.3 3.0.0-rc.1 3.0.0-rc.0 2.21.15 2.21.14 2.21.13 2.21.12 2.21.11 2.21.10 2.21.9 2.21.8 2.21.7 2.21.6 2.21.5 2.21.4 2.21.3 2.21.2 2.21.1 2.21.0 2.20.3 2.20.2 2.20.1 2.20.0 2.19.14 2.19.13 2.19.12 2.19.11 2.19.10 2.19.9 2.19.8 2.19.7 2.19.6 2.19.5 2.19.4 2.19.3 retired 2.19.2 retired 2.19.1 retired 2.19.0 retired 2.18.2 2.18.1 2.18.0 2.17.24 2.17.23 2.17.22 2.17.21 2.17.20 2.17.19 2.17.18 2.17.17 2.17.16 2.17.15 2.17.14 2.17.13 2.17.12 2.17.11 2.17.10 2.17.9 2.17.8 2.17.7 2.17.6 2.17.5 2.17.4 2.17.3 2.17.2 2.17.1 2.17.0 2.16.1 2.16.0 2.15.20 2.15.19 2.15.18 2.15.17 2.15.16 2.15.15 2.15.14 2.15.13 2.15.12 2.15.11 2.15.10 2.15.9 2.15.8 2.15.7 2.15.6 2.15.5 2.15.4 2.15.2 2.15.1 2.15.0 2.14.21 2.14.20 2.14.19 2.14.18 2.14.17 2.14.16 2.14.15 2.14.14 2.14.13 2.14.12 2.14.11 2.14.10 2.14.9 2.14.8 2.14.7 2.14.6 2.14.5 2.14.4 2.14.3 2.14.2 2.14.1 2.14.0 2.13.4 retired 2.13.3 2.13.2 2.13.1 2.13.0 2.12.1 2.12.0 2.11.11 2.11.10 2.11.9 2.11.8 2.11.7 2.11.6 2.11.5 2.11.4 2.11.3 2.11.2 2.11.1 2.11.0 2.11.0-rc.3 2.11.0-rc.2 2.11.0-rc.1 2.11.0-rc.0 2.10.2 2.10.1 2.10.0 2.9.29 2.9.28 2.9.27 2.9.26 2.9.25 2.9.24 2.9.23 2.9.22 2.9.21 2.9.20 2.9.19 2.9.18 2.9.17 2.9.16 2.9.15 2.9.14 2.9.13 2.9.12 2.9.11 2.9.10 2.9.9 2.9.8 2.9.7 2.9.6 2.9.5 2.9.4 2.9.3 2.9.2 2.9.1 2.9.0 2.8.1 2.8.0 2.7.1 2.7.0 2.6.31 2.6.30 2.6.29 2.6.28 2.6.27 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.16 2.5.15 2.5.14 2.5.13 2.5.12 2.5.11 2.5.10 2.5.9 2.5.8 2.5.7 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.5.0-rc.6 2.5.0-rc.5 2.5.0-rc.4 2.5.0-rc.3 2.5.0-rc.2 2.5.0-rc.1 2.5.0-rc.0 2.4.30 2.4.29 2.4.28 2.4.27 2.4.26 2.4.25 2.4.24 2.4.23 2.4.22 2.4.21 2.4.20 2.4.19 2.4.18 2.4.17 2.4.16 2.4.15 2.4.14 2.4.13 2.4.12 2.4.11 2.4.10 2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 2.3.0 2.2.0 2.1.0 2.0.0 2.0.0-rc.15 2.0.0-rc.14 2.0.0-rc.13 2.0.0-rc.12 2.0.0-rc.11 2.0.0-rc.10 2.0.0-rc.9 2.0.0-rc.8 2.0.0-rc.7 2.0.0-rc.6 2.0.0-rc.5 2.0.0-rc.4 2.0.0-rc.3 2.0.0-rc.2 2.0.0-rc.1 2.0.0-rc.0 2.0.0-pre.8 2.0.0-pre.7 2.0.0-pre.6 2.0.0-pre.5 2.0.0-pre.4 2.0.0-pre.3 2.0.0-pre.2 2.0.0-pre.1 2.0.0-pre.0 1.53.3 1.53.2 1.53.0 1.52.0-rc.22 1.52.0-rc.21 1.52.0-rc.20 1.52.0-rc.19 1.52.0-rc.18 1.52.0-rc.17 1.52.0-rc.16 1.52.0-rc.15 1.52.0-rc.14 1.52.0-rc.13 1.52.0-rc.12 1.52.0-rc.11 1.52.0-rc.10 1.52.0-rc.9 1.52.0-rc.8 1.52.0-rc.7 1.52.0-rc.6 1.52.0-rc.5 1.52.0-rc.4 1.52.0-rc.3 1.52.0-rc.2 1.52.0-rc.1 1.52.0-rc.0 1.51.2 1.51.1 retired 1.51.0 1.50.21 1.50.20 1.50.19 1.50.18 1.50.17 1.50.16 1.50.15 1.50.14 1.50.13 1.50.12 1.50.11 1.50.10 1.50.9 1.50.8 1.50.7 1.50.6 1.50.5 1.50.4 1.50.3 1.50.2 1.50.1 1.50.0 1.49.0 1.48.0-rc.30 1.48.0-rc.29 1.48.0-rc.28 1.48.0-rc.27 1.48.0-rc.26 1.48.0-rc.25 1.48.0-rc.24 1.48.0-rc.23 1.48.0-rc.22 1.48.0-rc.21 1.48.0-rc.20 1.48.0-rc.19 1.48.0-rc.18 1.48.0-rc.17 1.48.0-rc.16 1.48.0-rc.15 1.48.0-rc.14 1.48.0-rc.13 1.48.0-rc.12 1.48.0-rc.11 1.48.0-rc.10 1.48.0-rc.9 1.48.0-rc.8 1.48.0-rc.7 1.48.0-rc.6 1.48.0-rc.5 1.48.0-rc.4 1.48.0-rc.3 1.48.0-rc.2 1.48.0-rc.1 1.48.0-rc.0 1.47.12 1.47.11 1.47.10 1.47.9 1.47.8 1.47.7 1.47.6 1.47.5 1.47.4 1.47.3 1.47.2 1.47.1 1.47.0 1.46.13 1.46.12 1.46.11 1.46.10 1.46.9 1.46.8 1.46.7 1.46.6 1.46.5 1.46.4 1.46.3 1.46.2 1.46.1 1.46.0 1.45.0-rc9 1.45.0-rc8 1.45.0-rc7 1.45.0-rc6 1.45.0-rc5 1.45.0-rc4 1.45.0-rc3 1.45.0-rc20 1.45.0-rc2 1.45.0-rc19 1.45.0-rc18 1.45.0-rc17 1.45.0-rc16 1.45.0-rc15 1.45.0-rc14 1.45.0-rc13 1.45.0-rc12 1.45.0-rc11 1.45.0-rc10 1.45.0-rc1 1.45.0-rc0 1.44.13 1.44.12 1.44.11 1.44.10 1.44.9 1.44.8 1.44.7 1.44.6 1.44.5 1.44.4 1.44.3 1.44.2 1.44.1 1.44.0 1.43.12 1.43.11 1.43.10 1.43.9 1.43.8 1.43.7 1.43.6 1.43.5 1.43.4 1.43.3 1.43.2 1.43.1 1.43.0 1.42.0 1.41.12 1.41.11 1.41.10 1.41.9 1.41.8 1.41.7 1.41.6 1.41.5 1.41.4 1.41.3 1.41.2 1.41.1 1.41.0 1.40.0 1.39.7 1.39.6 1.39.5 1.39.4 1.39.3 1.39.2 1.39.1 1.39.0 1.38.0 1.37.2 1.37.1 1.37.0 1.36.22 1.36.21 1.36.19 1.36.18 1.36.17 1.36.16 1.36.15 1.36.14 1.36.13 1.36.12 1.36.11 1.36.10 1.36.9 1.36.8 1.36.7 1.36.6 1.36.5 1.36.4 1.36.3 1.36.2 1.36.0 1.35.1 1.35.0 1.34.9 1.34.8 1.34.7 1.34.6 1.34.5 1.34.4 1.34.3 1.34.2 1.34.1 1.34.0 1.33.0 1.32.2 1.32.1 1.32.0 1.31.1 1.31.0 1.30.2 1.30.1 1.29.0-rc1 1.29.0-rc0 1.28.1 1.28.0 1.27.1 1.27.0 1.26.13 1.26.12 1.26.11 1.26.10 1.26.9 1.26.8 1.26.7 1.26.6 1.26.5 1.26.4 1.26.2 1.26.1 1.26.0 1.25.8 1.25.7 1.25.6 1.25.5 1.25.4 1.25.3 1.25.2 1.25.1 1.25.0 1.24.2 1.24.1 1.24.0 1.23.3 1.23.2 1.23.1 1.23.0 1.22.1 1.22.0 1.20.1 1.20.0 1.19.1 1.19.0 1.18.1 1.18.0 1.17.1 1.17.0 1.16.2 1.15.1 1.15.0 1.14.0 1.13.4 1.13.3 1.13.2 1.13.1 1.13.0 1.12.0 1.11.1 1.11.0 1.10.0 1.9.0 1.8.0 1.7.0 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.1 1.5.0 1.4.1 1.4.0 1.3.1 1.3.0 1.2.1 1.2.0 1.1.3 1.1.2 1.1.0 1.0.3 1.0.2 1.0.1 1.0.0 0.13.1 0.13.0 0.12.0 0.10.0 0.9.1 0.9.0 0.8.0 0.7.0 0.6.5 0.6.4 0.6.3 0.6.2 0.6.1 0.6.0 0.5.2 0.5.1 0.5.0 0.4.0 0.3.0 0.2.0 0.1.9 0.1.8 0.1.3 0.1.1 0.1.0

A declarative, extensible framework for building Elixir applications.

Security advisory: This version has known vulnerabilities. View advisories

Current section

Files

Jump to
ash lib ash resource change change.ex
Raw

lib/ash/resource/change/change.ex

# SPDX-FileCopyrightText: 2019 ash contributors <https://github.com/ash-project/ash/graphs/contributors>
#
# SPDX-License-Identifier: MIT
defmodule Ash.Resource.Change do
@moduledoc """
The behaviour for an action-specific resource change.
`c:init/1` is defined automatically by `use Ash.Resource.Change`, but can be implemented if you want to validate/transform any
options passed to the module.
The main function is `c:change/3`. It takes the changeset, any options that were provided
when this change was configured on a resource, and the context, which currently only has
the actor.
"""
defstruct [
:change,
:on,
:only_when_valid?,
:description,
:always_atomic?,
where: [],
__spark_metadata__: nil
]
require Ash.BehaviourHelpers
@type t :: %__MODULE__{__spark_metadata__: Spark.Dsl.Entity.spark_meta()}
@type ref :: {module(), Keyword.t()} | module()
@doc false
def schema do
[
on: [
type: {:wrap_list, {:in, [:create, :update, :destroy]}},
default: [:create, :update],
doc: """
The action types the change should run on. Destroy actions are omitted by default as most changes don't make sense for a destroy.
"""
],
only_when_valid?: [
type: :boolean,
default: false,
doc: """
If the change should only be run on valid changes. By default, all changes are run unless stated otherwise here.
"""
],
description: [
type: :string,
doc: "An optional description for the change"
],
change: [
type:
{:spark_function_behaviour, Ash.Resource.Change, Ash.Resource.Change.Builtins,
{Ash.Resource.Change.Function, 2}},
doc: """
The module and options for a change. Also accepts a function that takes the changeset and the context. See `Ash.Resource.Change.Builtins` for builtin changes.
""",
required: true
],
where: [
type:
{:wrap_list,
{:spark_function_behaviour, Ash.Resource.Validation, Ash.Resource.Validation.Builtins,
{Ash.Resource.Validation.Function, 2}}},
required: false,
default: [],
doc: """
Validations that should pass in order for this change to apply. These validations failing will result in this change being ignored.
"""
],
always_atomic?: [
type: :boolean,
default: false,
doc:
"By default, changes are only run atomically if all changes will be run atomically or if there is no `change/3` callback defined. Set this to `true` to run it atomically always."
]
]
end
@doc false
def action_schema do
Keyword.delete(schema(), :on)
end
@doc false
def change({module, opts}) when is_atom(module) do
if Keyword.keyword?(opts) do
{:ok, {module, opts}}
else
{:error, "Expected opts to be a keyword, got: #{inspect(opts)}"}
end
end
def change(module) when is_atom(module), do: {:ok, {module, []}}
def change(other) do
{:error, "Expected a module and opts, got: #{inspect(other)}"}
end
def change(module, changeset, opts, context) do
Ash.BehaviourHelpers.check_type!(module, module.change(changeset, opts, context), [
%Ash.Changeset{}
])
end
defmodule Context do
@moduledoc """
The context for a change.
This is passed into various callbacks for `Ash.Resource.Change`.
"""
defstruct [:actor, :tenant, :authorize?, :tracer, bulk?: false, source_context: %{}]
@type t :: %__MODULE__{
actor: Ash.Resource.record() | nil,
tenant: term(),
source_context: map(),
authorize?: boolean() | nil,
tracer: Ash.Tracer.t() | [Ash.Tracer.t()] | nil,
bulk?: boolean
}
end
@type context :: Context.t()
@callback init(opts :: Keyword.t()) :: {:ok, Keyword.t()} | {:error, term}
@callback change(changeset :: Ash.Changeset.t(), opts :: Keyword.t(), context :: Context.t()) ::
Ash.Changeset.t()
@doc """
Replaces `change/3` for batch actions, allowing to optimize changes for bulk actions.
You can define only `batch_change/3`, and it will be used for both single and batch actions.
It cannot, however, be used in place of the `atomic/3` callback.
"""
@callback batch_change(
changesets :: [Ash.Changeset.t()],
opts :: Keyword.t(),
context :: Context.t()
) ::
Enumerable.t(Ash.Changeset.t())
@doc """
Runs on each batch before it is dispatched to the data layer.
"""
@callback before_batch(
changesets :: [Ash.Changeset.t()],
opts :: Keyword.t(),
context :: Context.t()
) ::
Enumerable.t(Ash.Changeset.t() | Ash.Notifier.Notification.t())
@doc """
Runs on each batch result after it is dispatched to the data layer.
"""
@callback after_batch(
changesets_and_results :: [{Ash.Changeset.t(), Ash.Resource.record()}],
opts :: Keyword.t(),
context :: Context.t()
) ::
:ok
| Enumerable.t(
{:ok, Ash.Resource.record()}
| {:error, Ash.Error.t()}
| Ash.Notifier.Notification.t()
)
@doc """
Whether or not batch callbacks should be run (if they are defined). Defaults to `true`.
"""
@callback batch_callbacks?(
changesets_or_query :: [Ash.Changeset.t()] | Ash.Query.t(),
opts :: Keyword.t(),
context :: Context.t()
) ::
boolean
@doc """
The atomic version of a change. This is called instead of `c:change/3` when running
atomically. Atomic changes are expressed as maps of attribute names to expressions
that will be evaluated in the data layer.
## Return Values
- `{:atomic, atomics}` - A map of attribute names to expressions for updating existing records.
Used during the UPDATE phase (for updates or the ON CONFLICT clause of upserts).
The expression can reference existing values using `atomic_ref(:field)`.
- `{:atomic_set, atomics}` - A map of attribute names to expressions for creating records.
Used during the INSERT phase of create actions. Cannot use `atomic_ref/1` since there
is no existing row to reference. For update actions, behaves the same as `{:atomic, ...}`.
- A list containing both `{:atomic, ...}` and `{:atomic_set, ...}` tuples when you need
to set values for both the INSERT and UPDATE phases (useful for upserts).
- `{:ok, changeset}` - Return a modified changeset (the change was applied in-memory).
- `{:not_atomic, reason}` - Indicates the change cannot run atomically.
- `:ok` - No changes needed.
- `{:error, term}` - An error occurred.
## Examples
# Simple atomic update (increment counter)
def atomic(_changeset, _opts, _context) do
{:atomic, %{counter: expr(counter + 1)}}
end
# Atomic set for creates (set timestamp)
def atomic(_changeset, _opts, _context) do
{:atomic_set, %{created_at: expr(now())}}
end
# Both insert and update values (for upserts)
def atomic(_changeset, _opts, _context) do
[
{:atomic_set, %{created_at: expr(now())}},
{:atomic, %{updated_at: expr(now())}}
]
end
"""
@callback atomic(changeset :: Ash.Changeset.t(), opts :: Keyword.t(), context :: Context.t()) ::
{:ok, Ash.Changeset.t()}
| {:atomic, %{optional(atom()) => Ash.Expr.t() | {:atomic, Ash.Expr.t()}}}
| {:atomic, Ash.Changeset.t(), %{optional(atom()) => Ash.Expr.t()}}
| {:atomic, Ash.Changeset.t(), %{optional(atom()) => Ash.Expr.t()},
list(
{:atomic, involved_fields :: list(atom) | :*, condition_expr :: Ash.Expr.t(),
error_expr :: Ash.Expr.t()}
)}
| {:atomic, %{optional(atom()) => Ash.Expr.t()},
list(
{:atomic, involved_fields :: list(atom) | :*, condition_expr :: Ash.Expr.t(),
error_expr :: Ash.Expr.t()}
)}
| {:atomic_set, %{optional(atom()) => Ash.Expr.t() | {:atomic, Ash.Expr.t()}}}
| list(
{:atomic, %{optional(atom()) => Ash.Expr.t() | {:atomic, Ash.Expr.t()}}}
| {:atomic_set, %{optional(atom()) => Ash.Expr.t() | {:atomic, Ash.Expr.t()}}}
)
| {:not_atomic, String.t()}
| :ok
| {:error, term()}
@callback atomic?() :: boolean
@callback has_change?() :: boolean
@callback has_batch_change?() :: boolean
@callback has_after_batch?() :: boolean
@callback has_before_batch?() :: boolean
@optional_callbacks before_batch: 3,
after_batch: 3,
batch_change: 3,
change: 3,
atomic: 3
defmacro __using__(_) do
quote do
@behaviour Ash.Resource.Change
@before_compile Ash.Resource.Change
import Ash.Expr
require Ash.Query
@impl true
def init(opts), do: {:ok, opts}
@impl true
def batch_callbacks?(_, _, _), do: true
defoverridable init: 1, batch_callbacks?: 3
end
end
defmacro __before_compile__(_) do
quote do
if !Module.defines?(__MODULE__, {:change, 3}, :def) &&
!Module.defines?(__MODULE__, {:atomic, 3}, :def) &&
!Module.defines?(__MODULE__, {:batch_change, 3}, :def) do
raise """
Must define at least one of the following functions in #{inspect(__MODULE__)}:
def change(changeset, opts, context)
def batch_change(changesets, opts, context)
def atomic(changeset, opts, context)
Perhaps you have a typo or have incorrectly defined one of the above functions?
"""
end
if Module.defines?(__MODULE__, {:change, 3}, :def) do
@impl true
def has_change?, do: true
else
if Module.defines?(__MODULE__, {:batch_change, 3}, :def) do
@impl true
def change(changeset, opts, context) do
changeset
|> simulate_before_batch(opts, context)
|> then(fn changeset ->
[changeset] |> batch_change(opts, context) |> Enum.at(0)
end)
|> simulate_after_batch(opts, context)
end
if Module.defines?(__MODULE__, {:before_batch, 3}, :def) do
defp simulate_before_batch(changeset, opts, context) do
Ash.Changeset.before_action(changeset, fn changeset ->
{[changeset], notifications} =
Enum.split_with(
apply(__MODULE__, :before_batch, [[changeset], opts, context]),
fn
%Ash.Notifier.Notification{} ->
false
%Ash.Changeset{} ->
true
other ->
raise "Expected before_batch/3 to return a list of changesets and notifications, got: #{inspect(other)}"
end
)
{changeset, %{notifications: notifications}}
end)
end
else
defp simulate_before_batch(changeset, _opts, _context) do
changeset
end
end
if Module.defines?(__MODULE__, {:after_batch, 3}, :def) do
defp simulate_after_batch(changeset, opts, context) do
Ash.Changeset.after_action(changeset, fn changeset, result ->
apply(__MODULE__, :after_batch, [[{changeset, result}], opts, context])
|> then(fn
:ok -> [{:ok, result}]
other -> other
end)
|> Enum.reduce({[], [], []}, fn item, {records, errors, notifications} ->
case item do
{:ok, record} -> {[record | records], errors, notifications}
{:error, error} -> {records, [error | errors], notifications}
%Ash.Notifier.Notification{} -> {records, errors, [item | notifications]}
end
end)
|> case do
{[record], [], notifications} ->
{:ok, record, notifications}
{other, [], _notifications} ->
raise "Invalid return value from `after_batch/3`. Expected exactly one record: #{inspect(other)}"
{_, errors, _notifications} ->
{:error, errors}
end
end)
end
else
defp simulate_after_batch(changeset, _opts, _context), do: changeset
end
@impl true
def has_change?, do: true
else
@impl true
def has_change?, do: false
end
end
if Module.defines?(__MODULE__, {:batch_change, 3}, :def) do
@impl true
def has_batch_change?, do: true
else
@impl true
def has_batch_change?, do: false
end
if Module.defines?(__MODULE__, {:before_batch, 3}, :def) do
@impl true
def has_before_batch?, do: true
else
@impl true
def has_before_batch?, do: false
end
if Module.defines?(__MODULE__, {:after_batch, 3}, :def) do
@impl true
def has_after_batch?, do: true
else
@impl true
def has_after_batch?, do: false
end
if Module.defines?(__MODULE__, {:atomic, 3}, :def) do
if !Module.defines?(__MODULE__, {:atomic?, 0}, :def) do
@impl true
def atomic?, do: true
end
else
if !Module.defines?(__MODULE__, {:atomic?, 0}, :def) do
@impl true
def atomic?, do: false
end
@impl true
def atomic(_changeset, _opts, _context) do
{:not_atomic, "#{inspect(__MODULE__)} does not implement `atomic/3`"}
end
end
end
end
end