From 604c9cf6ad67fe304326022928a7a99b9e44bcf3 Mon Sep 17 00:00:00 2001 From: Ben Mabey Date: Mon, 19 Dec 2011 21:10:09 -0700 Subject: [PATCH] fixes #22, AR code doesn't create new connections like it is going out of style The code that switches ActiveRecord adapters to take a model class instead of a connection hash or name. --- README.textile | 8 +- .../step_definitions/activerecord_steps.rb | 6 +- examples/features/support/env.rb | 10 +- features/cleaning_multiple_dbs.feature | 3 +- lib/database_cleaner/active_record/base.rb | 36 ++--- .../active_record/deletion.rb | 1 - .../active_record/transaction.rb | 18 +-- .../active_record/truncation.rb | 6 +- .../active_record/base_spec.rb | 133 ++---------------- 9 files changed, 51 insertions(+), 170 deletions(-) diff --git a/README.textile b/README.textile index 5271dfe..bfa63c0 100644 --- a/README.textile +++ b/README.textile @@ -148,7 +148,13 @@ Sometimes you need to use multiple ORMs in your application. You can use Databas DatabaseCleaner[:mongo_mapper].strategy = :truncation #How to specify particular connections - DatabaseCleaner[:active_record,{:connection => :two}] + + # with DataMapper you pass in the name of the repository + DatabaseCleaner[:data_mapper,{:connection => :my_other_repository}] + + # With ActiveRecord you pass in the model whose #connection DataCleaner should use + DatabaseCleaner[:active_record,{:connection => Widget}] + DatabaseCleaner[:active_record,{:connection => "Widget"}] # You may pass in the model as a String in case you need/want to delay loading. Usage beyond that remains the same with DatabaseCleaner.start calling any setup on the different configured connections, and DatabaseCleaner.clean executing afterwards. diff --git a/examples/features/step_definitions/activerecord_steps.rb b/examples/features/step_definitions/activerecord_steps.rb index a242987..5f4266f 100644 --- a/examples/features/step_definitions/activerecord_steps.rb +++ b/examples/features/step_definitions/activerecord_steps.rb @@ -1,9 +1,9 @@ Given /^I have setup database cleaner to clean multiple databases using activerecord$/ do #DatabaseCleaner - # require "#{File.dirname(__FILE__)}/../../../lib/datamapper_models" + # require "#{File.dirname(__FILE__)}/../../../lib/activerecord_models" # - # DatabaseCleaner[:datamapper, {:connection => :one} ].strategy = :truncation - # DatabaseCleaner[:datamapper, {:connection => :two} ].strategy = :truncation + # DatabaseCleaner[:active_record, {:connection => 'ActiveRecordWidgetUsingDatabaseOne'} ].strategy = whatever + # DatabaseCleaner[:active_record, {:connection => 'ActiveRecordWidgetUsingDatabaseTwo'} ].strategy = whatever end When /^I create a widget using activerecord$/ do diff --git a/examples/features/support/env.rb b/examples/features/support/env.rb index cd44839..cdfa46d 100644 --- a/examples/features/support/env.rb +++ b/examples/features/support/env.rb @@ -42,9 +42,13 @@ if orm && strategy DatabaseCleaner.app_root = "#{File.dirname(__FILE__)}/../.." orm_sym = orm.gsub(/(.)([A-Z]+)/,'\1_\2').downcase.to_sym - if orm_sym == :mongo_mapper - DatabaseCleaner[ orm_sym, {:connection => 'database_cleaner_test_one'} ].strategy = strategy.to_sym - DatabaseCleaner[ orm_sym, {:connection => 'database_cleaner_test_two'} ].strategy = strategy.to_sym + case orm_sym + when :mongo_mapper + DatabaseCleaner[:mongo_mapper, {:connection => 'database_cleaner_test_one'} ].strategy = strategy.to_sym + DatabaseCleaner[:mongo_mapper, {:connection => 'database_cleaner_test_two'} ].strategy = strategy.to_sym + when :active_record + DatabaseCleaner[:active_record, {:connection => 'ActiveRecordWidgetUsingDatabaseOne'} ].strategy = strategy.to_sym + DatabaseCleaner[:active_record, {:connection => 'ActiveRecordWidgetUsingDatabaseTwo'} ].strategy = strategy.to_sym else DatabaseCleaner[ orm_sym, {:connection => :one} ].strategy = strategy.to_sym DatabaseCleaner[ orm_sym, {:connection => :two} ].strategy = strategy.to_sym diff --git a/features/cleaning_multiple_dbs.feature b/features/cleaning_multiple_dbs.feature index 23cc375..a426b5c 100644 --- a/features/cleaning_multiple_dbs.feature +++ b/features/cleaning_multiple_dbs.feature @@ -17,5 +17,4 @@ Feature: multiple database cleaning | DataMapper | truncation | | MongoMapper | truncation | | DataMapper | transaction | -# Not working... -#| ActiveRecord | transaction | + | ActiveRecord | transaction | diff --git a/lib/database_cleaner/active_record/base.rb b/lib/database_cleaner/active_record/base.rb index c097831..9f2c550 100644 --- a/lib/database_cleaner/active_record/base.rb +++ b/lib/database_cleaner/active_record/base.rb @@ -9,44 +9,24 @@ module DatabaseCleaner %w[truncation transaction deletion] end - def self.config_file_location=(path) - @config_file_location = path - end - - def self.config_file_location - @config_file_location ||= "#{DatabaseCleaner.app_root}/config/database.yml" - end - module Base include ::DatabaseCleaner::Generic::Base - attr_accessor :connection_hash - - def db=(desired_db) - @db = desired_db - load_config + def db=(model_class) + @model_class = model_class unless model_class == :default # hack. this design sucks. + @connection_class = nil end def db - @db || super + @model_class || ::ActiveRecord::Base end - def load_config - if self.db != :default && File.file?(ActiveRecord.config_file_location) - connection_details = YAML::load(ERB.new(IO.read(ActiveRecord.config_file_location)).result) - @connection_hash = connection_details[self.db.to_s] - end + def connection + connection_class.connection end - def create_connection_klass - Class.new(::ActiveRecord::Base) - end - - def connection_klass - return ::ActiveRecord::Base unless connection_hash - klass = create_connection_klass - klass.send :establish_connection, connection_hash - klass + def connection_class + @connection_class ||= db.is_a?(String) ? Module.const_get(db) : db end end end diff --git a/lib/database_cleaner/active_record/deletion.rb b/lib/database_cleaner/active_record/deletion.rb index 4e1426e..2494cc8 100644 --- a/lib/database_cleaner/active_record/deletion.rb +++ b/lib/database_cleaner/active_record/deletion.rb @@ -53,7 +53,6 @@ module DatabaseCleaner::ActiveRecord class Deletion < Truncation def clean - connection = connection_klass.connection tables_to_truncate(connection).each do |table_name| connection.delete_table table_name end diff --git a/lib/database_cleaner/active_record/transaction.rb b/lib/database_cleaner/active_record/transaction.rb index b4e9fd5..755fa64 100644 --- a/lib/database_cleaner/active_record/transaction.rb +++ b/lib/database_cleaner/active_record/transaction.rb @@ -4,24 +4,24 @@ module DatabaseCleaner::ActiveRecord include ::DatabaseCleaner::ActiveRecord::Base def start - if connection_klass.connection.respond_to?(:increment_open_transactions) - connection_klass.connection.increment_open_transactions + if connection.respond_to?(:increment_open_transactions) + connection.increment_open_transactions else - connection_klass.__send__(:increment_open_transactions) + connection_class.__send__(:increment_open_transactions) end - connection_klass.connection.begin_db_transaction + connection.begin_db_transaction end def clean - return unless connection_klass.connection.open_transactions > 0 + return unless connection.open_transactions > 0 - connection_klass.connection.rollback_db_transaction + connection.rollback_db_transaction - if connection_klass.connection.respond_to?(:decrement_open_transactions) - connection_klass.connection.decrement_open_transactions + if connection.respond_to?(:decrement_open_transactions) + connection.decrement_open_transactions else - connection_klass.__send__(:decrement_open_transactions) + connection_class.__send__(:decrement_open_transactions) end end end diff --git a/lib/database_cleaner/active_record/truncation.rb b/lib/database_cleaner/active_record/truncation.rb index 76ec0fe..65d95cb 100755 --- a/lib/database_cleaner/active_record/truncation.rb +++ b/lib/database_cleaner/active_record/truncation.rb @@ -86,7 +86,7 @@ module ActiveRecord def truncate_table(table_name) truncate_tables([table_name]) end - + def truncate_tables(table_names) execute("TRUNCATE TABLE #{table_names.map{|name| quote_table_name(name)}.join(', ')} #{restart_identity} #{cascade};") end @@ -119,7 +119,6 @@ module DatabaseCleaner::ActiveRecord include ::DatabaseCleaner::Generic::Truncation def clean - connection = connection_klass.connection connection.disable_referential_integrity do connection.truncate_tables(tables_to_truncate(connection)) end @@ -138,6 +137,3 @@ module DatabaseCleaner::ActiveRecord end end - - - diff --git a/spec/database_cleaner/active_record/base_spec.rb b/spec/database_cleaner/active_record/base_spec.rb index 9e09652..7f2f99a 100644 --- a/spec/database_cleaner/active_record/base_spec.rb +++ b/spec/database_cleaner/active_record/base_spec.rb @@ -3,140 +3,37 @@ require 'active_record' require 'database_cleaner/active_record/base' require 'database_cleaner/shared_strategy_spec' -module DatabaseCleaner - describe ActiveRecord do - it { should respond_to(:available_strategies) } - - describe "config_file_location" do - subject { ActiveRecord.config_file_location } - - it "should default to DatabaseCleaner.root / config / database.yml" do - ActiveRecord.config_file_location=nil - DatabaseCleaner.should_receive(:app_root).and_return("/path/to") - subject.should == '/path/to/config/database.yml' - end - end - +class FakeModel + def self.connection + :fake_connection end +end +module DatabaseCleaner module ActiveRecord class ExampleStrategy include ::DatabaseCleaner::ActiveRecord::Base end describe ExampleStrategy do - let :config_location do - '/path/to/config/database.yml' - end - - before { ::DatabaseCleaner::ActiveRecord.stub(:config_file_location).and_return(config_location) } it_should_behave_like "a generic strategy" - describe "db" do - it "should store my desired db" do - subject.stub(:load_config) - - subject.db = :my_db - subject.db.should == :my_db + describe "#connection" do + it "returns the connection from ActiveRecord::Base by default" do + ::ActiveRecord::Base.stub!(:connection).and_return(:fake_connection) + subject.connection.should == :fake_connection end - it "should default to :default" do - subject.db.should == :default + it "returns the connection of the model provided" do + subject.db = FakeModel + subject.connection.should == :fake_connection end - it "should load_config when I set db" do - subject.should_receive(:load_config) - subject.db = :my_db - end - end - - describe "load_config" do - - before do - subject.db = :my_db - yaml = <<-Y -my_db: - database: <%= "ONE".downcase %> - Y - File.stub(:file?).with(config_location).and_return(true) - IO.stub(:read).with(config_location).and_return(yaml) - end - - it "should parse the config" do - YAML.should_receive(:load).and_return( {:nil => nil} ) - subject.load_config - end - - it "should process erb in the config" do - transformed = <<-Y -my_db: - database: one - Y - YAML.should_receive(:load).with(transformed).and_return({ "my_db" => {"database" => "one"} }) - subject.load_config - end - - it "should store the relevant config in connection_hash" do - subject.load_config - subject.connection_hash.should == {"database" => "one"} - end - - it "should skip config if config file is not available" do - File.should_receive(:file?).with(config_location).and_return(false) - subject.load_config - subject.connection_hash.should be_blank - end - - it "skips the file when the db is set to :default" do - # to avoid https://github.com/bmabey/database_cleaner/issues/72 - subject.db = :default - YAML.should_not_receive(:load) - subject.load_config - end - - end - - describe "connection_hash" do - it "should store connection_hash" do - subject.connection_hash = { :key => "value" } - subject.connection_hash.should == { :key => "value" } - end - end - - describe "create_connection_klass" do - it "should return a class" do - subject.create_connection_klass.should be_a(Class) - end - - it "should return a class extending ::ActiveRecord::Base" do - subject.create_connection_klass.ancestors.should include(::ActiveRecord::Base) - end - end - - describe "connection_klass" do - it { expect{ subject.connection_klass }.to_not raise_error } - it "should default to ActiveRecord::Base" do - subject.connection_klass.should == ::ActiveRecord::Base - end - - context "when connection_hash is set" do - let(:hash) { mock("hash") } - before { subject.stub(:connection_hash).and_return(hash) } - - it "should create connection_klass if it doesnt exist if connection_hash is set" do - subject.should_receive(:create_connection_klass).and_return(mock('class').as_null_object) - subject.connection_klass - end - - it "should configure the class from create_connection_klass if connection_hash is set" do - klass = mock('klass') - klass.should_receive(:establish_connection).with(hash) - - subject.should_receive(:create_connection_klass).and_return(klass) - subject.connection_klass - end + it "allows for the model to be passed in as a string" do + subject.db = "FakeModel" + subject.connection.should == :fake_connection end end end