Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 375
jvnill Avatar asked Apr 05 '18 06:04

jvnill


1 Answers

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.

like image 108
Laura Paakkinen Avatar answered Sep 27 '22 00:09

Laura Paakkinen