Skip to content

Commit 9503388

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

4 files changed

Lines changed: 83 additions & 5 deletions

File tree

lib/polymorphic_embed.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ defmodule PolymorphicEmbed do
172172
{:ok, nil} when not required ->
173173
Ecto.Changeset.put_change(changeset, field, nil)
174174

175-
{:ok, map} when map == %{} and not array? ->
175+
{:ok, map} when map == %{} and not array? and not required ->
176176
changeset
177177

178178
{: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 if embed is required" 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, channel4_required?: true)
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: 8 additions & 4 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,14 +83,17 @@ 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+
channel4_required? = Keyword.get(opts, :channel4_required?, false)
89+
8690
struct
8791
|> cast(values, [:date, :text, :type])
8892
|> validate_required(:date)
8993
|> cast_polymorphic_embed(:channel)
90-
|> cast_polymorphic_embed(:channel2)
94+
|> cast_polymorphic_embed(:channel2, required: channel2_required?)
9195
|> cast_polymorphic_embed(:channel3)
92-
|> cast_polymorphic_embed(:channel4)
96+
|> cast_polymorphic_embed(:channel4, required: channel4_required?)
9397
|> cast_polymorphic_embed(:contexts,
9498
sort_param: :contexts_sort,
9599
default_type_on_sort_create: :location,

0 commit comments

Comments
 (0)