mathieuprog / polymorphic_embed

Polymorphic embeds in Ecto
Apache License 2.0
341 stars 63 forks source link

More ways to specify embedded schema module #18

Open pzingg opened 4 years ago

pzingg commented 4 years ago

I was working on a library that uses polymorphic embeds. Since it's a library that is used by applications, it cannot know all the polymorphic types ahead of time. This PR adds two more ways of specifying the :types option for a PolymorphicEmbed Ecto type: a lookup function that translates between "type" keys and schema modules, and a :by_module method that means that the type key atom (or a string version) IS the module.

Feel free to ignore, edit or implement this another way.

I added two more embeds to the test Reminder module, added a number of tests, and added instructions in the README file.

mathieuprog commented 3 years ago

Quite some work had to be done before I could look into this PR. I think we're definitely interested into adding this PR in the codebase. That's some quality work.

I guess you depend on your fork now? As there has been a lot of code refactoring in the meanwhile, would you be interested to bring these new functionalities into the current master code?

Ivor commented 2 years ago

This seems like a great feature that will make the library very dynamic. Any plans to merge this. What are the changes required to get this merged?

mathieuprog commented 2 years ago

The contributor created this PR while I was writing some major refactor when the library was in alpha.

It was created in November 2020 so the code changed quite a lot since then.

fireproofsocks commented 1 year ago

I haven't looked through this PR, but I would find it useful (and more common in my experience) if the field that qualified the polymorphism were outside the arguments. For example, I might have a database with columns for type and data, where type qualifies the ad-hoc data map, e.g. as x, y, or z. Often the code pivots on such a field to determine how to handle the data.

I worked around this by adding an append_type function that read a field from the incoming parameters and then inserted it (as an atom) into the map field used by the polymorphic join:

def changeset(x, %{} = params, opts \\ []) do
    params = append_type(params, :qualifier, :args)
    x
    |> cast(params, [...])
    # ...
end

  defp append_type(params, src_field, target_embed_field) do
    qualifier = Map.fetch!(params, src_field)

    Map.put(
      params,
      :args,
      params
      |> Map.get(target_embed_field, %{})
      |> Map.put(:__type__, String.to_atom(qualifier))
    )
  end

This works, but it feels a bit awkward because it cannot rely on the usual get_change/get_field etc. and the requirement that the qualifying value has to be an atom threw me for bit.