How to create my own association macro with Ecto?

Hey forum,
I am gradually chipping away at an Elixir umbrella app that handles business logic and data reading/storage for a legacy Rails app (4.0.x). On top of that, it uses a very old version of Spree.

Inevitably, there’s a lot of the old-fashioned Rails polymorphic fields around: say you want to fetch source, you’d have source_id=1 and source_type="Spree::CreditCard".

I’ve made my own module and function to resolve polymorphic associations and it works perfectly. Skeleton example:

defmodule MyApp.Polymorphic do
  def resolve(%{} = record, field)
  when is_atom(field) do
    # the code here successfully resolves the polymorphic association;
    # it returns different types of structs by working with the <field_type> name
    # and converting to a valid Ecto struct module name.
  end
end

That’s not my problem.

My problem is that now I want to have this in my Ecto schema definitions:

defmodule MyApp.Payment do
  schema "table" do
    # loads of fields

    has_polymorphic :source # <- how do I declare and implement this macro?
  end
end

What I need is a way to implement such a macro so it uses the MyApp.Polymorphic.resolve(record, field) function. I can’t figure it out because when inspecting the Ecto.Schema source, it seems like all current association macros don’t accept an instance of the Ecto struct and only work at a module level. They also use compile-time module attributes which prevents me from tracking what are the Ecto macros doing exactly.

I am very sure I am missing something but can’t figure it out just yet.

Any ideas?

1 Like

I’ve been told by core Elixir and Ecto contributors that it’s not possible.

So I ended up making a helper module – separate from the schema module – that simply does the job dynamically, at runtime, as opposed to compile time as Ecto does (which is obviously not possible for a polymorphic association).

2 Likes