Skip to content

Commit

Permalink
fix: ensure atomic set_attribute behaves the same as non-atomic
Browse files Browse the repository at this point in the history
  • Loading branch information
zachdaniel committed Feb 17, 2025
1 parent a5d4dab commit 8780874
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 6 deletions.
15 changes: 14 additions & 1 deletion lib/ash/changeset/changeset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,8 @@ defmodule Ash.Changeset do
changeset.context
)

with {:atomic, changeset, atomic_changes, validations} <-
with {:ok, change_opts} <- module.init(change_opts),
{:atomic, changeset, atomic_changes, validations} <-
atomic_with_changeset(
module.atomic(changeset, change_opts, struct(Ash.Resource.Change.Context, context)),
changeset
Expand Down Expand Up @@ -1805,6 +1806,18 @@ defmodule Ash.Changeset do
end
end)
|> Ash.Changeset.hydrate_atomic_refs(actor, eager?: true)
|> then(fn changeset ->
if changeset.action.type == :update do
attributes =
changeset.attributes
|> Map.keys()
|> Enum.reject(&Ash.Resource.Info.attribute(changeset.resource, &1).allow_nil?)

require_values(changeset, :update, false, attributes)
else
changeset
end
end)
end

@doc """
Expand Down
43 changes: 39 additions & 4 deletions lib/ash/resource/change/set_attribute.ex
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ defmodule Ash.Resource.Change.SetAttribute do
Changeset.force_change_attribute(changeset, opts[:attribute], value)
end
else
if opts[:set_when_nil?] or
Changeset.get_attribute(changeset, opts[:attribute]) != nil do
if value != nil or opts[:set_when_nil?] do
Changeset.force_change_attribute(changeset, opts[:attribute], value)
else
changeset
Expand All @@ -81,13 +80,49 @@ defmodule Ash.Resource.Change.SetAttribute do
end

@impl true
def atomic(_changeset, opts, _context) do
def atomic(changeset, opts, context) do
value =
case opts[:value] do
value when is_function(value) -> value.()
value -> value
end

{:atomic, %{opts[:attribute] => value}}
if Ash.Expr.expr?(value) do
if opts[:new?] do
{:atomic,
%{
opts[:attribute] =>
expr(
if is_nil(^atomic_ref(opts[:attribute])) do
^value
else
^atomic_ref(opts[:attribute])
end
)
}}
else
if opts[:set_when_nil?] do
{:atomic, %{opts[:attribute] => value}}
else
if is_nil(value) do
{:ok, changeset}
else
{:atomic,
%{
opts[:attribute] =>
expr(
if is_nil(^value) do
^atomic_ref(opts[:attribute])
else
^value
end
)
}}
end
end
end
else
{:ok, change(changeset, opts, context)}
end
end
end
2 changes: 1 addition & 1 deletion test/actions/update_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ defmodule Ash.Test.Actions.UpdateTest do

update :set_private_attribute_to_nil do
accept []
change set_attribute(:non_nil_private, nil)
change set_attribute(:non_nil_private, nil, set_when_nil?: true)
end

update :set_private_attribute_from_arg do
Expand Down

0 comments on commit 8780874

Please sign in to comment.