Skip to content

Commit 6ebdcad

Browse files
committed
fixed casting empty embedded schemas with use_parent_field_for_type
1 parent 17b5c76 commit 6ebdcad

4 files changed

Lines changed: 87 additions & 5 deletions

File tree

lib/polymorphic_embed.ex

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,11 @@ defmodule PolymorphicEmbed do
125125

126126
raise_if_invalid_options(field, field_opts)
127127

128-
%{array?: array?, types_metadata: types_metadata} = field_opts
128+
%{
129+
array?: array?,
130+
use_parent_field_for_type: parent_field_for_type,
131+
types_metadata: types_metadata
132+
} = field_opts
129133

130134
required = Keyword.get(cast_opts, :required, false)
131135
with = Keyword.get(cast_opts, :with, nil)
@@ -172,7 +176,8 @@ defmodule PolymorphicEmbed do
172176
{:ok, nil} when not required ->
173177
Ecto.Changeset.put_change(changeset, field, nil)
174178

175-
{:ok, map} when map == %{} and not array? ->
179+
{:ok, map}
180+
when map == %{} and not array? and is_nil(parent_field_for_type) and not required ->
176181
changeset
177182

178183
{:ok, params_for_field} when array? ->

test/polymorphic_embed_test.exs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,69 @@ defmodule PolymorphicEmbedTest do
228228
insert_result
229229
end
230230

231+
test "infer type from parent field and cast an empty embed" do
232+
generator = :polymorphic
233+
reminder_module = get_module(Reminder, generator)
234+
235+
reminder_attrs = %{
236+
date: DateTime.utc_now(),
237+
text: "This is a reminder #{generator}",
238+
type: "not_provided",
239+
channel4: %{}
240+
}
241+
242+
insert_result =
243+
struct(reminder_module)
244+
|> reminder_module.changeset(reminder_attrs)
245+
|> Repo.insert()
246+
247+
assert {:ok, %{id: id, channel4: %PolymorphicEmbed.Channel.NotProvided{}}} =
248+
insert_result
249+
250+
assert %{channel4: %PolymorphicEmbed.Channel.NotProvided{}} = Repo.get!(reminder_module, id)
251+
end
252+
253+
test "cast an empty embed with no type if the embedded field is not required" do
254+
generator = :polymorphic
255+
reminder_module = get_module(Reminder, generator)
256+
257+
reminder_attrs = %{
258+
date: DateTime.utc_now(),
259+
text: "This is a reminder #{generator}",
260+
channel2: %{}
261+
}
262+
263+
insert_result =
264+
struct(reminder_module)
265+
|> reminder_module.changeset(reminder_attrs)
266+
|> Repo.insert()
267+
268+
assert {:ok, %{id: id, channel2: nil}} = insert_result
269+
270+
assert %{channel2: nil} = Repo.get!(reminder_module, id)
271+
end
272+
273+
test "cannot cast an empty embed when no type can be inferred but the embed is required" do
274+
generator = :polymorphic
275+
reminder_module = get_module(Reminder, generator)
276+
277+
reminder_attrs = %{
278+
date: DateTime.utc_now(),
279+
text: "This is a reminder #{generator}",
280+
channel2: %{}
281+
}
282+
283+
insert_result =
284+
struct(reminder_module)
285+
|> reminder_module.changeset(reminder_attrs, channel2_required?: true)
286+
|> Repo.insert()
287+
288+
assert {:error, changeset} = insert_result
289+
290+
assert changeset.errors == [channel2: {"is invalid", []}]
291+
refute changeset.valid?
292+
end
293+
231294
test "validations before casting polymorphic embed still work" do
232295
for generator <- @generators do
233296
reminder_module = get_module(Reminder, generator)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
defmodule PolymorphicEmbed.Channel.NotProvided do
2+
use Ecto.Schema
3+
import Ecto.Changeset
4+
5+
embedded_schema do
6+
end
7+
8+
def changeset(struct, attrs) do
9+
cast(struct, attrs, [])
10+
end
11+
end

test/support/models/polymorphic/reminder.ex

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ defmodule PolymorphicEmbed.Reminder do
4545
polymorphic_embeds_one(:channel4,
4646
types: [
4747
sms: PolymorphicEmbed.Channel.SMS,
48-
email: PolymorphicEmbed.Channel.Email
48+
email: PolymorphicEmbed.Channel.Email,
49+
not_provided: PolymorphicEmbed.Channel.NotProvided
4950
],
5051
on_replace: :update,
5152
use_parent_field_for_type: :type
@@ -82,12 +83,14 @@ defmodule PolymorphicEmbed.Reminder do
8283
timestamps()
8384
end
8485

85-
def changeset(struct, values) do
86+
def changeset(struct, values, opts \\ []) do
87+
channel2_required? = Keyword.get(opts, :channel2_required?, false)
88+
8689
struct
8790
|> cast(values, [:date, :text, :type])
8891
|> validate_required(:date)
8992
|> cast_polymorphic_embed(:channel)
90-
|> cast_polymorphic_embed(:channel2)
93+
|> cast_polymorphic_embed(:channel2, required: channel2_required?)
9194
|> cast_polymorphic_embed(:channel3)
9295
|> cast_polymorphic_embed(:channel4)
9396
|> cast_polymorphic_embed(:contexts,

0 commit comments

Comments
 (0)