Skip to content

Commit

Permalink
fixes #22, AR code doesn't create new connections like it is going ou…
Browse files Browse the repository at this point in the history
…t of style

The code that switches ActiveRecord adapters to take a model class
instead of a connection hash or name.
  • Loading branch information
bmabey committed Dec 20, 2011
1 parent 501025b commit 604c9cf
Show file tree
Hide file tree
Showing 9 changed files with 51 additions and 170 deletions.
8 changes: 7 additions & 1 deletion README.textile
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -148,7 +148,13 @@ Sometimes you need to use multiple ORMs in your application. You can use Databas
DatabaseCleaner[:mongo_mapper].strategy = :truncation DatabaseCleaner[:mongo_mapper].strategy = :truncation


#How to specify particular connections #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> </pre>


Usage beyond that remains the same with DatabaseCleaner.start calling any setup on the different configured connections, and DatabaseCleaner.clean executing afterwards. Usage beyond that remains the same with DatabaseCleaner.start calling any setup on the different configured connections, and DatabaseCleaner.clean executing afterwards.
Expand Down
6 changes: 3 additions & 3 deletions examples/features/step_definitions/activerecord_steps.rb
Original file line number Original file line Diff line number Diff line change
@@ -1,9 +1,9 @@
Given /^I have setup database cleaner to clean multiple databases using activerecord$/ do Given /^I have setup database cleaner to clean multiple databases using activerecord$/ do
#DatabaseCleaner #DatabaseCleaner
# require "#{File.dirname(__FILE__)}/../../../lib/datamapper_models" # require "#{File.dirname(__FILE__)}/../../../lib/activerecord_models"
# #
# DatabaseCleaner[:datamapper, {:connection => :one} ].strategy = :truncation # DatabaseCleaner[:active_record, {:connection => 'ActiveRecordWidgetUsingDatabaseOne'} ].strategy = whatever
# DatabaseCleaner[:datamapper, {:connection => :two} ].strategy = :truncation # DatabaseCleaner[:active_record, {:connection => 'ActiveRecordWidgetUsingDatabaseTwo'} ].strategy = whatever
end end


When /^I create a widget using activerecord$/ do When /^I create a widget using activerecord$/ do
Expand Down
10 changes: 7 additions & 3 deletions examples/features/support/env.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -42,9 +42,13 @@
DatabaseCleaner.app_root = "#{File.dirname(__FILE__)}/../.." DatabaseCleaner.app_root = "#{File.dirname(__FILE__)}/../.."
orm_sym = orm.gsub(/(.)([A-Z]+)/,'\1_\2').downcase.to_sym orm_sym = orm.gsub(/(.)([A-Z]+)/,'\1_\2').downcase.to_sym


if orm_sym == :mongo_mapper case orm_sym
DatabaseCleaner[ orm_sym, {:connection => 'database_cleaner_test_one'} ].strategy = strategy.to_sym when :mongo_mapper
DatabaseCleaner[ orm_sym, {:connection => 'database_cleaner_test_two'} ].strategy = strategy.to_sym 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 else
DatabaseCleaner[ orm_sym, {:connection => :one} ].strategy = strategy.to_sym DatabaseCleaner[ orm_sym, {:connection => :one} ].strategy = strategy.to_sym
DatabaseCleaner[ orm_sym, {:connection => :two} ].strategy = strategy.to_sym DatabaseCleaner[ orm_sym, {:connection => :two} ].strategy = strategy.to_sym
Expand Down
3 changes: 1 addition & 2 deletions features/cleaning_multiple_dbs.feature
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -17,5 +17,4 @@ Feature: multiple database cleaning
| DataMapper | truncation | | DataMapper | truncation |
| MongoMapper | truncation | | MongoMapper | truncation |
| DataMapper | transaction | | DataMapper | transaction |
# Not working... | ActiveRecord | transaction |
#| ActiveRecord | transaction |
36 changes: 8 additions & 28 deletions lib/database_cleaner/active_record/base.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -9,44 +9,24 @@ def self.available_strategies
%w[truncation transaction deletion] %w[truncation transaction deletion]
end 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 module Base
include ::DatabaseCleaner::Generic::Base include ::DatabaseCleaner::Generic::Base


attr_accessor :connection_hash def db=(model_class)

@model_class = model_class unless model_class == :default # hack. this design sucks.
def db=(desired_db) @connection_class = nil
@db = desired_db
load_config
end end


def db 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
end end


