Search code examples
ruby-on-railsjsonformatactiveresource

How do I make two different ActiveResource models use two different connection objects?


Turns out the connection between ActiveResource models is shared. So if you set the format in one model, it does stay different from the format in other models. However, if you call the .connection.format method on two separate models, that format changes every time you set a new format. So if Profile got loaded in second with the format :json then .connection.format for both models turns into ActiveResource::Formats::JsonFormat

My original question was completely different (I didn't fully understand what was going on) - you can look at the edit history to see the original version. Hopefully I'll get more responses...

Proof:

class Location < ActiveResource::Base
  self.format = :xml
end

class Profile < ActiveResource::Base
  self.format = :json
end

Then in a rails console...

>> Location.format
=> ActiveResource::Formats::XmlFormat
>> Location.connection.format
=> ActiveResource::Formats::XmlFormat

So far so good... the Location model and it's connection have the correct format.

>> Profile.format
=> ActiveResource::Formats::JsonFormat

Looks normal, that's the format for Profile that I want.

>> Location.format
=> ActiveResource::Formats::XmlFormat

Ok... Location.format is still the same after having loaded the Profile model Note: These models are lazy-loaded so their files and code aren't included until you attempt to call the class name.

>> Location.connection.format
=> ActiveResource::Formats::JsonFormat

And here the problem begins. After we've made a call to the Profile model, it f'ed up the Location.connection.format

>> Profile.connection.format
=> ActiveResource::Formats::JsonFormat

The formats are not supposed to be the same. This causes parsing to be completely broken when you make a call to something like Location.find(:all, :from => "/something.xml") - it attempts to parse the xml as json

I guess my question now is - how do I separate the two connections? (Or otherwise resolve this issue)

Edit to add this test in the console:

>> Location.connection == Profile.connection
=> true

Solution

  • This... feels like a bug.

    I was looking at ActiveResource source code. When you call the format= method in your ARes class it writes the format to connection.format.

    The connection method in this context will grab @@connection if it's defined, otherwise it will call superclass.connection. In our case superclass is ActiveResource::Base. As you can guess, @@connection on the Location or Profile models is not defined. It will get set if you call self.site= on your class, in which case the self.format= method will set the format on your class-specific version of the connection object.

    In my case I have no reason to use a different site variable for the different ActiveResource models, just a different return format. For this reason I was setting ActiveResource::Base.site = "myresource.local" in development (and the appropriate one in the production.rb env file too).

    So my fix for this problem?

    class Location < ActiveResource::Base
      self.site = self.site
      self.format = :xml
    end
    

    Yep, self.site = self.site forces the class to use it's own connection object. Somehow I feel like self.format = should also...

    Not gonna accept this answer quite yet because it feels like a hack and/or bug, but that's how I solved it for now and it appears to work.

    Hopefully someone else has thoughts on this? It doesn't seem like very many people use ActiveResource but we use it on a daily basis and I have run into this before.