Mongoid の多態性と埋め込みの関連付け

概要

埋め込みと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

なぜこれが機能しないのかわかりません。私が期待しているのは、モデル エンティティにさまざまなタイプの多くのフィールドが埋め込まれていることですが、それは次のように爆発します。

 NameError:
   uninitialized constant MetadataService::Models::Entity::CustomField

私の何が間違っているのでしょうか?

解決策

Mongoid は embeds_many によるポリモーフィズムをサポートしていません。代わりに、継承を使用して、共通の祖先を参照することで、異なるクラスのドキュメントを埋め込むことができます。

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

「フィールド」の代わりに「アイテム」を使用しているのは、後者が Mongoid では特別な意味を持ち、予約語とみなされているためです。この制限を回避するには、おそらく embeds_many :items, class_name: ‘Field’ を使用できますが、私は例をできるだけ単純にしておきたいと思いました。

使用法:

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"
#       }
#     ]
#   }

ビルド経由で項目のクラスを渡し、別の引数としてヘルパーを作成することもできます。

e.items.build({ name: 'email', value: '[email protected]' }, EmailItem)
e.items.build({ name: 'email', value: [1, 2, 3] }, MultivalueItem)