heroku/databasedotcom — pull request #61 by eigenuser (2012-02-17 06:38:22+00:00)
Title: Support for interacting with multiple Salesforce organizations

Modified the reader and writer methods for Databasedotcom::Sobject::Sobject.client so they use a class-level instance variable rather than a class variable to store the client object.

To understand the problem this revision solves, consider the following scenario.

his_client = Databasedotcom::Client.new("databasedotcom.yml")
his_client.authenticate(:token => "his-oauth-token", :instance_url => "http://na1.salesforce.com")
his_client.sobject_module = His

her_client = Databasedotcom::Client.new("databasedotcom.yml")
her_client.authenticate(:token => "her-oauth-token", :instance_url => "http://na2.salesforce.com")
her_client.sobject_module = Her

his_client.materialize("HotDog") #=> His::HotDog
her_client.materialize("HotDog") #=> Her::HotDog

Her::HotDog.create({:bun => 'wheat', :meat => 'beef'}) #=> Creates a new HotDog in her Salesforce org as expected

His::HotDog.create({:bun => 'white', :meat => 'chicken'}) #=> Creates a new HotDog in HER Salesforce org !!!

This last line of code behaves in this unexpected way because both materialized classes His::HotDog and Her::HotDog inherit from the Databasedotcom::Sobject::Sobject class, which uses a class variable @@client to keep track of the Databasedotcom::Client object that was used to materialize the class.

Because Ruby class variables are shared across all subclasses, when the second HotDog class is materialized in the Her module namespace and Her::HotDog.client is assigned the object instance `her_client`, the assignment is in fact made to the @@client variable in the Databasedotcom::Sobject::Sobject class. Consequently the change is reflected immediately in His::HotDog.client as well.

Thus, when the call to His::HotDog.create is made, the class is using her_client to send a POST to the Salesforce instance server.

We can avoid this problem by using a class-level instance variable instead of class variable to store the Sobject client object.