mirror of
				https://github.com/DatabaseCleaner/database_cleaner
				synced 2023-03-27 23:22:03 -04:00 
			
		
		
		
	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.
This commit is contained in:
		
							parent
							
								
									501025b9de
								
							
						
					
					
						commit
						604c9cf6ad
					
				
					 9 changed files with 51 additions and 170 deletions
				
			
		| 
						 | 
				
			
			@ -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.
 | 
			
		||||
</pre>
 | 
			
		||||
 | 
			
		||||
Usage beyond that remains the same with DatabaseCleaner.start calling any setup on the different configured connections, and DatabaseCleaner.clean executing afterwards.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,5 +17,4 @@ Feature: multiple database cleaning
 | 
			
		|||
  | DataMapper   | truncation    |
 | 
			
		||||
  | MongoMapper  | truncation    |
 | 
			
		||||
  | DataMapper   | transaction   |
 | 
			
		||||
# Not working...
 | 
			
		||||
#| ActiveRecord | transaction   |
 | 
			
		||||
  | ActiveRecord | transaction   |
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue