From f7858cd50118eaeaf644d0c83cae81412df2055e Mon Sep 17 00:00:00 2001 From: Jon Rowe Date: Tue, 30 Mar 2010 22:49:30 +0100 Subject: [PATCH] specs for multi orms/connections and some implementation --- lib/database_cleaner/configuration.rb | 101 ++++-- spec/database_cleaner/configuration_spec.rb | 353 +++++++++++++++----- spec/spec.opts | 1 + 3 files changed, 352 insertions(+), 103 deletions(-) diff --git a/lib/database_cleaner/configuration.rb b/lib/database_cleaner/configuration.rb index 659ec0f..f4e4c4f 100644 --- a/lib/database_cleaner/configuration.rb +++ b/lib/database_cleaner/configuration.rb @@ -36,9 +36,48 @@ module DatabaseCleaner %w[truncation] end end + + class << self + def [](orm) + raise NoORMDetected if orm.nil? + DatabaseCleaner::Base.new(orm) + end - class << self - + def connections + @connections ||= [::DatabaseCleaner::Base.new] + end + + def strategy=(stratagem) + self.connections.first.strategy = stratagem + end + + def orm=(orm) + self.connections.first.orm = orm + end + + def start + self.connections.first.start + end + + def clean + self.connections.first.clean + end + + def clean_with(stratagem) + self.connections.first.clean_with stratagem + end + end + + class Base + + def initialize(desired_orm = nil) + if desired_orm == :autodetect || desired_orm.nil? + autodetect + else + self.orm = desired_orm + end + end + def create_strategy(*args) strategy, *strategy_args = args orm_strategy(strategy).new(*strategy_args) @@ -63,8 +102,8 @@ module DatabaseCleaner end end - def orm=(orm_string) - @orm = orm_string + def orm=(desired_orm) + @orm = desired_orm end def start @@ -76,52 +115,62 @@ module DatabaseCleaner end alias clean! clean - + + def orm + @orm || autodetect + end + + def auto_detected + return true unless @autodetected.nil? + end + private def strategy return @strategy if @strategy raise NoStrategySetError, "Please set a strategy with DatabaseCleaner.strategy=." end - + + def orm_module + ::DatabaseCleaner.orm_module(orm) + end + def orm_strategy(strategy) - require "database_cleaner/#{orm}/#{strategy}" + require "database_cleaner/#{orm.to_s}/#{strategy.to_s}" orm_module.const_get(strategy.to_s.capitalize) rescue LoadError => e raise UnknownStrategySpecified, "The '#{strategy}' strategy does not exist for the #{orm} ORM! Available strategies: #{orm_module.available_strategies.join(', ')}" end - - - def orm - @orm ||=begin + + def autodetect + @orm ||= begin + @autodetected = true if defined? ::ActiveRecord - 'active_record' + :active_record elsif defined? ::DataMapper - 'data_mapper' + :data_mapper elsif defined? ::MongoMapper - 'mongo_mapper' + :mongo_mapper elsif defined? ::CouchPotato - 'couch_potato' + :couch_potato else raise NoORMDetected, "No known ORM was detected! Is ActiveRecord, DataMapper, MongoMapper or CouchPotato loaded?" end end end - - + def orm_module case orm - when 'active_record' - DatabaseCleaner::ActiveRecord - when 'data_mapper' - DatabaseCleaner::DataMapper - when 'mongo_mapper' - DatabaseCleaner::MongoMapper - when 'couch_potato' - DatabaseCleaner::CouchPotato + when :active_record + DatabaseCleaner::ActiveRecord + when :data_mapper + DatabaseCleaner::DataMapper + when :mongo_mapper + DatabaseCleaner::MongoMapper + when :couch_potato + DatabaseCleaner::CouchPotato end end - end end diff --git a/spec/database_cleaner/configuration_spec.rb b/spec/database_cleaner/configuration_spec.rb index bb772f8..29fb6b5 100644 --- a/spec/database_cleaner/configuration_spec.rb +++ b/spec/database_cleaner/configuration_spec.rb @@ -18,101 +18,300 @@ describe DatabaseCleaner do end end end + + context "orm specification" do - # These examples muck around with the constants for autodetection so we need to clean up.... - before(:all) do - TempAR = ActiveRecord unless defined?(TempAR) - TempMM = MongoMapper unless defined?(TempMM) - Object.send(:remove_const, 'MongoMapper') if defined?(::MongoMapper) - # need to add one for each ORM that we load in the spec helper... - end - after(:all) do - Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord) #want to make sure we have the real one... - ActiveRecord = TempAR - MongoMapper = TempMM - end - - before(:each) do - DatabaseCleaner::ActiveRecord::Transaction.stub!(:new).and_return(@strategy = mock('strategy')) - Object.const_set('ActiveRecord', "just mocking out the constant here...") unless defined?(::ActiveRecord) - DatabaseCleaner.strategy = nil - DatabaseCleaner.orm = nil + it "should not accept unrecognised orms" do + lambda { ::DatabaseCleaner[nil] }.should raise_error ::DatabaseCleaner::NoORMDetected + end + + it "should accept :active_record" do + cleaner = ::DatabaseCleaner[:active_record] + cleaner.should be_a ::DatabaseCleaner::Base + cleaner.orm.should == :active_record + end + + it "should accept :data_mapper" do + cleaner = ::DatabaseCleaner[:data_mapper] + cleaner.should be_a ::DatabaseCleaner::Base + cleaner.orm.should == :data_mapper + end + + it "should accept :mongo_mapper" do + cleaner = ::DatabaseCleaner[:mongo_mapper] + cleaner.should be_a ::DatabaseCleaner::Base + cleaner.orm.should == :mongo_mapper + end + + it "should accept :couch_potato" do + cleaner = ::DatabaseCleaner[:couch_potato] + cleaner.should be_a ::DatabaseCleaner::Base + cleaner.orm.should == :couch_potato + end end - describe ".create_strategy" do - it "should initialize and return the appropirate strategy" do - DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash') - result = DatabaseCleaner.create_strategy(:transaction, {'options' => 'hash'}) - result.should == @strategy + context "class methods" do + it "should have array of connections (orm agnostic)" do + ::DatabaseCleaner.connections.should respond_to(:each) + end + + it "should give me a default (autodetection) databasecleaner by default" do + ::DatabaseCleaner.connections.should have (1).items + ::DatabaseCleaner.connections.first.should be_a ::DatabaseCleaner::Base end end - - describe ".clean_with" do - it "should initialize the appropirate strategy and clean with it" do - DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash') - @strategy.should_receive(:clean) - DatabaseCleaner.clean_with(:transaction, 'options' => 'hash') + + context "instance methods" do + it "should default to autodetect upon initalisation" do + cleaner = ::DatabaseCleaner::Base.new + cleaner.auto_detected.should == true end + + context "autodetect" do + before(:all) do + Temp_AR = ActiveRecord if defined?(::ActiveRecord) and not defined?(Temp_AR) + Temp_DM = DataMapper if defined?(::DataMapper) and not defined?(Temp_DM) + Temp_MM = MongoMapper if defined?(::MongoMapper) and not defined?(Temp_MM) + Temp_CP = CouchPotato if defined?(::CouchPotato) and not defined?(Temp_CP) + end + + before(:each) do + # Remove ORM consts for these tests + Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord) + Object.send(:remove_const, 'DataMapper') if defined?(::DataMapper) + Object.send(:remove_const, 'MongoMapper') if defined?(::MongoMapper) + Object.send(:remove_const, 'CouchPotato') if defined?(::CouchPotato) + end + + + it "should raise an error when no ORM is detected" do + running { DatabaseCleaner::Base.new(:autodetect) }.should raise_error(DatabaseCleaner::NoORMDetected) + end + + it "should detect ActiveRecord first" do + Object.const_set('ActiveRecord','Actively mocking records.') + Object.const_set('DataMapper', 'Mapping data mocks') + Object.const_set('MongoMapper', 'Mapping mock mongos') + Object.const_set('CouchPotato', 'Couching mock potatos') + + cleaner = DatabaseCleaner::Base.new :autodetect + cleaner.orm.should == :active_record + end + it "should detect DataMapper second" do + Object.const_set('DataMapper', 'Mapping data mocks') + Object.const_set('MongoMapper', 'Mapping mock mongos') + Object.const_set('CouchPotato', 'Couching mock potatos') + + cleaner = DatabaseCleaner::Base.new :autodetect + cleaner.orm.should == :data_mapper + end + it "should detect MongoMapper third" do + Object.const_set('MongoMapper', 'Mapping mock mongos') + Object.const_set('CouchPotato', 'Couching mock potatos') + + cleaner = DatabaseCleaner::Base.new :autodetect + cleaner.orm.should == :mongo_mapper + end + + it "should detect CouchPotato last" do + Object.const_set('CouchPotato', 'Couching mock potatos') + + cleaner = DatabaseCleaner::Base.new :autodetect + cleaner.orm.should == :couch_potato + end + + after(:all) do + Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord) + Object.send(:remove_const, 'DataMapper') if defined?(::DataMapper) + Object.send(:remove_const, 'MongoMapper') if defined?(::MongoMapper) + Object.send(:remove_const, 'CouchPotato') if defined?(::CouchPotato) + + # Restore ORMs + ActiveRecord = Temp_AR if defined? Temp_AR + DataMapper = Temp_DM if defined? Temp_DM + MongoMapper = Temp_MM if defined? Temp_MM + CouchPotato = Temp_CP if defined? Temp_CP + end + end end - describe ".strategy=" do - it "should initialize the appropirate strategy based on the ORM adapter detected" do - DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash') - DatabaseCleaner.strategy = :transaction, {'options' => 'hash'} - - Object.send(:remove_const, 'ActiveRecord') - Object.const_set('DataMapper', "just mocking out the constant here...") - DatabaseCleaner.orm = nil - - DatabaseCleaner::DataMapper::Transaction.should_receive(:new).with(no_args) - DatabaseCleaner.strategy = :transaction + context "single orm" do + let (:connection) { ::DatabaseCleaner.connections.first } + it "should proxy strategy=" do + stratagum = mock("stratagum") + connection.should_receive(:strategy=).with(stratagum) + ::DatabaseCleaner.strategy = stratagum end - - it "should raise an error when no ORM is detected" do - Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord) - Object.send(:remove_const, 'DataMapper') if defined?(::DataMapper) - Object.send(:remove_const, 'CouchPotato') if defined?(::CouchPotato) - - running { DatabaseCleaner.strategy = :transaction }.should raise_error(DatabaseCleaner::NoORMDetected) + + it "should proxy orm=" do + orm = mock("orm") + connection.should_receive(:orm=).with(orm) + ::DatabaseCleaner.orm = orm end - - it "should use the strategy version of the ORM specified with #orm=" do - DatabaseCleaner.orm = 'data_mapper' - DatabaseCleaner::DataMapper::Transaction.should_receive(:new) - - DatabaseCleaner.strategy = :transaction + + it "should proxy start" do + connection.should_receive(:start) + ::DatabaseCleaner.start end - - it "should raise an error when multiple args is passed in and the first is not a symbol" do - running { DatabaseCleaner.strategy=Object.new, {:foo => 'bar'} }.should raise_error(ArgumentError) + + it "should proxy clean" do + connection.should_receive(:clean) + ::DatabaseCleaner.clean end - - it "should raise an error when the specified strategy is not found" do - running { DatabaseCleaner.strategy = :foo }.should raise_error(DatabaseCleaner::UnknownStrategySpecified) + + it "should proxy clean_with" do + stratagem = mock("stratgem") + connection.should_receive(:clean_with).with(stratagem) + ::DatabaseCleaner.clean_with stratagem end - - it "should allow any object to be set as the strategy" do - mock_strategy = mock('strategy') - running { DatabaseCleaner.strategy = mock_strategy }.should_not raise_error - end - end - - - %w[start clean].each do |strategy_method| - describe ".#{strategy_method}" do - it "should be delgated to the strategy set with strategy=" do - DatabaseCleaner.strategy = :transaction - - @strategy.should_receive(strategy_method) - - DatabaseCleaner.send(strategy_method) + + describe ::DatabaseCleaner::Base do + + describe "orm_module" do + it "should proxy to the class method" + end + + let(:strategy) { mock("stratagum") } + + context "active record" do + let(:cleaner) { ::DatabaseCleaner::Base.new(:active_record) } + + before(:each) do + ::DatabaseCleaner::ActiveRecord::Transaction.stub!(:new).and_return(strategy) + #Object.const_set('ActiveRecord', "just mocking out the constant here...") unless defined?(::ActiveRecord) + cleaner.strategy = nil + end + + describe ".create_strategy" do + it "should initialize and return the appropirate strategy" do + DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash') + result = cleaner.create_strategy(:transaction, {'options' => 'hash'}) + result.should == strategy end + end - it "should raise en error when no strategy has been set" do - running { DatabaseCleaner.send(strategy_method) }.should raise_error(DatabaseCleaner::NoStrategySetError) + describe ".clean_with" do + it "should initialize the appropirate strategy and clean with it" do + DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash') + strategy.should_receive(:clean) + cleaner.clean_with(:transaction, 'options' => 'hash') + end + end + + describe ".strategy=" do + it "should initialize the appropirate strategy for active record" do + DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash') + cleaner.strategy = :transaction, {'options' => 'hash'} + end + + it "should allow any object to be set as the strategy" do + mock_strategy = mock('strategy') + running { DatabaseCleaner.strategy = mock_strategy }.should_not raise_error + end + + it "should raise an error when the specified strategy is not found" do + running { DatabaseCleaner.strategy = :foo }.should raise_error(DatabaseCleaner::UnknownStrategySpecified) + end + + it "should raise an error when multiple args is passed in and the first is not a symbol" do + running { DatabaseCleaner.strategy=Object.new, {:foo => 'bar'} }.should raise_error(ArgumentError) + end + end + + %w[start clean].each do |strategy_method| + describe ".#{strategy_method}" do + it "should be delgated to the strategy set with strategy=" do + cleaner.strategy = :transaction + + strategy.should_receive(strategy_method) + + cleaner.send(strategy_method) + end + + it "should raise en error when no strategy has been set" do + running { cleaner.send(strategy_method) }.should raise_error(DatabaseCleaner::NoStrategySetError) + end end end end + context "data mapper" do + let(:cleaner) { ::DatabaseCleaner::Base.new(:data_mapper) } + + before(:each) do + ::DatabaseCleaner::DataMapper::Transaction.stub!(:new).and_return(strategy) + #Object.const_set('ActiveRecord', "just mocking out the constant here...") unless defined?(::ActiveRecord) + cleaner.strategy = nil + end + + describe ".create_strategy" do + it "should initialize and return the appropirate strategy" do + DatabaseCleaner::DataMapper::Transaction.should_receive(:new) + result = cleaner.create_strategy(:transaction) + result.should == strategy + end + end + + describe ".clean_with" do + it "should initialize the appropirate strategy and clean with it" do + DatabaseCleaner::DataMapper::Transaction.should_receive(:new) + strategy.should_receive(:clean) + cleaner.clean_with(:transaction) + end + end + + describe ".strategy=" do + it "should initalize the appropriate strategy for data mapper" do + DatabaseCleaner::DataMapper::Transaction.should_receive(:new).with(no_args) + cleaner.strategy = :transaction + end + + it "should allow any object to be set as the strategy" do + mock_strategy = mock('strategy') + running { cleaner.strategy = mock_strategy }.should_not raise_error + end + + it "should raise an error when the specified strategy is not found" do + running { cleaner.strategy = :foo }.should raise_error(DatabaseCleaner::UnknownStrategySpecified) + end + + it "should raise an error when multiple args is passed in and the first is not a symbol" do + running { cleaner.strategy=Object.new, {:foo => 'bar'} }.should raise_error(ArgumentError) + end + end + + %w[start clean].each do |strategy_method| + describe ".#{strategy_method}" do + it "should be delgated to the strategy set with strategy=" do + cleaner.strategy = :transaction + + strategy.should_receive(strategy_method) + + cleaner.send(strategy_method) + end + + it "should raise en error when no strategy has been set" do + running { cleaner.send(strategy_method) }.should raise_error(DatabaseCleaner::NoStrategySetError) + end + end + end + end + + # it "should raise an error when no ORM is detected" do + # Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord) + # Object.send(:remove_const, 'DataMapper') if defined?(::DataMapper) + # Object.send(:remove_const, 'CouchPotato') if defined?(::CouchPotato) + # + # running { DatabaseCleaner.strategy = :transaction }.should raise_error(DatabaseCleaner::NoORMDetected) + # end + + # it "should use the strategy version of the ORM specified with #orm=" do + # DatabaseCleaner.orm = 'data_mapper' + # DatabaseCleaner::DataMapper::Transaction.should_receive(:new) + # + # DatabaseCleaner.strategy = :transaction + # end + + end end diff --git a/spec/spec.opts b/spec/spec.opts index 8c1da55..6d0c7c4 100644 --- a/spec/spec.opts +++ b/spec/spec.opts @@ -4,3 +4,4 @@ mtime --reverse --backtrace +--debugger \ No newline at end of file