improvement: undo some data tracking changes that didn't work

improvement: continue improving sparse forms
This commit is contained in:
Zach Daniel 2021-08-15 11:30:46 -04:00
parent f5b1d56936
commit f194e157ae
3 changed files with 212 additions and 206 deletions

View file

@ -128,6 +128,7 @@ defmodule AshPhoenix.Form.Auto do
forms: [], forms: [],
sparse?: auto_opts[:sparse_lists?], sparse?: auto_opts[:sparse_lists?],
managed_relationship: {relationship.source, relationship.name}, managed_relationship: {relationship.source, relationship.name},
must_load?: Ash.Changeset.ManagedRelationshipHelpers.must_load?(manage_opts),
updater: fn opts -> updater: fn opts ->
opts = opts =
opts opts

View file

@ -2078,7 +2078,7 @@ defmodule AshPhoenix.Form do
update_action = update_action =
opts[:update_action] || opts[:update_action] ||
raise AshPhoenix.Form.NoActionConfigured, raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])), path: Enum.reverse(trail, [key]),
action: :update action: :update
data = data =
@ -2088,7 +2088,7 @@ defmodule AshPhoenix.Form do
case call_data(opts[:data], prev_data_trail) do case call_data(opts[:data], prev_data_trail) do
%Ash.NotLoaded{} -> %Ash.NotLoaded{} ->
raise AshPhoenix.Form.NoDataLoaded, raise AshPhoenix.Form.NoDataLoaded,
path: Enum.reverse(trail, Enum.reverse(trail, [key])) path: Enum.reverse(trail, [key])
other -> other ->
other other
@ -2131,6 +2131,8 @@ defmodule AshPhoenix.Form do
end) end)
end end
end end
else
nil
end end
else else
if (opts[:type] || :single) == :single do if (opts[:type] || :single) == :single do
@ -2313,7 +2315,7 @@ defmodule AshPhoenix.Form do
read_action = read_action =
opts[:read_action] || opts[:read_action] ||
raise AshPhoenix.Form.NoActionConfigured, raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])), path: Enum.reverse(trail, [key]),
action: :read action: :read
resource = resource =
@ -2335,7 +2337,7 @@ defmodule AshPhoenix.Form do
create_action = create_action =
opts[:create_action] || opts[:create_action] ||
raise AshPhoenix.Form.NoActionConfigured, raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])), path: Enum.reverse(trail, [key]),
action: :create action: :create
resource = resource =
@ -2365,7 +2367,7 @@ defmodule AshPhoenix.Form do
read_action = read_action =
opts[:read_action] || opts[:read_action] ||
raise AshPhoenix.Form.NoActionConfigured, raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])), path: Enum.reverse(trail, [key]),
action: :read action: :read
resource = resource =
@ -2387,7 +2389,7 @@ defmodule AshPhoenix.Form do
create_action = create_action =
opts[:create_action] || opts[:create_action] ||
raise AshPhoenix.Form.NoActionConfigured, raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])), path: Enum.reverse(trail, [key]),
action: :create action: :create
resource = resource =
@ -2442,7 +2444,7 @@ defmodule AshPhoenix.Form do
update_action = update_action =
opts[:update_action] || opts[:update_action] ||
raise AshPhoenix.Form.NoActionConfigured, raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])), path: Enum.reverse(trail, [key]),
action: :update action: :update
for_action(data, update_action, for_action(data, update_action,
@ -2460,7 +2462,7 @@ defmodule AshPhoenix.Form do
destroy_action = destroy_action =
opts[:destroy_action] || opts[:destroy_action] ||
raise AshPhoenix.Form.NoActionConfigured, raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])), path: Enum.reverse(trail, [key]),
action: :destroy action: :destroy
for_action(data, destroy_action, for_action(data, destroy_action,
@ -2480,7 +2482,7 @@ defmodule AshPhoenix.Form do
create_action = create_action =
opts[:create_action] || opts[:create_action] ||
raise AshPhoenix.Form.NoActionConfigured, raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])), path: Enum.reverse(trail, [key]),
action: :create action: :create
resource = resource =
@ -2508,7 +2510,7 @@ defmodule AshPhoenix.Form do
read_action = read_action =
opts[:read_action] || opts[:read_action] ||
raise AshPhoenix.Form.NoActionConfigured, raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])), path: Enum.reverse(trail, [key]),
action: :read action: :read
for_action(resource, read_action, for_action(resource, read_action,
@ -2540,7 +2542,7 @@ defmodule AshPhoenix.Form do
read_action = read_action =
opts[:read_action] || opts[:read_action] ||
raise AshPhoenix.Form.NoActionConfigured, raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])), path: Enum.reverse(trail, [key]),
action: :read action: :read
form = form =
@ -2557,162 +2559,43 @@ defmodule AshPhoenix.Form do
{[form | forms], data} {[form | forms], data}
else else
if opts[:sparse?] do case find_form_match(data, form_params, opts) do
case find_sparse_match(data, form_params, opts) do [nil | rest] ->
nil -> create_action =
create_action = opts[:create_action] ||
opts[:create_action] || raise AshPhoenix.Form.NoActionConfigured,
raise AshPhoenix.Form.NoActionConfigured, path: Enum.reverse(trail, [key]),
path: Enum.reverse(trail, Enum.reverse(trail, [key])), action: :create
action: :create
resource = resource =
opts[:create_resource] || opts[:resource] || opts[:create_resource] || opts[:resource] ||
raise AshPhoenix.Form.NoResourceConfigured, raise AshPhoenix.Form.NoResourceConfigured,
path: Enum.reverse(trail, [key]) path: Enum.reverse(trail, [key])
form = form =
for_action(resource, create_action, for_action(resource, create_action,
params: Map.put(form_params, "_index", to_string(original_index)), params: Map.put(form_params, "_index", to_string(original_index)),
forms: opts[:forms] || [], forms: opts[:forms] || [],
errors: error?, errors: error?,
prev_data_trail: prev_data_trail, prev_data_trail: prev_data_trail,
manage_relationship_source: manage_relationship_source: manage_relationship_source(source_changeset, opts),
manage_relationship_source(source_changeset, opts), as: name <> "[#{key}][#{index}]",
as: name <> "[#{key}][#{index}]", id: id <> "_#{key}_#{index}",
id: id <> "_#{key}_#{index}", data_updates: updates_for_index(further, index)
data_updates: updates_for_index(further, index) )
)
{[form | forms], data} {[form | forms], rest}
data -> [data | rest] ->
form = form =
if map(form_params)["_form_type"] == "destroy" do if map(form_params)["_form_type"] == "destroy" do
destroy_action = destroy_action =
opts[:destroy_action] || opts[:destroy_action] ||
raise AshPhoenix.Form.NoActionConfigured, raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])), path: Enum.reverse(trail, [key]),
action: :destroy action: :destroy
for_action(data, destroy_action, for_action(data, destroy_action,
params: Map.put(form_params, "_index", to_string(original_index)),
forms: opts[:forms] || [],
errors: error?,
prev_data_trail: prev_data_trail,
manage_relationship_source:
manage_relationship_source(source_changeset, opts),
as: name <> "[#{key}][#{index}]",
id: id <> "_#{key}_#{index}",
data_updates: updates_for_index(further, index)
)
else
update_action =
opts[:update_action] ||
raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])),
action: :update
for_action(data, update_action,
params: Map.put(form_params, "_index", to_string(original_index)),
forms: opts[:forms] || [],
errors: error?,
prev_data_trail: prev_data_trail,
manage_relationship_source:
manage_relationship_source(source_changeset, opts),
as: name <> "[#{key}][#{index}]",
id: id <> "_#{key}_#{index}",
data_updates: updates_for_index(further, index)
)
end
{[form | forms], data}
end
else
case data do
[nil | rest] ->
create_action =
opts[:create_action] ||
raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])),
action: :create
resource =
opts[:create_resource] || opts[:resource] ||
raise AshPhoenix.Form.NoResourceConfigured,
path: Enum.reverse(trail, [key])
form =
for_action(resource, create_action,
params: Map.put(form_params, "_index", to_string(original_index)),
forms: opts[:forms] || [],
errors: error?,
prev_data_trail: prev_data_trail,
manage_relationship_source:
manage_relationship_source(source_changeset, opts),
as: name <> "[#{key}][#{index}]",
id: id <> "_#{key}_#{index}",
data_updates: updates_for_index(further, index)
)
{[form | forms], rest}
[data | rest] ->
form =
if map(form_params)["_form_type"] == "destroy" do
destroy_action =
opts[:destroy_action] ||
raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])),
action: :destroy
for_action(data, destroy_action,
params: form_params,
forms: opts[:forms] || [],
errors: error?,
prev_data_trail: prev_data_trail,
manage_relationship_source:
manage_relationship_source(source_changeset, opts),
as: name <> "[#{key}][#{index}]",
id: id <> "_#{key}_#{index}",
data_updates: updates_for_index(further, index)
)
else
update_action =
opts[:update_action] ||
raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])),
action: :update
for_action(data, update_action,
params: form_params,
forms: opts[:forms] || [],
errors: error?,
prev_data_trail: prev_data_trail,
manage_relationship_source:
manage_relationship_source(source_changeset, opts),
as: name <> "[#{key}][#{index}]",
id: id <> "_#{key}_#{index}",
data_updates: updates_for_index(further, index)
)
end
{[form | forms], rest}
[] ->
resource =
opts[:create_resource] || opts[:resource] ||
raise AshPhoenix.Form.NoResourceConfigured,
path: Enum.reverse(trail, [key])
create_action =
opts[:create_action] ||
raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, Enum.reverse(trail, [key])),
action: :create
form =
for_action(resource, create_action,
params: form_params, params: form_params,
forms: opts[:forms] || [], forms: opts[:forms] || [],
errors: error?, errors: error?,
@ -2723,68 +2606,185 @@ defmodule AshPhoenix.Form do
id: id <> "_#{key}_#{index}", id: id <> "_#{key}_#{index}",
data_updates: updates_for_index(further, index) data_updates: updates_for_index(further, index)
) )
else
update_action =
opts[:update_action] ||
raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, [key]),
action: :update
{[form | forms], []} for_action(data, update_action,
end params: form_params,
forms: opts[:forms] || [],
errors: error?,
prev_data_trail: prev_data_trail,
manage_relationship_source:
manage_relationship_source(source_changeset, opts),
as: name <> "[#{key}][#{index}]",
id: id <> "_#{key}_#{index}",
data_updates: updates_for_index(further, index)
)
end
{[form | forms], rest}
[] ->
resource =
opts[:create_resource] || opts[:resource] ||
raise AshPhoenix.Form.NoResourceConfigured,
path: Enum.reverse(trail, [key])
create_action =
opts[:create_action] ||
raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(trail, [key]),
action: :create
form =
for_action(resource, create_action,
params: form_params,
forms: opts[:forms] || [],
errors: error?,
prev_data_trail: prev_data_trail,
manage_relationship_source: manage_relationship_source(source_changeset, opts),
as: name <> "[#{key}][#{index}]",
id: id <> "_#{key}_#{index}",
data_updates: updates_for_index(further, index)
)
{[form | forms], []}
end end
end end
end) end)
|> add_forms_for_remaining_data(
opts,
prev_data_trail,
key,
source_changeset,
name,
id,
further,
error?
)
|> elem(0) |> elem(0)
|> Enum.reverse() |> Enum.reverse()
end end
end end
defp find_sparse_match(data, form_params, opts) do defp add_forms_for_remaining_data(
find_resource = {forms, remaining_data},
case map(form_params)["_form_type"] || "update" do opts,
"destroy" -> opts[:destroy_resource] || opts[:resource] prev_data_trail,
"update" -> opts[:update_resource] || opts[:resource] key,
_ -> nil source_changeset,
end name,
id,
further,
error?
) do
if opts[:sparse?] do
offset = Enum.count(forms)
if find_resource do update_action =
pkey_fields = Ash.Resource.Info.primary_key(find_resource) opts[:update_action] ||
raise AshPhoenix.Form.NoActionConfigured,
path: Enum.reverse(prev_data_trail, [key]),
action: :update
pkey = all_forms =
Enum.map(pkey_fields, fn field -> remaining_data
Ash.Resource.Info.attribute(find_resource, field) |> List.wrap()
|> Enum.with_index()
|> Enum.reduce(Enum.reverse(forms), fn {data, index}, forms ->
index = index + offset
[
for_action(data, update_action,
errors: error?,
prev_data_trail: prev_data_trail,
forms: opts[:forms] || [],
manage_relationship_source: manage_relationship_source(source_changeset, opts),
as: name <> "[#{key}][#{index}]",
id: id <> "_#{key}_#{index}",
data_updates: updates_for_index(further, index)
)
| forms
]
end) end)
|> Enum.reverse()
casted_pkey = {all_forms, []}
Enum.reduce_while(pkey, {:ok, %{}}, fn attribute, {:ok, key_search} -> else
fetched = {forms, remaining_data}
case Map.fetch(form_params, attribute.name) do end
{:ok, value} -> end
{:ok, value}
:error -> defp find_form_match(data, form_params, opts) do
Map.fetch(form_params, to_string(attribute.name)) match_index =
end if opts[:sparse?] do
find_resource =
case data do
data when data in [nil, []] ->
nil
case fetched do [%resource{} | _] ->
{:ok, value} -> resource
case Ash.Type.cast_input(attribute.type, value, attribute.constraints) do
{:ok, value} -> {:cont, {:ok, Map.put(key_search, attribute.name, value)}} %resource{} ->
_ -> {:halt, :error} resource
end
if find_resource do
pkey_fields = Ash.Resource.Info.primary_key(find_resource)
pkey =
Enum.map(pkey_fields, fn field ->
Ash.Resource.Info.attribute(find_resource, field)
end)
casted_pkey =
Enum.reduce_while(pkey, {:ok, %{}}, fn attribute, {:ok, key_search} ->
fetched =
case Map.fetch(form_params, attribute.name) do
{:ok, value} ->
{:ok, value}
:error ->
Map.fetch(form_params, to_string(attribute.name))
end
case fetched do
{:ok, value} ->
case Ash.Type.cast_input(attribute.type, value, attribute.constraints) do
{:ok, value} -> {:cont, {:ok, Map.put(key_search, attribute.name, value)}}
_ -> {:halt, :error}
end
:error ->
{:halt, :error}
end end
end)
case casted_pkey do
{:ok, empty} when empty == %{} ->
nil
{:ok, pkey_search} ->
Enum.find_index(data, fn data ->
data && Map.take(data, pkey_fields) == pkey_search
end)
:error -> :error ->
{:halt, :error} nil
end end
end) end
case casted_pkey do
{:ok, empty} when empty == %{} ->
nil
{:ok, pkey_search} ->
Enum.find(data, fn data ->
data && Map.take(data, pkey_fields) == pkey_search
end)
:error ->
nil
end end
if match_index do
{match, rest} = List.pop_at(data, match_index)
[match | rest]
else
data
end end
end end
@ -2854,6 +2854,13 @@ defmodule AshPhoenix.Form do
fields -> Keyword.put(hidden, :_touched, fields) fields -> Keyword.put(hidden, :_touched, fields)
end end
hidden =
if form.params["_index"] && form.params["_index"] != "" do
Keyword.put(hidden, :_index, form.params["_index"])
else
hidden
end
errors = errors =
if form.errors do if form.errors do
if form.just_submitted? do if form.just_submitted? do

View file

@ -966,8 +966,6 @@ defmodule AshPhoenix.FormTest do
] ]
) )
Application.put_env(:foo, :bar, true)
updated_comment = updated_comment =
form form
|> AshPhoenix.Form.submit!( |> AshPhoenix.Form.submit!(