diff --git a/README.md b/README.md index 16829b5..66ea628 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,36 @@ # ActiveRecord::AllowConnectionFailure -Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/active_record/allow_connection_failure`. To experiment with that code, run `bin/console` for an interactive prompt. +This gem monkeypatches methods inside of `ActiveRecord::QueryCache` and +`ActiveRecord::Migration::CheckPending`. -TODO: Delete this and the text above, and describe your gem +The intention is to allow a Rails application to boot without first establishing +a database connection, enabling AR's query caching mechanism, and checking for +any pending migrations. -## Installation +In practice, this is useful for situations in which you want to boot your app +and check e.g. a `/status` endpoint which does not have a database/ORM +(read: `ActiveRecord`) dependency. -Add this line to your application's Gemfile: +## Associated `Rails` PR -```ruby -gem 'active_record-allow_connection_failure' -``` - -And then execute: - - $ bundle - -Or install it yourself as: - - $ gem install active_record-allow_connection_failure +For additional context, see the associated Rails +[pull request](https://github.com/rails/rails/issues/22154). ## Usage -TODO: Write usage instructions here +Add this gem to your `Gemfile`: -## Development - -After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. - -To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). +```ruby +gem 'active_record-allow_connection_failure' +``` ## Contributing -Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/active_record-allow_connection_failure. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. - +Bug reports and pull requests are welcome on GitHub at +https://github.com/zipmark/active_record-allow_connection_failure. This project +is intended to be a safe, welcoming space for collaboration, and contributors +are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. ## License The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). - diff --git a/active_record-allow_connection_failure.gemspec b/active_record-allow_connection_failure.gemspec index 8a71199..291844d 100644 --- a/active_record-allow_connection_failure.gemspec +++ b/active_record-allow_connection_failure.gemspec @@ -20,8 +20,8 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_development_dependency "bundler", "~> 1.11" - spec.add_development_dependency "rake", "~> 10.0" - spec.add_development_dependency "rspec", "~> 3.0" + spec.add_development_dependency "rake", "~> 10.0" + spec.add_development_dependency "rspec", "~> 3.0" - spec.add_runtime_dependency "rails", ">= 4.2" + spec.add_runtime_dependency "rails", ">= 3.2" end diff --git a/lib/active_record/allow_connection_failure.rb b/lib/active_record/allow_connection_failure.rb index 1113c0f..c1fd2c7 100644 --- a/lib/active_record/allow_connection_failure.rb +++ b/lib/active_record/allow_connection_failure.rb @@ -5,51 +5,95 @@ module ActiveRecord module AllowConnectionFailure # monkeypatches activerecord/lib/active_record/query_cache.rb - ActiveRecord::QueryCache.class_eval do - def self.run - if ActiveRecord::Base.connected? - connection = ActiveRecord::Base.connection - enabled = connection.query_cache_enabled - connection_id = ActiveRecord::Base.connection_id - connection.enable_query_cache! - - [enabled, connection_id] + if Rails::VERSION::MAJOR == 3 + ActiveRecord::QueryCache::BodyProxy.class_eval do + def close + @target.close if @target.respond_to?(:close) + ensure + ActiveRecord::Base.connection_id = @connection_id + if ActiveRecord::Base.connected? + ActiveRecord::Base.connection.clear_query_cache + unless @original_cache_value + ActiveRecord::Base.connection.disable_query_cache! + end + end end end - def self.complete(state) - if ActiveRecord::Base.connected? - enabled, connection_id = state - - ActiveRecord::Base.connection_id = connection_id - ActiveRecord::Base.connection.clear_query_cache - ActiveRecord::Base.connection.disable_query_cache! unless enabled + ActiveRecord::QueryCache.class_eval do + def call(env) + begin + if ActiveRecord::Base.connected? + old = ActiveRecord::Base.connection.query_cache_enabled + ActiveRecord::Base.connection.enable_query_cache! + end + + status, headers, body = @app.call(env) + [status, headers, ActiveRecord::QueryCache::BodyProxy.new(old, body, ActiveRecord::Base.connection_id)] + rescue Exception => e + raise e.inspect + ActiveRecord::Base.connection.clear_query_cache + unless old + ActiveRecord::Base.connection.disable_query_cache! + end + raise e + end end end + + ActiveRecord::ModelSchema::ClassMethods.module_eval do + def clear_cache! # :nodoc: + connection.schema_cache.clear! if connected? + end + end + end + + if Rails::VERSION::MAJOR >= 4 + ActiveRecord::QueryCache.class_eval do + def self.run + if ActiveRecord::Base.connected? + connection = ActiveRecord::Base.connection + enabled = connection.query_cache_enabled + connection_id = ActiveRecord::Base.connection_id + connection.enable_query_cache! - def self.install_executor_hooks(executor = ActiveSupport::Executor) - executor.register_hook(self) + [enabled, connection_id] + end + end + + def self.complete(state) + if ActiveRecord::Base.connected? + enabled, connection_id = state - executor.to_complete do - unless ActiveRecord::Base.connected? && ActiveRecord::Base.connection.transaction_open? - ActiveRecord::Base.clear_active_connections! + ActiveRecord::Base.connection_id = connection_id + ActiveRecord::Base.connection.clear_query_cache + ActiveRecord::Base.connection.disable_query_cache! unless enabled end end - end - end - # monkeypatches activerecord/lib/active_record/migration.rb - ActiveRecord::Migration::CheckPending.class_eval do - def call(env) - if ActiveRecord::Base.connected? && connection.supports_migrations? - mtime = ActiveRecord::Migrator.last_migration.mtime.to_i - if @last_check < mtime - ActiveRecord::Migration.check_pending!(connection) - @last_check = mtime + def self.install_executor_hooks(executor = ActiveSupport::Executor) + executor.register_hook(self) + + executor.to_complete do + unless ActiveRecord::Base.connected? && ActiveRecord::Base.connection.transaction_open? + ActiveRecord::Base.clear_active_connections! + end end end - @app.call(env) end - end - end + + ActiveRecord::Migration::CheckPending.class_eval do + def call(env) + if ActiveRecord::Base.connected? && connection.supports_migrations? + mtime = ActiveRecord::Migrator.last_migration.mtime.to_i + if @last_check < mtime + ActiveRecord::Migration.check_pending!(connection) + @last_check = mtime + end + end + @app.call(env) + end + end + end + end end