Reputation: 1282
I'm confused on how to make polymorphism work with embeds and mongoid 8.x.
class Entity
include Mongoid::Document
embeds_many :custom_fields, as: :item
end
class EmailField
include Mongoid::Document
field :name, type: String
field :value, type: String
embedded_in :item, polymorphic: true
end
class MultivalueField
include Mongoid::Document
field :name
field :value, type: Array
embedded_in :item, polymorphic: true
end
I don't understand why this doesn't work. What I expect is the model Entity
to have many fields of different types embedded, but it explodes with
NameError:
uninitialized constant MetadataService::Models::Entity::CustomField
What am I doing wrong?
Upvotes: 0
Views: 49
Reputation: 114248
Mongoid doesn't support polymorphism with embeds_many
. Instead, you can use inheritance to embed documents of different classes by referencing their common ancestor:
class Entity
include Mongoid::Document
embeds_many :items
end
class Item
include Mongoid::Document
embedded_in :entity
field :name, type: String
end
class EmailItem < Item
field :value, type: String
end
class MultivalueItem < Item
field :value, type: Array
end
I'm using "Item" instead of "Field" because the latter has special meaning in Mongoid and is regarded a reserved word. You can probably use embeds_many :items, class_name: 'Field'
to work around this limitation, but I wanted to keep the example as simple as possible.
Usage:
e = Entity.new
e.items << EmailItem.new(name: 'email', value: '[email protected]')
e.items << MultivalueItem.new(name: 'email', value: [1, 2, 3])
e.as_document
#=> {
# "_id"=>BSON::ObjectId('660e7a7e571de609e1c84723'),
# "items"=>[
# {
# "_id"=>BSON::ObjectId('660e7a7e571de609e1c84724'),
# "name"=>"email",
# "value"=>"[email protected]",
# "_type"=>"EmailItem"
# },
# {
# "_id"=>BSON::ObjectId('660e7a7e571de609e1c84725'),
# "name"=>"email",
# "value"=>[1, 2, 3],
# "_type"=>"MultivalueItem"
# }
# ]
# }
You can also pass the item's class via the build
and create
helpers as a separate argument:
e.items.build({ name: 'email', value: '[email protected]' }, EmailItem)
e.items.build({ name: 'email', value: [1, 2, 3] }, MultivalueItem)
Upvotes: 1