diff --git a/.formatter.exs b/.formatter.exs index dad98164..e8cef808 100644 --- a/.formatter.exs +++ b/.formatter.exs @@ -167,6 +167,7 @@ spark_locals_without_parens = [ prefix: 1, prepare: 1, prepare: 2, + previous_values?: 1, primary?: 1, primary_key?: 1, private?: 1, diff --git a/benchmarks/embedded_resources.exs b/benchmarks/embedded_resources.exs index 892b4ae7..102f0ba4 100644 --- a/benchmarks/embedded_resources.exs +++ b/benchmarks/embedded_resources.exs @@ -39,12 +39,6 @@ Resource |> Ash.Changeset.for_create(:create, %{embeds: embeds_input, maps: embeds_input}) |> Api.create!() -# :eflame.apply(fn -> -# Resource -# |> Ash.Changeset.for_create(:create, %{embeds: embeds_input}) -# |> Api.create!() -# end, []) - Benchee.run( %{ embeds: fn -> diff --git a/documentation/dsls/DSL:-Ash.Notifier.PubSub.md b/documentation/dsls/DSL:-Ash.Notifier.PubSub.md index 4c78bef7..86667b5a 100644 --- a/documentation/dsls/DSL:-Ash.Notifier.PubSub.md +++ b/documentation/dsls/DSL:-Ash.Notifier.PubSub.md @@ -80,6 +80,7 @@ publish :assign, "assigned" | Name | Type | Default | Docs | |------|------|---------|------| +| [`previous_values?`](#pub_sub-publish-previous_values?){: #pub_sub-publish-previous_values? } | `boolean` | `true` | Whether or not to publish messages with both the new values and the old values for referencing changed attributes | | [`event`](#pub_sub-publish-event){: #pub_sub-publish-event } | `String.t` | | The name of the event to publish. Defaults to the action name | | [`dispatcher`](#pub_sub-publish-dispatcher){: #pub_sub-publish-dispatcher } | `atom` | | The module to use as a dispatcher. If none is set, the pubsub module provided is used. | @@ -123,6 +124,7 @@ publish_all :create, "created" | Name | Type | Default | Docs | |------|------|---------|------| | [`action`](#pub_sub-publish_all-action){: #pub_sub-publish_all-action } | `atom` | | The name of the action that should be published | +| [`previous_values?`](#pub_sub-publish_all-previous_values?){: #pub_sub-publish_all-previous_values? } | `boolean` | `true` | Whether or not to publish messages with both the new values and the old values for referencing changed attributes | | [`event`](#pub_sub-publish_all-event){: #pub_sub-publish_all-event } | `String.t` | | The name of the event to publish. Defaults to the action name | | [`dispatcher`](#pub_sub-publish_all-dispatcher){: #pub_sub-publish_all-dispatcher } | `atom` | | The module to use as a dispatcher. If none is set, the pubsub module provided is used. | diff --git a/lib/ash/embeddable_type.ex b/lib/ash/embeddable_type.ex index a395f7ed..d00f19aa 100644 --- a/lib/ash/embeddable_type.ex +++ b/lib/ash/embeddable_type.ex @@ -512,7 +512,12 @@ defmodule Ash.EmbeddableType do |> Ash.EmbeddableType.copy_source(constraints[:__source__]) |> Ash.Query.load(constraints[:load] || []) - query = Ash.Query.sort(query, constraints[:sort]) + query = + if constraints[:sort] do + Ash.Query.sort(query, constraints[:sort]) + else + query + end case ShadowApi.read(query) do {:ok, result} -> diff --git a/lib/ash/policy/check/changing_attributes.ex b/lib/ash/policy/check/changing_attributes.ex index cada1bdd..a4c1a5c5 100644 --- a/lib/ash/policy/check/changing_attributes.ex +++ b/lib/ash/policy/check/changing_attributes.ex @@ -1,6 +1,9 @@ defmodule Ash.Policy.Check.ChangingAttributes do @moduledoc "This check is true when attribute changes correspond to the provided options." - use Ash.Policy.SimpleCheck + use Ash.Policy.FilterCheckWithContext + + import Ash.Filter.TemplateHelpers + require Ash.Expr @impl true def describe(opts) do @@ -26,43 +29,33 @@ defmodule Ash.Policy.Check.ChangingAttributes do end @impl true - def requires_original_data?(_, opts) do - Enum.any?(opts[:changing], fn - {_key, further} -> - Keyword.has_key?(further, :from) + def filter(_actor, %{changeset: %Ash.Changeset{} = changeset}, options) do + Enum.reduce_while(options[:changing], true, fn {attribute, opts}, expr -> + if Keyword.has_key?(opts, :from) && changeset.action_type == :create do + {:halt, false} + else + if Ash.Changeset.changing_attribute?(changeset, attribute) do + case {Keyword.fetch(opts, :from), Keyword.fetch(opts, :to)} do + {:error, :error} -> + {:cont, expr} - _ -> - false - end) - end + {{:ok, from}, {:ok, to}} -> + Ash.Expr.expr( + ^expr and not (ref(attribute) == ^from and ^atomic_ref(attribute) == ^to) + ) - @impl true - def match?(_actor, %{changeset: %Ash.Changeset{} = changeset}, opts) do - Enum.all?(opts[:changing], fn - {attribute, opts} -> - if Keyword.has_key?(opts, :from) && changeset.action_type == :create do - false - else - case Ash.Changeset.fetch_change(changeset, attribute) do - {:ok, new_value} -> - opts == [] || - Enum.all?(opts, fn - {:to, value} -> - new_value == value + {{:ok, from}, :error} -> + Ash.Expr.expr(^expr and ref(attribute) != ^from) - {:from, value} -> - Map.get(changeset.data, attribute) == value - end) - - _ -> - false + {:error, {:ok, to}} -> + Ash.Expr.expr(^expr and ^atomic_ref(attribute) != ^to) end + else + {:cont, expr} end - - attribute -> - match?({:ok, _}, Ash.Changeset.fetch_change(changeset, attribute)) + end end) end - def match?(_, _, _), do: false + def filter(_, _, _), do: false end diff --git a/lib/ash/type/type.ex b/lib/ash/type/type.ex index 29082181..c2f3ee28 100644 --- a/lib/ash/type/type.ex +++ b/lib/ash/type/type.ex @@ -726,7 +726,7 @@ defmodule Ash.Type do |> case do {terms, []} -> if type.custom_apply_constraints_array?() do - case type.apply_constraints_array(Enum.reverse(term), constraints) do + case type.apply_constraints_array(Enum.reverse(terms), constraints) do :ok -> {:ok, term} other -> other end diff --git a/test/embedded_resource_test.exs b/test/embedded_resource_test.exs index 72683cff..061124fd 100644 --- a/test/embedded_resource_test.exs +++ b/test/embedded_resource_test.exs @@ -202,7 +202,7 @@ defmodule Ash.Test.Changeset.EmbeddedResourceTest do end test "embedded resources can be created" do - assert %{profile: %Profile{}, tags: [%Tag{}, %Tag{}]} = + assert %{profile: %Profile{}, tags: [%Tag{name: "trainer"}, %Tag{name: "human"}]} = Changeset.for_create( Author, :create,