This project is archived and is in readonly mode.

#6157 open
Peer Allan

ActiveRecord scope class method requires a database connection

Reported by Peer Allan | December 13th, 2010 @ 01:46 PM

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.

Comments and changes to this ticket

  • Nick

    Nick January 14th, 2011 @ 10:56 AM

    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?

  • Peer Allan

    Peer Allan March 8th, 2011 @ 02:30 PM

    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.

  • Aaron Patterson

    Aaron Patterson March 8th, 2011 @ 04:21 PM

    • State changed from “new” to “open”
    • Assigned user set to “Aaron Patterson”
    • Importance changed from “” to “Low”

    @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.

  • Peer Allan

    Peer Allan March 8th, 2011 @ 05:40 PM

    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.

  • Jon Leighton

    Jon Leighton March 8th, 2011 @ 06:22 PM

    It's caused by this line:

    https://github.com/rails/rails/blob/3-0-stable/activerecord/lib/act...

    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 ;)

  • bingbing

Create your profile

Help contribute to this project by taking a few moments to create your personal profile. Create your profile »

<h2 style="font-size: 14px">Tickets have moved to Github</h2>

The new ticket tracker is available at <a href="https://github.com/rails/rails/issues">https://github.com/rails/rails/issues</a>

Attachments

Pages