diff --git a/lib/ex_machina/ecto_strategy.ex b/lib/ex_machina/ecto_strategy.ex index 4911ec1..cc13df2 100644 --- a/lib/ex_machina/ecto_strategy.ex +++ b/lib/ex_machina/ecto_strategy.ex @@ -29,36 +29,25 @@ defmodule ExMachina.EctoStrategy do defp cast(record) do record |> cast_all_fields + |> cast_all_embeds |> cast_all_assocs end - defp cast_all_fields(struct) do - struct - |> ExMachina.Ecto.drop_ecto_fields - |> Map.keys - |> cast_all_fields(struct) - end + defp cast_all_fields(%{__struct__: schema} = struct) do + schema + |> schema_fields() + |> Enum.reduce(struct, fn(field_key, struct) -> + casted_value = cast_field(field_key, struct) - defp cast_all_fields(fields, struct) do - Enum.reduce(fields, struct, fn(field, struct) -> - casted_value = cast_field(field, struct) - Map.put(struct, field, casted_value) + Map.put(struct, field_key, casted_value) end) end - defp cast_field(field, %{__struct__: schema} = struct) do - field_type = schema.__schema__(:type, field) - virtual_field? = !field_type - embed_type = schema.__schema__(:embed, field) - embedded_field? = !!embed_type - - value = Map.get(struct, field) + defp cast_field(field_key, %{__struct__: schema} = struct) do + field_type = schema.__schema__(:type, field_key) + value = Map.get(struct, field_key) - if virtual_field? || embedded_field? do - value - else - cast_value(field_type, value, struct) - end + cast_value(field_type, value, struct) end defp cast_value(field_type, value, struct) do @@ -70,40 +59,73 @@ defmodule ExMachina.EctoStrategy do end end + defp cast_all_embeds(%{__struct__: schema} = struct) do + schema + |> schema_embeds() + |> Enum.reduce(struct, fn(embed_key, struct) -> + casted_value = struct |> Map.get(embed_key) |> cast_embed(embed_key, struct) + + Map.put(struct, embed_key, casted_value) + end) + end + + defp cast_embed(embeds_many, embed_key, struct) when is_list(embeds_many) do + Enum.map(embeds_many, &(cast_embed(&1, embed_key, struct))) + end + defp cast_embed(embed, embed_key, %{__struct__: schema}) do + if embed do + embedding_reflection = schema.__schema__(:embed, embed_key) + embed_type = embedding_reflection.related + embed_type |> struct() |> Map.merge(embed) |> cast() + end + end + defp cast_all_assocs(%{__struct__: schema} = struct) do - assocs = get_schema_assocs(schema) + assoc_keys = schema_associations(schema) - Enum.reduce(assocs, struct, fn(assoc, struct) -> - casted_value = struct |> Map.get(assoc) |> cast_assoc(assoc, struct) + Enum.reduce(assoc_keys, struct, fn(assoc_key, struct) -> + casted_value = struct |> Map.get(assoc_key) |> cast_assoc(assoc_key, struct) - Map.put(struct, assoc, casted_value) + Map.put(struct, assoc_key, casted_value) end) end - defp cast_assoc(original_assoc, assoc_key, %{__struct__: schema} = struct) do - case original_assoc do - has_or_embeds_many when is_list(has_or_embeds_many) -> - Enum.map(has_or_embeds_many, &(cast_assoc(&1, assoc_key, struct))) - + defp cast_assoc(has_many_assoc, assoc_key, struct) when is_list(has_many_assoc) do + Enum.map(has_many_assoc, &(cast_assoc(&1, assoc_key, struct))) + end + defp cast_assoc(assoc, assoc_key, %{__struct__: schema}) do + case assoc do %{__meta__: %{__struct__: Ecto.Schema.Metadata, state: :built}} -> - cast(original_assoc) + cast(assoc) %{__struct__: Ecto.Association.NotLoaded} -> - original_assoc + assoc %{__struct__: _} -> - cast(original_assoc) + cast(assoc) %{} -> - assoc_reflection = schema.__schema__(:association, assoc_key) || schema.__schema__(:embed, assoc_key) + assoc_reflection = schema.__schema__(:association, assoc_key) assoc_type = assoc_reflection.related - assoc_type |> struct() |> Map.merge(original_assoc) |> cast() + assoc_type |> struct() |> Map.merge(assoc) |> cast() nil -> nil end end - defp get_schema_assocs(schema) do - schema.__schema__(:associations) ++ schema.__schema__(:embeds) + defp schema_fields(schema) do + schema_non_virtual_fields(schema) -- schema_embeds(schema) + end + + defp schema_non_virtual_fields(schema) do + schema.__schema__(:fields) + end + + defp schema_embeds(schema) do + schema.__schema__(:embeds) + end + + defp schema_associations(schema) do + schema.__schema__(:associations) end end