def create_connection_klass def connection
Class.new(::ActiveRecord::Base) connection_class.connection
end end


def connection_klass def connection_class
return ::ActiveRecord::Base unless connection_hash @connection_class ||= db.is_a?(String) ? Module.const_get(db) : db
klass = create_connection_klass
klass.send :establish_connection, connection_hash
klass
end end
end end
end end
Expand Down
1 change: 0 additions & 1 deletion lib/database_cleaner/active_record/deletion.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ module DatabaseCleaner::ActiveRecord
class Deletion < Truncation class Deletion < Truncation


def clean def clean
connection = connection_klass.connection
tables_to_truncate(connection).each do |table_name| tables_to_truncate(connection).each do |table_name|
connection.delete_table table_name connection.delete_table table_name
end end
Expand Down
18 changes: 9 additions & 9 deletions lib/database_cleaner/active_record/transaction.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ class Transaction
include ::DatabaseCleaner::ActiveRecord::Base include ::DatabaseCleaner::ActiveRecord::Base


def start def start
if connection_klass.connection.respond_to?(:increment_open_transactions) if connection.respond_to?(:increment_open_transactions)
connection_klass.connection.increment_open_transactions connection.increment_open_transactions
else else
connection_klass.__send__(:increment_open_transactions) connection_class.__send__(:increment_open_transactions)
end end
connection_klass.connection.begin_db_transaction connection.begin_db_transaction
end end




def clean 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) if connection.respond_to?(:decrement_open_transactions)
connection_klass.connection.decrement_open_transactions connection.decrement_open_transactions
else else
connection_klass.__send__(:decrement_open_transactions) connection_class.__send__(:decrement_open_transactions)
end end
end end
end end
Expand Down
6 changes: 1 addition & 5 deletions lib/database_cleaner/active_record/truncation.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def restart_identity
def truncate_table(table_name) def truncate_table(table_name)
truncate_tables([table_name]) truncate_tables([table_name])
end end

def truncate_tables(table_names) def truncate_tables(table_names)
execute("TRUNCATE TABLE #{table_names.map{|name| quote_table_name(name)}.join(', ')} #{restart_identity} #{cascade};") execute("TRUNCATE TABLE #{table_names.map{|name| quote_table_name(name)}.join(', ')} #{restart_identity} #{cascade};")
end end
Expand Down Expand Up @@ -119,7 +119,6 @@ class Truncation
include ::DatabaseCleaner::Generic::Truncation include ::DatabaseCleaner::Generic::Truncation


def clean def clean
connection = connection_klass.connection
connection.disable_referential_integrity do connection.disable_referential_integrity do
connection.truncate_tables(tables_to_truncate(connection)) connection.truncate_tables(tables_to_truncate(connection))
end end
Expand All @@ -138,6 +137,3 @@ def migration_storage_name


end end
end end



133 changes: 15 additions & 118 deletions spec/database_cleaner/active_record/base_spec.rb
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -3,140 +3,37 @@
require 'database_cleaner/active_record/base' require 'database_cleaner/active_record/base'
require 'database_cleaner/shared_strategy_spec' require 'database_cleaner/shared_strategy_spec'


module DatabaseCleaner class FakeModel
describe ActiveRecord do def self.connection
it { should respond_to(:available_strategies) } :fake_connection

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

end end
end


module DatabaseCleaner
module ActiveRecord module ActiveRecord
class ExampleStrategy class ExampleStrategy
include ::DatabaseCleaner::ActiveRecord::Base include ::DatabaseCleaner::ActiveRecord::Base
end end


describe ExampleStrategy do 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" 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
end

it "should default to :default" do
subject.db.should == :default
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 describe "#connection" do
File.should_receive(:file?).with(config_location).and_return(false) it "returns the connection from ActiveRecord::Base by default" do
subject.load_config ::ActiveRecord::Base.stub!(:connection).and_return(:fake_connection)
subject.connection_hash.should be_blank subject.connection.should == :fake_connection
end end


it "skips the file when the db is set to :default" do it "returns the connection of the model provided" do
# to avoid https://github.com/bmabey/database_cleaner/issues/72 subject.db = FakeModel
subject.db = :default subject.connection.should == :fake_connection
YAML.should_not_receive(:load)
subject.load_config
end end


end it "allows for the model to be passed in as a string" do

subject.db = "FakeModel"
describe "connection_hash" do subject.connection.should == :fake_connection
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
end end
end end
end end
Expand Down

0 comments on commit 604c9cf

Please sign in to comment.