cucumber/cucumber-rails — Issue #193: JavaScript shared connection strategy is broken for AR::Base subclasses 
Author: tomstuart
Created: 2012-01-15 10:10:29+00:00
State: open

cucumber-rails [monkeypatches Active Record](https://github.com/cucumber/cucumber-rails/blob/v1.2.1/lib/cucumber/rails/hooks/active_record.rb#L2-8) to add a `shared_connection` class attribute:

class ActiveRecord::Base
class_attribute :shared_connection

def self.connection
self.shared_connection || retrieve_connection
end
end

This is [used by the shared connection strategy](https://github.com/cucumber/cucumber-rails/blob/v1.2.1/lib/cucumber/rails/database.rb#L53-61) to force all threads to share a connection per model:

ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection
ActiveRecord::Base.descendants.each do |model|
model.shared_connection = model.connection
end

However, this latter code is broken in the presence of `ActiveRecord::Base` subclasses which use `establish_connection` to open a different connection (e.g. against a different database) from the one used by `Base`:

* class attributes are [inherited by subclasses](https://github.com/rails/rails/blob/v3.1.3/activesupport/lib/active_support/core_ext/class/attribute.rb#L6-7);
* as above, cucumber-rails overrides the [original implementation](https://github.com/rails/rails/blob/v3.1.3/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb#L88-90) of `ActiveRecord::Base.connection` to default it to the `shared_connection` class attribute whenever the latter has been set;
* as above, the shared connection strategy sets each model class's `shared_connection` to its current `connection`, starting with `ActiveRecord::Base` and then iterating through its [descendants](https://github.com/rails/rails/blob/v3.1.3/activesupport/lib/active_support/descendants_tracker.rb#L11-16);
* but since `ActiveRecord::Base.shared_connection` is set first, every descendant inherits `ActiveRecord::Base`'s `shared_connection` (because `shared_connection` is inherited by subclasses), which then becomes each descendant's `connection` (because the replacement `ActiveRecord::Base.connection` implementation defaults to the value of `shared_connection`), which then gets assigned as each descendant's `shared_connection`;
* so every descendant's `shared_connection` is set to `ActiveRecord::Base`'s `shared_connection`;
* so every descendant's `connection` is set to `ActiveRecord::Base`'s `connection`.

The upshot is that any model class which expects to use a different connection from the one used by `ActiveRecord::Base` will be made to use `ActiveRecord::Base`'s connection during JavaScript scenarios, and will therefore break.

Potential solutions might involve:

1. figuring out the correct order of descendants on which to set `shared_connection` without clobbering anything;
2. taking advantage of inheritance by only setting `shared_connection` on the classes which establish their own connection, instead of setting it on all descendants; and/or
3. avoiding inheritance entirely by using a genuinely per-class attribute instead of the `class_attribute`.