Created
November 6, 2021 15:22
-
-
Save zachdaniel/e5cfade3c5ddd8b60c198d35c1c297e5 to your computer and use it in GitHub Desktop.
Check if an AshPhoenix Form is changed
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defp changed?(form) do | |
is_changed?(form) || | |
Enum.any?(form.forms, fn {_key, forms} -> | |
forms | |
|> List.wrap() | |
|> Enum.any?(&changed?/1) | |
end) | |
end | |
defp is_changed?(form) do | |
if form.type == :create do | |
true | |
else | |
attributes_changed?(form) || arguments_changed?(form) | |
end | |
end | |
defp attributes_changed?(%{source: %Ash.Query{}}), do: false | |
defp attributes_changed?(form) do | |
changeset = form.source | |
form_keys = update_forms(form.form_keys) | |
changeset.attributes | |
|> Map.drop(Enum.map(form_keys, &elem(&1, 0))) | |
|> Map.delete(:last_editor_save) | |
|> Enum.any?(fn {key, value} -> | |
original_value = Map.get(changeset.data, key) || default(changeset.resource, key) | |
Comp.not_equal?(value, original_value) | |
end) | |
end | |
def arguments_changed?(form) do | |
changeset = form.source | |
form_keys = update_forms(form.form_keys) | |
changeset.arguments | |
|> Map.drop(Enum.map(form_keys, &elem(&1, 0))) | |
|> Enum.any?(fn {key, value} -> | |
action = | |
if is_atom(changeset.action) do | |
Ash.Resource.Info.action(changeset.resource, changeset.action) | |
else | |
changeset.action | |
end | |
original_value = default_argument(action, key) | |
value != original_value | |
end) | |
end | |
# if the value is the same as the default, we don't want to consider it as changed | |
defp default_argument(action, key) do | |
action.arguments | |
|> Enum.find(&(&1.name == key)) | |
|> case do | |
nil -> | |
nil | |
argument -> | |
cond do | |
is_nil(argument.default) -> | |
nil | |
is_function(argument.default) -> | |
argument.default.() | |
true -> | |
argument.default | |
end | |
end | |
end | |
defp default(resource, key) do | |
attribute = Ash.Resource.Info.attribute(resource, key) | |
cond do | |
is_nil(attribute.default) -> | |
nil | |
is_function(attribute.default) -> | |
attribute.default.() | |
true -> | |
attribute.default | |
end | |
end | |
# To prevent infinite loops when building configuration, each level is constructed as some keys | |
# along w/ an `updater` key. Here we call it if there is one. If this was done inside of `AshPhoenix.Form`, | |
# it would save any produced form options along w/ each form when checking, and save a `.changed?` key whenever | |
# the form was validated/created (since we have to traverse all the input anyway, this wouldn't be expensive. | |
defp update_forms(form_keys) do | |
form_keys | |
|> Enum.map(fn | |
{key, opts} -> | |
if opts[:updater] do | |
{key, Keyword.delete(opts[:updater].(opts), :updater)} | |
else | |
{key, opts} | |
end | |
end) | |
|> Enum.uniq_by(&elem(&1, 0)) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment