Refactor adaptors to reflect base class status, now strategy (as in generic)

This commit is contained in:
Jon Rowe 2010-04-22 17:41:02 +01:00
parent 00071394b6
commit 57c11963fb
22 changed files with 275 additions and 341 deletions

6
TODO
View file

@ -0,0 +1,6 @@
REFACTOR AR/DM/CP/MM modules to extend Generic Adaptor, strategies then should have base methods automagically :D,
oh and generic transaction? not implemented i suspect
REFACTOR ADAPTORS / STRATEGIES
PASS CONNECTION TO STRATEGIES
WHERE ARE DATAMAPPER SPECS?

View file

@ -1,17 +0,0 @@
require 'database_cleaner/strategy_base'
module DatabaseCleaner
module ActiveRecord
def self.available_strategies
%w[truncation transaction]
end
module Adaptor
include ::DatabaseCleaner::StrategyBase
def connection_klass
::ActiveRecord::Base
end
end
end
end

View file

@ -0,0 +1,19 @@
require 'database_cleaner/generic/strategy'
module DatabaseCleaner
module ActiveRecord
def self.available_strategies
%w[truncation transaction]
end
module Strategy
include ::DatabaseCleaner::Generic::Strategy
def connection_klass
#TODO, multiple connections...
#::ActiveRecord::Base
end
end
end
end

View file

@ -1,7 +1,7 @@
require 'database_cleaner/active_record/adaptor'
require 'database_cleaner/active_record/strategy'
module DatabaseCleaner::ActiveRecord
class Transaction
include ::DatabaseCleaner::ActiveRecord::Adaptor
include ::DatabaseCleaner::ActiveRecord::Strategy
def start
if connection_klass.connection.respond_to?(:increment_open_transactions)

View file

@ -1,7 +1,7 @@
require 'active_record/base'
require 'active_record/connection_adapters/abstract_adapter'
require "database_cleaner/truncation_base"
require 'database_cleaner/active_record/adaptor'
require "database_cleaner/generic/truncation"
require 'database_cleaner/active_record/strategy'
module ActiveRecord
module ConnectionAdapters
@ -70,9 +70,10 @@ end
module DatabaseCleaner::ActiveRecord
class Truncation < ::DatabaseCleaner::TruncationBase
include ::DatabaseCleaner::ActiveRecord::Adaptor
class Truncation
include ::DatabaseCleaner::ActiveRecord::Strategy
include ::DatabaseCleaner::Generic::Truncation
def clean
connection.disable_referential_integrity do
tables_to_truncate.each do |table_name|

View file

@ -1,8 +1,6 @@
require 'database_cleaner/truncation_base'
module DatabaseCleaner
module CouchPotato
class Truncation < DatabaseCleaner::TruncationBase
class Truncation
def initialize(options = {})
if options.has_key?(:only) || options.has_key?(:except)
raise ArgumentError, "The :only and :except options are not available for use with CouchPotato/CouchDB."

View file

@ -1,5 +1,3 @@
require "database_cleaner/truncation_base"
module DataMapper
module Adapters
@ -116,7 +114,7 @@ end
module DatabaseCleaner::DataMapper
class Truncation < ::DatabaseCleaner::TruncationBase
class Truncation
def clean(repository = :default)
adapter = DataMapper.repository(repository).adapter

View file

@ -0,0 +1,35 @@
module ::DatabaseCleaner
module Generic
module Strategy
def self.included(base)
base.extend(ClassMethods)
base.send(:include, InstanceMethods)
end
module InstanceMethods
def initialize(db = :default)
@db = db
end
def db=(desired_db)
@db = desired_db
end
def db
@db
end
def connection_klass
raise NotImplementedError
end
end
module ClassMethods
def available_strategies
%W[]
end
end
end
end
end

View file

@ -0,0 +1,43 @@
module DatabaseCleaner
module Generic
module Truncation
def self.included(base)
base.send(:include, InstanceMethods)
end
module InstanceMethods
def initialize(opts={})
if !opts.empty? && !(opts.keys - [:only, :except]).empty?
raise ArgumentError, "The only valid options are :only and :except. You specified #{opts.keys.join(',')}."
end
if opts.has_key?(:only) && opts.has_key?(:except)
raise ArgumentError, "You may only specify either :only or :either. Doing both doesn't really make sense does it?"
end
@only = opts[:only]
@tables_to_exclude = (opts[:except] || [])
@tables_to_exclude << migration_storage_name unless migration_storage_name.nil?
end
def start
#included for compatability reasons, do nothing if you don't need to
end
def clean
raise NotImplementedError
end
private
def tables_to_truncate
raise NotImplementedError
end
# overwrite in subclasses
# default implementation given because migration storage need not be present
def migration_storage_name
nil
end
end
end
end
end

View file

@ -1,8 +1,8 @@
require 'database_cleaner/truncation_base'
module DatabaseCleaner
module MongoMapper
class Truncation < DatabaseCleaner::TruncationBase
class Truncation
def clean
if @only
collections.each { |c| c.remove if @only.include?(c.name) }

View file

@ -1,11 +0,0 @@
module DatabaseCleaner
module StrategyBase
def db=(desired_db)
@db = desired_db
end
def db
@db || :default
end
end
end

View file

@ -1,41 +0,0 @@
module DatabaseCleaner
class TruncationBase
def initialize(options = {})
if !options.empty? && !(options.keys - [:only, :except]).empty?
raise ArgumentError, "The only valid options are :only and :except. You specified #{options.keys.join(',')}."
end
if options.has_key?(:only) && options.has_key?(:except)
raise ArgumentError, "You may only specify either :only or :either. Doing both doesn't really make sense does it?"
end
@only = options[:only]
@tables_to_exclude = (options[:except] || [])
if migration_storage = migration_storage_name
@tables_to_exclude << migration_storage
end
end
def start
# no-op
end
def clean
raise NotImplementedError
end
private
def tables_to_truncate
raise NotImplementedError
end
# overwrite in subclasses
# default implementation given because migration storage need not be present
def migration_storage_name
nil
end
end
end

View file

@ -1,19 +0,0 @@
require 'spec_helper'
require 'database_cleaner/active_record/adaptor'
require 'database_cleaner/shared_adaptor_spec'
class ExampleStrategy
include ::DatabaseCleaner::ActiveRecord::Adaptor
end
module DatabaseCleaner
describe ActiveRecord do
it { should respond_to :available_strategies }
end
module ActiveRecord
describe ExampleStrategy do
it_should_behave_like "database cleaner adaptor"
end
end
end

View file

@ -0,0 +1,20 @@
require 'spec_helper'
require 'database_cleaner/active_record/strategy'
require 'database_cleaner/shared_strategy_spec'
module DatabaseCleaner
describe ActiveRecord do
it { should respond_to :available_strategies }
end
module ActiveRecord
class ExampleStrategy
include ::DatabaseCleaner::ActiveRecord::Strategy
end
describe ExampleStrategy do
it_should_behave_like "database cleaner strategy"
it { expect{ subject.connection_klass }.to_not raise_error }
end
end
end

View file

@ -20,145 +20,52 @@ module DatabaseCleaner
end
describe "start" do
context "single connection" do
it "should increment open transactions if possible" do
connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(true)
connection.stub!(:begin_db_transaction)
connection.should_receive(:increment_open_transactions)
Transaction.new.start
end
it "should increment open transactions if possible" do
connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(true)
connection.stub!(:begin_db_transaction)
it "should tell ActiveRecord to increment connection if its not possible to increment current connection" do
connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(false)
connection.stub!(:begin_db_transaction)
::ActiveRecord::Base.should_receive(:increment_open_transactions)
Transaction.new.start
end
it "should start a transaction" do
connection.stub!(:increment_open_transactions)
connection.should_receive(:begin_db_transaction)
Transaction.new.start
end
connection.should_receive(:increment_open_transactions)
Transaction.new.start
end
it "should tell ActiveRecord to increment connection if its not possible to increment current connection" do
connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(false)
connection.stub!(:begin_db_transaction)
context "multiple connection" do
it "should has specs?"
# before(:each) do
# DatabaseCleaner::ActiveRecord.connection_klasses = [model_klass]
# end
#
# after(:each) do
# DatabaseCleaner::ActiveRecord.connection_klasses = []
# end
#
# it "should increment open transactions on both connections if possible" do
# connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(true)
# connection.stub!(:begin_db_transaction)
#
# another_connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(true)
# another_connection.stub!(:begin_db_transaction)
#
# connection.should_receive(:increment_open_transactions)
# another_connection.should_receive(:increment_open_transactions)
# Transaction.new.start
# end
#
# it "should tell ActiveRecord to increment connection if its not possible to increment current connection" do
# connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(false)
# connection.stub!(:begin_db_transaction)
#
# another_connection.stub!(:respond_to?).with(:increment_open_transactions).and_return(false)
# another_connection.stub!(:begin_db_transaction)
#
# ::ActiveRecord::Base.should_receive(:increment_open_transactions)
# model_klass.should_receive(:increment_open_transactions)
# Transaction.new.start
# end
#
# it "should start a transaction" do
# connection.stub!(:increment_open_transactions)
# another_connection.stub!(:increment_open_transactions)
#
# connection.should_receive(:begin_db_transaction)
# another_connection.should_receive(:begin_db_transaction)
# Transaction.new.start
# end
::ActiveRecord::Base.should_receive(:increment_open_transactions)
Transaction.new.start
end
it "should start a transaction" do
connection.stub!(:increment_open_transactions)
connection.should_receive(:begin_db_transaction)
Transaction.new.start
end
end
describe "clean" do
context "single connection" do
it "should finish a transaction" do
connection.stub!(:decrement_open_transactions)
it "should finish a transaction" do
connection.stub!(:decrement_open_transactions)
connection.should_receive(:rollback_db_transaction)
Transaction.new.clean
end
it "should decrement open transactions if possible" do
connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(true)
connection.stub!(:rollback_db_transaction)
connection.should_receive(:decrement_open_transactions)
connection.should_receive(:rollback_db_transaction)
Transaction.new.clean
end
it "should decrement connection via ActiveRecord::Base if connection won't" do
connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(false)
connection.stub!(:rollback_db_transaction)
::ActiveRecord::Base.should_receive(:decrement_open_transactions)
Transaction.new.clean
end
end
context "multiple connection" do
it "should has specs?"
# before(:each) do
# DatabaseCleaner::ActiveRecord.connection_klasses = [model_klass]
# end
#
# after(:each) do
# DatabaseCleaner::ActiveRecord.connection_klasses = []
# end
#
# it "should finish a transaction" do
# connection.stub!(:decrement_open_transactions)
# another_connection.stub!(:decrement_open_transactions)
#
# connection.should_receive(:rollback_db_transaction)
# another_connection.should_receive(:rollback_db_transaction)
# Transaction.new.clean
# end
#
# it "should decrement open transactions if possible" do
# connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(true)
# connection.stub!(:rollback_db_transaction)
#
# another_connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(true)
# another_connection.stub!(:rollback_db_transaction)
#
# connection.should_receive(:decrement_open_transactions)
# another_connection.should_receive(:decrement_open_transactions)
# Transaction.new.clean
# end
#
# it "should decrement connection via ActiveRecord::Base if connection won't" do
# connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(false)
# connection.stub!(:rollback_db_transaction)
#
# another_connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(false)
# another_connection.stub!(:rollback_db_transaction)
#
# ::ActiveRecord::Base.should_receive(:decrement_open_transactions)
# model_klass.should_receive(:decrement_open_transactions)
# Transaction.new.clean
# end
it "should decrement open transactions if possible" do
connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(true)
connection.stub!(:rollback_db_transaction)
connection.should_receive(:decrement_open_transactions)
Transaction.new.clean
end
it "should decrement connection via ActiveRecord::Base if connection won't" do
connection.stub!(:respond_to?).with(:decrement_open_transactions).and_return(false)
connection.stub!(:rollback_db_transaction)
::ActiveRecord::Base.should_receive(:decrement_open_transactions)
Transaction.new.clean
end
end
end

View file

@ -1,6 +1,7 @@
require File.dirname(__FILE__) + '/../../spec_helper'
require 'database_cleaner/active_record/truncation'
require 'active_record'
require 'active_record'
module ActiveRecord
module ConnectionAdapters
[MysqlAdapter, SQLite3Adapter, JdbcAdapter, PostgreSQLAdapter].each do |adapter|
@ -62,76 +63,6 @@ module DatabaseCleaner
running { Truncation.new(:foo => 'bar') }.should raise_error(ArgumentError)
end
end
# context "multiple connection" do
#
# let (:another_connection) { mock('another connection') }
# let (:model_klass) { mock('klass') }
#
# before(:each) do
# another_connection.stub!(:disable_referential_integrity).and_yield
# model_klass.stub!(:connection).and_return(another_connection)
#
# DatabaseCleaner::ActiveRecord.connection_klasses = [model_klass]
# end
#
# after(:each) do
# DatabaseCleaner::ActiveRecord.connection_klasses = []
# end
#
# it "should truncate all tables except for schema_migrations on both connections" do
# connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
# another_connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
#
# connection.should_receive(:truncate_table).with('widgets')
# connection.should_receive(:truncate_table).with('dogs')
# connection.should_not_receive(:truncate_table).with('schema_migrations')
#
# another_connection.should_receive(:truncate_table).with('widgets')
# another_connection.should_receive(:truncate_table).with('dogs')
# another_connection.should_not_receive(:truncate_table).with('schema_migrations')
#
# Truncation.new.clean
# end
#
# it "should only truncate the tables specified in the :only option when provided" do
# connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
# another_connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
#
# connection.should_receive(:truncate_table).with('widgets')
# connection.should_not_receive(:truncate_table).with('dogs')
#
# another_connection.should_receive(:truncate_table).with('widgets')
# another_connection.should_not_receive(:truncate_table).with('dogs')
#
# Truncation.new(:only => ['widgets']).clean
# end
#
# it "should not truncate the tables specified in the :except option" do
# connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
# another_connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
#
# connection.should_receive(:truncate_table).with('dogs')
# connection.should_not_receive(:truncate_table).with('widgets')
#
# another_connection.should_receive(:truncate_table).with('dogs')
# another_connection.should_not_receive(:truncate_table).with('widgets')
#
# Truncation.new(:except => ['widgets']).clean
# end
#
# it "should raise an error when :only and :except options are used" do
# running {
# Truncation.new(:except => ['widgets'], :only => ['widgets'])
# }.should raise_error(ArgumentError)
# end
#
# it "should raise an error when invalid options are provided" do
# running { Truncation.new(:foo => 'bar') }.should raise_error(ArgumentError)
# end
# end
end
end
end

View file

@ -1,8 +0,0 @@
require 'spec_helper'
module ::DatabaseCleaner::Generic
describe Adaptor do
it { should respond_to :db }
it { should respond_to :db= }
it { should respond_to :connection_klass }
end
end

View file

@ -0,0 +1,32 @@
require 'spec_helper'
require 'database_cleaner/generic/strategy'
module ::DatabaseCleaner
module Generic
class ExtendedStrategy
include Strategy
end
describe ExtendedStrategy do
context "class methods" do
subject { ExtendedStrategy }
its (:available_strategies) { should be_empty }
end
it { should respond_to :db }
it { should respond_to :db= }
it { should respond_to :connection_klass }
its (:db) { should == :default }
it "should raise NotImplementedError upon access of connection_klass" do
expect { subject.connection_klass }.to raise_error NotImplementedError
end
it "should accept the desired database upon initalisation" do
eg = ExtendedStrategy.new :my_database
eg.db.should == :my_database
end
end
end
end

View file

@ -0,0 +1,68 @@
require 'spec_helper'
require 'database_cleaner/generic/truncation'
module ::DatabaseCleaner
module Generic
class TruncationExample
include ::DatabaseCleaner::Generic::Truncation
def only
@only
end
def except
@tables_to_exclude
end
end
class MigrationExample < TruncationExample
def migration_storage_name
"migration_storage_name"
end
end
describe TruncationExample do
its (:start) { expect{ subject }.to_not raise_error }
its (:clean) { expect{ subject }.to raise_error NotImplementedError }
context "private methods" do
it { should_not respond_to :tables_to_truncate }
its (:tables_to_truncate) { expect{ subject }.to raise_error NotImplementedError }
it { should_not respond_to :migration_storage_name }
its (:migration_storage_name) { should be_nil }
end
describe "initialize" do
it { expect{ subject }.to_not raise_error }
it "should accept a hash of options" do
expect{ TruncationExample.new {} }.to_not raise_error
end
it { expect{ TruncationExample.new( { :a_random_param => "should raise ArgumentError" } ) }.to raise_error ArgumentError }
it { expect{ TruncationExample.new( { :except => "something",:only => "something else" } ) }.to raise_error ArgumentError }
it { expect{ TruncationExample.new( { :only => "something" } ) }.to_not raise_error ArgumentError }
it { expect{ TruncationExample.new( { :except => "something" } ) }.to_not raise_error ArgumentError }
context "" do
subject { TruncationExample.new( { :only => ["something"] } ) }
its (:only) { should == ["something"] }
its (:except) { should == [] }
end
context "" do
subject { TruncationExample.new( { :except => ["something"] } ) }
its (:only) { should == nil }
its (:except) { should include "something" }
end
context "" do
subject { MigrationExample.new }
its (:only) { should == nil }
its (:except) { should == ["migration_storage_name"] }
end
end
end
end
end

View file

@ -8,7 +8,7 @@ module DatabaseCleaner
#doing this in the file root breaks autospec, doing it before(:all) just fails the specs
before(:all) do
#pend the specs if CouchPotato is missing
#pend the specs if MongoMapper is missing
if defined?(::MongoMapper) && defined?(::Mongo)
require 'mongo_mapper'
require File.dirname(__FILE__) + '/mongo_examples'

View file

@ -1,4 +1,4 @@
shared_examples_for "database cleaner adaptor" do
shared_examples_for "database cleaner strategy" do
it { should respond_to :db }
it { should respond_to :db= }
it { should respond_to :connection_klass }

View file

@ -1,28 +0,0 @@
require File.dirname(__FILE__) + '/../spec_helper'
require 'database_cleaner/strategy_base'
#require 'database_cleaner/active_record/transaction'
#require 'database_cleaner/data_mapper/transaction'
class ExampleStrategy
include ::DatabaseCleaner::StrategyBase
end
module DatabaseCleaner
describe StrategyBase do
context "upon inclusion" do
subject { ExampleStrategy.new }
it "should add the db setter method" do
should respond_to :db=
end
its(:db) { should == :default }
it "should store a symbol representing the db to use" do
subject.db = :my_specific_connection
subject.db.should == :my_specific_connection
end
end
end
end