Run mongoid subclasses on _type field

From the Mongoid docs, I see that if I have the following:

class Base include Mongoid::Document end class InheritedA < Base end class InheritedB < Base end 

I can do the following, which will be stored with the attribute "_type".

 a = InheritedA.new a.save 

Mongoid will create the following document.

 { _type: "InheritedA" } 

My problem is that later on I have a function that has only String _type, and I want to create the corresponding type. I tried this:

 Base.new({ _type: mytype }); 

However, Mongoid considers this a dynamic attribute and rejects it. I know that the inclusion of dynamic attributes is not correct, because I do not want to allow this behavior in the general case.

I want to avoid having to do something like this:

 ob = nil if mytype == "InheritedA" ob = InheritedA.new elsif ... 

Does anyone know the correct method for this?

+4
source share
2 answers

There are two ways to handle this. First, a base class document is created, and then converted to a subclass document. Using this method, you can only initialize based on the fields defined in the base class:

 # attributes = {base_key1: value1, base_key2: value2} Base.new(attributes).becomes(mytype.constantize) 

The second creates the subclass document directly, and you can initialize any fields for the subclass:

 # attributes = {base_key1: value1, base_key2: value2, sub_key3: value3} Mongoid::Factory.build(mytype.constantize, attributes) 
+3
source

After studying the Mongoid source, I realized that Mongoid is really trying to create a suitable type based on viewing the _type field when you do something like this:

 Base.new({ _type: "InheritedA" }) 

He does this by looking at the descendants of the base class. Apparently my ruby ​​classes are lazy loading, and by default calling Base.descendants returns an empty list! I'm not sure if this is due to using JRuby or the quirk of Rails development mode or the value of config.cache_classes or something else, but I was able to solve this by doing the following:

 class Base Descendant1.inspect Descendant2.inspect ... 

Yes, this is a real pain, but it allows you to work with Rails by reloading your classes.

+1
source

Source: https://habr.com/ru/post/1499208/


All Articles