简体   繁体   中英

ActiveResource adds an extra class when running rails console in development

We have 2 Models: Valuation and Document. Both are in a microservice so we use ActiveResource to access them.

class Valuation < ActiveResource::Base
  self.site = "#{config.valuation_service.base_url}/valuations"
  self.include_root_in_json = false
end

class Document < ActiveResource::Base
  self.site = "#{config.valuation_service.base_url}/documents"
  self.include_root_in_json = true
end

Running the Rails console in development.

>> Valuation.new(documents: [{ title: 'Foo' }])
=> <Valuation:0x007f9af85f1708 @attributes={"documents"=>[#<Valuation::Document:0x007f9af85f0970 @attributes={"title"=>"Foo"}, @prefix_options={}, @persisted=false>]}, @prefix_options={}, @persisted=false>

so the document's class name is Valuation:Document . When you run the rails console in production

>> Valuation.new(documents: [{ title: 'Foo' }])
=> <Valuation:0x007f9af595b478 @attributes={"documents"=>[#<Document:0x007f9af595a500 @attributes={"title"=>"Foo"}, @prefix_options={}, @persisted=false>]}, @prefix_options={}, @persisted=false>

The class of the document is just Document and it respects the config like include_root_in_json .

The bigger issue is when calling .to_json on the objects.

# development
>> Valuation.new(documents: [{ title: 'Foo' }]).to_json
=> "{\"documents\":[{\"title\":\"Foo\"}]}"

# production
>> Valuation.new(documents: [{ title: 'Foo' }]).to_json
=> "{\"documents\":[{\"document\":{\"title\":\"Foo\"}}]}"

We are using Rails 4.2.10.

What exactly is causing this? I've checked configs if there is anything toggled on/off depending on the environment but I can't find any.

ActiveResource seems to be using an autoloading mechanism for dynamically turning the attribute names into classes for collections.

Because of this, the difference between development and production are due to eager loading. In production all the files are eager loaded and therefore all the constants already exists when you run the console.

When you are running the console in development mode, ActiveResource tries to define the class name for the attribute, but the mechanism won't work for your use case.

If the constant is not found on Object , it is created within the class that is being initialized ( Valuation ).

In order to get development working the same way as production, you'll need to bypass triggering this autoloading mechanism. This can be done easily with require_dependency method from Rails.

Just add

require_dependency 'document'

class Valuation < ActiveResource::Base

before the Valuation class, and everything should work just fine.

This line makes sure that the Document constant has already been loaded before anyone tries to create an instance of the Valuation class, and the autoloading mechanism won't used.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM