Skip to content
This repository

ActiveRecord scope class method requires a database connection #783

Closed
lighthouse-import opened this Issue May 15, 2011 · 5 comments

1 participant

Lighthouse Import
Lighthouse Import

Imported from Lighthouse. Original ticket at: http://rails.lighthouseapp.com/projects/8994/tickets/6157
Created by Peer Allan - 2011-01-18 08:52:34 UTC

ActiveRecord 3 based Rubygem
At our company we had to integrate with a legacy database system. To manage it we created a nice little gem to handle it. Basically the gem was just a collection of ActiveRecord models, nicely namespaced, so that we could include it in any application that made have needed access to that db. In code outside of Rails, where we commonly used the gem we would just do this,

  require 'legacy_database'
  LegacyDatabase::Base.establish_connection(...)

Using ActiveRecord 3 this no longer works when used outside of Rails.

  require 'legacy_database' # <= raises ActiveRecord::ConnectionNotEstablished
  LegacyDatabase::Base.establish_connection(...)

The backtrace showed us that when the gem was being loaded when it hits the first scope call Arel attempts to connect to the database. Its a classic chicken/egg situation. You can't initialize the connection until you load the gem and you can't load the gem without initializing the connection.

In our situation we aren't using the gem in concert with any other ActiveRecord connections so we are able to use ActiveRecord::Base to establish the connection.

  require 'activerecord'
  ActiveRecord::Base.establish_connection(...)
  require 'legacy_database'
  LegacyDatabase::Foo.find(1)  # It work's

This naturally is not an ideal situation.

Lighthouse Import

Imported from Lighthouse.
Comment by Nick - 2011-01-14 10:56:56 UTC

I've just come across this issue in another project where we manually require AR + all the models, then establish the connection some time afterwards.

My workaround is a class method encapsulating an anonymous scope, e.g.

class Foo
def self.online
self.scoped.joins(:bar).where(:bars => {:disconnected_at => nil})
end
end

Since AR/Arel can't validate such anonymous scopes at require-time, it gets around the problem. It does raise the question of exactly how useful that validation is, though. Obviously, they could just defer it until require_connection is called if there's no connection at require time - but maybe it's not useful and should just be quietly dropped in favour of an error at runtime when you try to use a named scope referencing an invisible table?

Lighthouse Import

Imported from Lighthouse.
Comment by Peer Allan - 2011-03-08 14:30:02 UTC

My original post described a workaround for when you are encountering this problem outside a Rails app. Since then I have tried to work around this in a couple more ways in a Rails 3 application and am not having much luck. Our legacy gem is formatted very much like ActiveRecord

module LegacyDatabase
  class Base < ActiveRecord::Base
    establish_connection :legacy_database # <= important line
  end
end

If we leave the establish_connection in the gem then we get a configuration not found error. (legacy_database database is not configured (ActiveRecord::AdapterNotSpecified))

If we remove that line then the app starts to load the models that are contained in the gem. However, once it hits an association, for example:

module LegacyDatabase
  class Foo < Base
    has_and_belongs_to_many :bars
  end
end

We get a ActiveRecord::ConnectionNotEstablished error. Here is the important part of the backtrace:

from /user/me/.rvm/gems/ruby-1.8.7-p334/gems/activerecord-3.0.5/lib/active_record/connection_adapters/abstract/connection_specification.rb:97:in `retrieve_connection'
from /user/me/.rvm/gems/ruby-1.8.7-p334/gems/activerecord-3.0.5/lib/active_record/connection_adapters/abstract/connection_specification.rb:89:in `connection'
from /user/me/.rvm/gems/ruby-1.8.7-p334/gems/activerecord-3.0.5/lib/active_record/associations.rb:1804:in `create_has_and_belongs_to_many_reflection'
from /user/me/.rvm/gems/ruby-1.8.7-p334/gems/activerecord-3.0.5/lib/active_record/associations.rb:1411:in `has_and_belongs_to_many'
from /user/me/.rvm/gems/ruby-1.8.7-p334/gems/activerecord-3.0.5/lib/active_record/autosave_association.rb:137:in `has_and_belongs_to_many'

At this point, we have exhausted all avenues that we can think of, to get around this. It is very unfortunate as this completely blocks our Rails 3 upgrade. Thanks.

Lighthouse Import

Imported from Lighthouse.
Comment by Aaron Patterson - 2011-03-08 16:21:55 UTC

@Peer, can you provide the source for "legacy_database". I cannot debug the problem without knowing why requiring that file will produce an ActiveRecord::ConnectionNotEstablished exception.

Lighthouse Import

Imported from Lighthouse.
Comment by Peer Allan - 2011-03-08 17:40:28 UTC

Attached is a simple gem that demonstrates the problem. Attempt to load it into a Rails 3 application and you will see the errors. Detailed instructions are shown below and included in the legacy_database.rb file. There are 3 scenarios listed in terms of importance.

Three things can be tested by including this gem in your app

  1. scope calls will attempt to connect to the database before a
    connection has been established

  2. has_and_belong_to_many associations in the models will attempt to
    connect to the database before a connection has been established

  3. an establish_connection in the Base class as shown below will
    not work regardless of database.yml settings. Although, setting
    a gem up this way is probably not useful anyway ;)

Instructions:

  1. Include this gem in your rails 3 app's Gemfile gem 'legacy_database', :path => '/path/to/legacy_database'
  2. bundle install the gem

Scenario 1
1) comment out the habtm lines in foo.rb and bar.rb
2) ensure the scope line in baz.rb is not commented out
3) attempt to load the console

Scenario 2
1) comment out scope in baz.rb
2) Ensure the habtm lines in bar.rb and foo.rb are not commented out
3) attempt to load the console

Scenario 3
1) uncomment the 'establish_connection' line in this file and attempt
to load a console
2) add a legacdy_database config to your database.yml and load the console

I also tested it with a "has_many" and a "has_many :through" relationships which did not cause the same problem. Therefore the workaround if a habtm is required is that you have to use a "has_many :through". That said, the association issues and establish_connection are only related side effects. Its the problem that primarily affects us (so now we have a workaround, yay!). The scope issues is the big fish in this ticket.

Lighthouse Import

Imported from Lighthouse.
Comment by Jon Leighton - 2011-03-08 18:22:58 UTC

It's caused by this line:

https://github.com/rails/rails/blob/3-0-stable/activerecord/lib/active_record/associations.rb#L1804

That test and the preceding one ought to be in AssociationReflection#check_validity! I think. I added a TODO comment for this in master, but I haven't got round TODOing it yet ;)

Lighthouse Import lighthouse-import closed this May 21, 2011
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.