Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoid "reverse polymorphism"? How to embed multiple class types under one field that doesn't match class name

I have one parent object, Order, that I need to embed one channel object. The thing is, the object that is the channel can be one of N classes within a module called Channels.

I keep getting the following error, because Mongoid can't look up the right class for the relation. How can I essentially tell Mongoid that the class of the :class field can be one of several? Essentially, I won't know until I'm creating the order which of the concrete classes in the Channels module it will need.

(This seems a bit like "reverse polymorphism" since it's the parent that has to handle multiple types, as opposed to the child being embeddable in multiple types of parents.)

My classes are like so:

class Order
  # ...
  with_options cascade_callbacks: true do |o|
    o.embeds_one :channel
    # I've tried several combinations of making the child object polymorphic
    # using the `as`/`belongs_to` syntax, to no avail
  end
  # ...
end

module Channels
  class Base
    embedded_in :order
    # lots of code here that all concrete channels share
  end

  # then there are concrete subclasses of Channels::Base, e.g.
  class Channel1 < Channels::Base
    # specifics omitted...
  end

  class Channel2 < Channels::Base
    # specifics omitted...
  end
end

The error I get when trying to load/save an order in the shell is this: NameError: uninitialized constant Channel

like image 941
AndrewS Avatar asked Jan 20 '26 11:01

AndrewS


1 Answers

facepalm The solution is very simple and should have been obvious from the error/stack trace:

  1. Either create a base class (not in the Channels module) called Channel, or
  2. Specify the class_name as the base class, e.g. o.embeds_one :channel, class_name: "Channels::Base"

Either of these will work and allow me to override the field with the appropriate concrete subclass for my channel.

Error results from the fact that Mongoid is calling #constantize (or equivalent) under the hood, and no base Channel class exists.

like image 187
AndrewS Avatar answered Jan 22 '26 10:01

AndrewS