mirror of
https://github.com/DatabaseCleaner/database_cleaner
synced 2023-03-27 23:22:03 -04:00
remove duplication by loading adapter files from adapter gems.
This commit is contained in:
parent
3e3e9055c0
commit
b83ed7d1e3
32 changed files with 3 additions and 1487 deletions
|
@ -26,6 +26,7 @@ Gem::Specification.new do |s|
|
|||
"cucumber.yml"]
|
||||
|
||||
s.files += Dir['lib/**/*.rb']
|
||||
s.files += Dir['adapters/**/lib/**/*.rb']
|
||||
|
||||
s.homepage = "http://github.com/DatabaseCleaner/database_cleaner"
|
||||
s.license = 'MIT'
|
||||
|
|
|
@ -1,92 +0,0 @@
|
|||
require 'database_cleaner/generic/base'
|
||||
require 'active_record'
|
||||
require 'erb'
|
||||
|
||||
module DatabaseCleaner
|
||||
module ActiveRecord
|
||||
|
||||
def self.available_strategies
|
||||
%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
|
||||
end
|
||||
|
||||
def db
|
||||
@db ||= super
|
||||
end
|
||||
|
||||
def load_config
|
||||
if self.db != :default && self.db.is_a?(Symbol) && File.file?(ActiveRecord.config_file_location)
|
||||
connection_details = YAML::load(ERB.new(IO.read(ActiveRecord.config_file_location)).result)
|
||||
@connection_hash = valid_config(connection_details)[self.db.to_s]
|
||||
end
|
||||
end
|
||||
|
||||
def valid_config(connection_file)
|
||||
if !::ActiveRecord::Base.configurations.nil? && !::ActiveRecord::Base.configurations.empty?
|
||||
if connection_file != ::ActiveRecord::Base.configurations
|
||||
return ::ActiveRecord::Base.configurations
|
||||
end
|
||||
end
|
||||
connection_file
|
||||
end
|
||||
|
||||
def connection_class
|
||||
@connection_class ||= if db && !db.is_a?(Symbol)
|
||||
db
|
||||
elsif connection_hash
|
||||
lookup_from_connection_pool || establish_connection
|
||||
else
|
||||
::ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
|
||||
def self.migration_table_name
|
||||
if ::ActiveRecord::VERSION::MAJOR < 5
|
||||
::ActiveRecord::Migrator.schema_migrations_table_name
|
||||
else
|
||||
::ActiveRecord::SchemaMigration.table_name
|
||||
end
|
||||
end
|
||||
|
||||
def self.exclusion_condition(column_name)
|
||||
result = " #{column_name} <> '#{::DatabaseCleaner::ActiveRecord::Base.migration_table_name}' "
|
||||
if ::ActiveRecord::VERSION::MAJOR >= 5
|
||||
result += " AND #{column_name} <> '#{::ActiveRecord::Base.internal_metadata_table_name}' "
|
||||
end
|
||||
result
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def lookup_from_connection_pool
|
||||
if ::ActiveRecord::Base.respond_to?(:descendants)
|
||||
database_name = connection_hash["database"] || connection_hash[:database]
|
||||
models = ::ActiveRecord::Base.descendants
|
||||
models.select(&:connection_pool).detect { |m| m.connection_pool.spec.config[:database] == database_name }
|
||||
end
|
||||
end
|
||||
|
||||
def establish_connection
|
||||
::ActiveRecord::Base.establish_connection(connection_hash)
|
||||
::ActiveRecord::Base
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,109 +0,0 @@
|
|||
require 'active_record/base'
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
require "database_cleaner/generic/truncation"
|
||||
require 'database_cleaner/active_record/base'
|
||||
require 'database_cleaner/active_record/truncation'
|
||||
|
||||
module DatabaseCleaner
|
||||
module ConnectionAdapters
|
||||
module AbstractDeleteAdapter
|
||||
def delete_table(table_name)
|
||||
raise NotImplementedError
|
||||
end
|
||||
end
|
||||
|
||||
module GenericDeleteAdapter
|
||||
def delete_table(table_name)
|
||||
execute("DELETE FROM #{quote_table_name(table_name)};")
|
||||
end
|
||||
end
|
||||
|
||||
module OracleDeleteAdapter
|
||||
def delete_table(table_name)
|
||||
execute("DELETE FROM #{quote_table_name(table_name)}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
AbstractAdapter.class_eval { include DatabaseCleaner::ConnectionAdapters::AbstractDeleteAdapter }
|
||||
|
||||
JdbcAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::GenericDeleteAdapter } if defined?(JdbcAdapter)
|
||||
AbstractMysqlAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::GenericDeleteAdapter } if defined?(AbstractMysqlAdapter)
|
||||
Mysql2Adapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::GenericDeleteAdapter } if defined?(Mysql2Adapter)
|
||||
SQLiteAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::GenericDeleteAdapter } if defined?(SQLiteAdapter)
|
||||
SQLite3Adapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::GenericDeleteAdapter } if defined?(SQLite3Adapter)
|
||||
PostgreSQLAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::GenericDeleteAdapter } if defined?(PostgreSQLAdapter)
|
||||
IBM_DBAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::GenericDeleteAdapter } if defined?(IBM_DBAdapter)
|
||||
SQLServerAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::GenericDeleteAdapter } if defined?(SQLServerAdapter)
|
||||
OracleEnhancedAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::OracleDeleteAdapter } if defined?(OracleEnhancedAdapter)
|
||||
end
|
||||
end
|
||||
|
||||
module DatabaseCleaner::ActiveRecord
|
||||
module SelectiveTruncation
|
||||
def tables_to_truncate(connection)
|
||||
if information_schema_exists?(connection)
|
||||
(@only || tables_with_new_rows(connection)) - @tables_to_exclude
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def tables_with_new_rows(connection)
|
||||
stats = table_stats_query(connection)
|
||||
if stats != ''
|
||||
connection.select_values(stats)
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
def table_stats_query(connection)
|
||||
@table_stats_query ||= build_table_stats_query(connection)
|
||||
ensure
|
||||
@table_stats_query = nil unless @cache_tables
|
||||
end
|
||||
|
||||
def build_table_stats_query(connection)
|
||||
tables = connection.select_values(<<-SQL)
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = database()
|
||||
AND #{::DatabaseCleaner::ActiveRecord::Base.exclusion_condition('table_name')};
|
||||
SQL
|
||||
queries = tables.map do |table|
|
||||
"(SELECT #{connection.quote(table)} FROM #{connection.quote_table_name(table)} LIMIT 1)"
|
||||
end
|
||||
queries.join(' UNION ALL ')
|
||||
end
|
||||
|
||||
def information_schema_exists? connection
|
||||
return false unless connection.is_a? ActiveRecord::ConnectionAdapters::Mysql2Adapter
|
||||
@information_schema_exists ||=
|
||||
begin
|
||||
connection.execute("SELECT 1 FROM information_schema.tables")
|
||||
true
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Deletion < Truncation
|
||||
if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
|
||||
include SelectiveTruncation
|
||||
end
|
||||
|
||||
def clean
|
||||
connection = connection_class.connection
|
||||
connection.disable_referential_integrity do
|
||||
tables_to_truncate(connection).each do |table_name|
|
||||
connection.delete_table table_name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,59 +0,0 @@
|
|||
require 'database_cleaner/active_record/base'
|
||||
require 'database_cleaner/generic/transaction'
|
||||
|
||||
module DatabaseCleaner::ActiveRecord
|
||||
class Transaction
|
||||
include ::DatabaseCleaner::ActiveRecord::Base
|
||||
include ::DatabaseCleaner::Generic::Transaction
|
||||
|
||||
def start
|
||||
# Hack to make sure that the connection is properly setup for
|
||||
# the clean code.
|
||||
connection_class.connection.transaction{ }
|
||||
|
||||
if connection_maintains_transaction_count?
|
||||
if connection_class.connection.respond_to?(:increment_open_transactions)
|
||||
connection_class.connection.increment_open_transactions
|
||||
else
|
||||
connection_class.__send__(:increment_open_transactions)
|
||||
end
|
||||
end
|
||||
if connection_class.connection.respond_to?(:begin_transaction)
|
||||
connection_class.connection.begin_transaction :joinable => false
|
||||
else
|
||||
connection_class.connection.begin_db_transaction
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def clean
|
||||
connection_class.connection_pool.connections.each do |connection|
|
||||
next unless connection.open_transactions > 0
|
||||
|
||||
if connection.respond_to?(:rollback_transaction)
|
||||
connection.rollback_transaction
|
||||
else
|
||||
connection.rollback_db_transaction
|
||||
end
|
||||
|
||||
# The below is for handling after_commit hooks.. see https://github.com/bmabey/database_cleaner/issues/99
|
||||
if connection.respond_to?(:rollback_transaction_records, true)
|
||||
connection.send(:rollback_transaction_records, true)
|
||||
end
|
||||
|
||||
if connection_maintains_transaction_count?
|
||||
if connection.respond_to?(:decrement_open_transactions)
|
||||
connection.decrement_open_transactions
|
||||
else
|
||||
connection_class.__send__(:decrement_open_transactions)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def connection_maintains_transaction_count?
|
||||
ActiveRecord::VERSION::MAJOR < 4
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,274 +0,0 @@
|
|||
require 'active_record/base'
|
||||
|
||||
require 'active_record/connection_adapters/abstract_adapter'
|
||||
|
||||
#Load available connection adapters
|
||||
%w(
|
||||
abstract_mysql_adapter postgresql_adapter sqlite3_adapter mysql_adapter mysql2_adapter oracle_enhanced_adapter
|
||||
).each do |known_adapter|
|
||||
begin
|
||||
require "active_record/connection_adapters/#{known_adapter}"
|
||||
rescue LoadError
|
||||
end
|
||||
end
|
||||
|
||||
require "database_cleaner/generic/truncation"
|
||||
require 'database_cleaner/active_record/base'
|
||||
|
||||
module DatabaseCleaner
|
||||
module ConnectionAdapters
|
||||
|
||||
module AbstractAdapter
|
||||
# used to be called views but that can clash with gems like schema_plus
|
||||
# this gem is not meant to be exposing such an extra interface any way
|
||||
def database_cleaner_view_cache
|
||||
@views ||= select_values("select table_name from information_schema.views where table_schema = '#{current_database}'") rescue []
|
||||
end
|
||||
|
||||
def database_cleaner_table_cache
|
||||
# the adapters don't do caching (#130) but we make the assumption that the list stays the same in tests
|
||||
@database_cleaner_tables ||= ::ActiveRecord::VERSION::MAJOR >= 5 ? data_sources : tables
|
||||
end
|
||||
|
||||
def truncate_table(table_name)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def truncate_tables(tables)
|
||||
tables.each do |table_name|
|
||||
self.truncate_table(table_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module AbstractMysqlAdapter
|
||||
def truncate_table(table_name)
|
||||
execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
|
||||
end
|
||||
|
||||
def truncate_tables(tables)
|
||||
tables.each { |t| truncate_table(t) }
|
||||
end
|
||||
|
||||
def pre_count_truncate_tables(tables, options = {:reset_ids => true})
|
||||
filter = options[:reset_ids] ? method(:has_been_used?) : method(:has_rows?)
|
||||
truncate_tables(tables.select(&filter))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def row_count(table)
|
||||
# Patch for MysqlAdapter with ActiveRecord 3.2.7 later
|
||||
# select_value("SELECT 1") #=> "1"
|
||||
select_value("SELECT EXISTS (SELECT 1 FROM #{quote_table_name(table)} LIMIT 1)").to_i
|
||||
end
|
||||
|
||||
def auto_increment_value(table)
|
||||
select_value(<<-SQL).to_i
|
||||
SELECT auto_increment
|
||||
FROM information_schema.tables
|
||||
WHERE table_name = '#{table}'
|
||||
AND table_schema = database()
|
||||
SQL
|
||||
end
|
||||
|
||||
# This method tells us if the given table has been inserted into since its
|
||||
# last truncation. Note that the table might have been populated, which
|
||||
# increased the auto-increment counter, but then cleaned again such that
|
||||
# it appears empty now.
|
||||
def has_been_used?(table)
|
||||
has_rows?(table) || auto_increment_value(table) > 1
|
||||
end
|
||||
|
||||
def has_rows?(table)
|
||||
row_count(table) > 0
|
||||
end
|
||||
end
|
||||
|
||||
module IBM_DBAdapter
|
||||
def truncate_table(table_name)
|
||||
execute("TRUNCATE #{quote_table_name(table_name)} IMMEDIATE")
|
||||
end
|
||||
end
|
||||
|
||||
module SQLiteAdapter
|
||||
def delete_table(table_name)
|
||||
execute("DELETE FROM #{quote_table_name(table_name)};")
|
||||
if uses_sequence
|
||||
execute("DELETE FROM sqlite_sequence where name = '#{table_name}';")
|
||||
end
|
||||
end
|
||||
alias truncate_table delete_table
|
||||
|
||||
def truncate_tables(tables)
|
||||
tables.each { |t| truncate_table(t) }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns a boolean indicating if the SQLite database is using the sqlite_sequence table.
|
||||
def uses_sequence
|
||||
select_value("SELECT name FROM sqlite_master WHERE type='table' AND name='sqlite_sequence';")
|
||||
end
|
||||
end
|
||||
|
||||
module TruncateOrDelete
|
||||
def truncate_table(table_name)
|
||||
begin
|
||||
execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
|
||||
rescue ::ActiveRecord::StatementInvalid
|
||||
execute("DELETE FROM #{quote_table_name(table_name)};")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module OracleAdapter
|
||||
def truncate_table(table_name)
|
||||
execute("TRUNCATE TABLE #{quote_table_name(table_name)}")
|
||||
end
|
||||
end
|
||||
|
||||
module PostgreSQLAdapter
|
||||
def db_version
|
||||
@db_version ||= postgresql_version
|
||||
end
|
||||
|
||||
def cascade
|
||||
@cascade ||= db_version >= 80200 ? 'CASCADE' : ''
|
||||
end
|
||||
|
||||
def restart_identity
|
||||
@restart_identity ||= db_version >= 80400 ? 'RESTART IDENTITY' : ''
|
||||
end
|
||||
|
||||
def truncate_table(table_name)
|
||||
truncate_tables([table_name])
|
||||
end
|
||||
|
||||
def truncate_tables(table_names)
|
||||
return if table_names.nil? || table_names.empty?
|
||||
execute("TRUNCATE TABLE #{table_names.map{|name| quote_table_name(name)}.join(', ')} #{restart_identity} #{cascade};")
|
||||
end
|
||||
|
||||
def pre_count_truncate_tables(tables, options = {:reset_ids => true})
|
||||
filter = options[:reset_ids] ? method(:has_been_used?) : method(:has_rows?)
|
||||
truncate_tables(tables.select(&filter))
|
||||
end
|
||||
|
||||
def database_cleaner_table_cache
|
||||
# AR returns a list of tables without schema but then returns a
|
||||
# migrations table with the schema. There are other problems, too,
|
||||
# with using the base list. If a table exists in multiple schemas
|
||||
# within the search path, truncation without the schema name could
|
||||
# result in confusing, if not unexpected results.
|
||||
@database_cleaner_tables ||= tables_with_schema
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns a boolean indicating if the given table has an auto-inc number higher than 0.
|
||||
# Note, this is different than an empty table since an table may populated, the index increased,
|
||||
# but then the table is cleaned. In other words, this function tells us if the given table
|
||||
# was ever inserted into.
|
||||
def has_been_used?(table)
|
||||
return has_rows?(table) unless has_sequence?(table)
|
||||
|
||||
cur_val = select_value("SELECT currval('#{table}_id_seq');").to_i rescue 0
|
||||
cur_val > 0
|
||||
end
|
||||
|
||||
def has_sequence?(table)
|
||||
select_value("SELECT true FROM pg_class WHERE relname = '#{table}_id_seq';")
|
||||
end
|
||||
|
||||
def has_rows?(table)
|
||||
select_value("SELECT true FROM #{table} LIMIT 1;")
|
||||
end
|
||||
|
||||
def tables_with_schema
|
||||
rows = select_rows <<-_SQL
|
||||
SELECT schemaname || '.' || tablename
|
||||
FROM pg_tables
|
||||
WHERE
|
||||
tablename !~ '_prt_' AND
|
||||
#{::DatabaseCleaner::ActiveRecord::Base.exclusion_condition('tablename')} AND
|
||||
schemaname = ANY (current_schemas(false))
|
||||
_SQL
|
||||
rows.collect { |result| result.first }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
#Apply adapter decoraters where applicable (adapter should be loaded)
|
||||
AbstractAdapter.class_eval { include DatabaseCleaner::ConnectionAdapters::AbstractAdapter }
|
||||
|
||||
if defined?(JdbcAdapter)
|
||||
if defined?(OracleJdbcConnection)
|
||||
JdbcAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::OracleAdapter }
|
||||
else
|
||||
JdbcAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::TruncateOrDelete }
|
||||
end
|
||||
end
|
||||
AbstractMysqlAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::AbstractMysqlAdapter } if defined?(AbstractMysqlAdapter)
|
||||
Mysql2Adapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::AbstractMysqlAdapter } if defined?(Mysql2Adapter)
|
||||
MysqlAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::AbstractMysqlAdapter } if defined?(MysqlAdapter)
|
||||
SQLiteAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::SQLiteAdapter } if defined?(SQLiteAdapter)
|
||||
SQLite3Adapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::SQLiteAdapter } if defined?(SQLite3Adapter)
|
||||
PostgreSQLAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::PostgreSQLAdapter } if defined?(PostgreSQLAdapter)
|
||||
IBM_DBAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::IBM_DBAdapter } if defined?(IBM_DBAdapter)
|
||||
SQLServerAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::TruncateOrDelete } if defined?(SQLServerAdapter)
|
||||
OracleEnhancedAdapter.class_eval { include ::DatabaseCleaner::ConnectionAdapters::OracleAdapter } if defined?(OracleEnhancedAdapter)
|
||||
end
|
||||
end
|
||||
|
||||
module DatabaseCleaner::ActiveRecord
|
||||
class Truncation
|
||||
include ::DatabaseCleaner::ActiveRecord::Base
|
||||
include ::DatabaseCleaner::Generic::Truncation
|
||||
|
||||
def clean
|
||||
connection = connection_class.connection
|
||||
connection.disable_referential_integrity do
|
||||
if pre_count? && connection.respond_to?(:pre_count_truncate_tables)
|
||||
connection.pre_count_truncate_tables(tables_to_truncate(connection), {:reset_ids => reset_ids?})
|
||||
else
|
||||
connection.truncate_tables(tables_to_truncate(connection))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tables_to_truncate(connection)
|
||||
tables_in_db = cache_tables? ? connection.database_cleaner_table_cache : connection.tables
|
||||
to_reject = (@tables_to_exclude + connection.database_cleaner_view_cache)
|
||||
(@only || tables_in_db).reject do |table|
|
||||
if ( m = table.match(/([^.]+)$/) )
|
||||
to_reject.include?(m[1])
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# overwritten
|
||||
def migration_storage_names
|
||||
[::DatabaseCleaner::ActiveRecord::Base.migration_table_name]
|
||||
end
|
||||
|
||||
def cache_tables?
|
||||
!!@cache_tables
|
||||
end
|
||||
|
||||
def pre_count?
|
||||
@pre_count == true
|
||||
end
|
||||
|
||||
def reset_ids?
|
||||
@reset_ids != false
|
||||
end
|
||||
end
|
||||
end
|
|
@ -170,7 +170,8 @@ module DatabaseCleaner
|
|||
end
|
||||
|
||||
def require_orm_strategy(orm, strategy)
|
||||
require "database_cleaner/#{orm.to_s}/#{strategy.to_s}"
|
||||
$LOAD_PATH.unshift File.expand_path("#{File.dirname(__FILE__)}/../../adapters/database_cleaner-#{orm}/lib/database_cleaner/#{orm}")
|
||||
require "database_cleaner/#{orm}/#{strategy}"
|
||||
rescue LoadError
|
||||
raise UnknownStrategySpecified, "The '#{strategy}' strategy does not exist for the #{orm} ORM! Available strategies: #{orm_module.available_strategies.join(', ')}"
|
||||
end
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
module DatabaseCleaner
|
||||
module CouchPotato
|
||||
def self.available_strategies
|
||||
%w[truncation]
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
require 'database_cleaner/generic/truncation'
|
||||
|
||||
module DatabaseCleaner
|
||||
module CouchPotato
|
||||
class Truncation
|
||||
include ::DatabaseCleaner::Generic::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."
|
||||
elsif !options.empty?
|
||||
raise ArgumentError, "Unsupported option. You specified #{options.keys.join(',')}."
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
def clean
|
||||
database.recreate!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def database
|
||||
::CouchPotato.couchrest_database
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,21 +0,0 @@
|
|||
require 'database_cleaner/generic/base'
|
||||
module DatabaseCleaner
|
||||
module DataMapper
|
||||
def self.available_strategies
|
||||
%w[truncation transaction]
|
||||
end
|
||||
|
||||
module Base
|
||||
include ::DatabaseCleaner::Generic::Base
|
||||
|
||||
def db=(desired_db)
|
||||
@db = desired_db
|
||||
end
|
||||
|
||||
def db
|
||||
@db ||= :default
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
require 'database_cleaner/data_mapper/base'
|
||||
require 'database_cleaner/generic/transaction'
|
||||
|
||||
module DatabaseCleaner::DataMapper
|
||||
class Transaction
|
||||
include ::DatabaseCleaner::DataMapper::Base
|
||||
include ::DatabaseCleaner::Generic::Transaction
|
||||
|
||||
def start(repository = self.db)
|
||||
::DataMapper.repository(repository) do |r|
|
||||
transaction = DataMapper::Transaction.new(r)
|
||||
transaction.begin
|
||||
r.adapter.push_transaction(transaction)
|
||||
end
|
||||
end
|
||||
|
||||
def clean(repository = self.db)
|
||||
::DataMapper.repository(repository) do |r|
|
||||
adapter = r.adapter
|
||||
while adapter.current_transaction
|
||||
adapter.current_transaction.rollback
|
||||
adapter.pop_transaction
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,172 +0,0 @@
|
|||
require "database_cleaner/generic/truncation"
|
||||
require 'database_cleaner/data_mapper/base'
|
||||
|
||||
module DataMapper
|
||||
module Adapters
|
||||
|
||||
class DataObjectsAdapter
|
||||
|
||||
def storage_names(repository = :default)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def truncate_tables(table_names)
|
||||
table_names.each do |table_name|
|
||||
truncate_table table_name
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class MysqlAdapter < DataObjectsAdapter
|
||||
|
||||
# taken from http://github.com/godfat/dm-mapping/tree/master
|
||||
def storage_names(repository = :default)
|
||||
select 'SHOW TABLES'
|
||||
end
|
||||
|
||||
def truncate_table(table_name)
|
||||
execute("TRUNCATE TABLE #{quote_name(table_name)};")
|
||||
end
|
||||
|
||||
# copied from activerecord
|
||||
def disable_referential_integrity
|
||||
old = select("SELECT @@FOREIGN_KEY_CHECKS;")
|
||||
begin
|
||||
execute("SET FOREIGN_KEY_CHECKS = 0;")
|
||||
yield
|
||||
ensure
|
||||
execute("SET FOREIGN_KEY_CHECKS = ?", *old)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module SqliteAdapterMethods
|
||||
|
||||
# taken from http://github.com/godfat/dm-mapping/tree/master
|
||||
def storage_names(repository = :default)
|
||||
# activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 177
|
||||
sql = <<-SQL
|
||||
SELECT name
|
||||
FROM sqlite_master
|
||||
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
||||
SQL
|
||||
# activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 181
|
||||
select(sql)
|
||||
end
|
||||
|
||||
def truncate_table(table_name)
|
||||
execute("DELETE FROM #{quote_name(table_name)};")
|
||||
if uses_sequence?
|
||||
execute("DELETE FROM sqlite_sequence where name = '#{table_name}';")
|
||||
end
|
||||
end
|
||||
|
||||
# this is a no-op copied from activerecord
|
||||
# i didn't find out if/how this is possible
|
||||
# activerecord also doesn't do more here
|
||||
def disable_referential_integrity
|
||||
yield
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns a boolean indicating if the SQLite database is using the sqlite_sequence table.
|
||||
def uses_sequence?
|
||||
sql = <<-SQL
|
||||
SELECT name FROM sqlite_master
|
||||
WHERE type='table' AND name='sqlite_sequence'
|
||||
SQL
|
||||
select(sql).first
|
||||
end
|
||||
end
|
||||
|
||||
class SqliteAdapter; include SqliteAdapterMethods; end
|
||||
class Sqlite3Adapter; include SqliteAdapterMethods; end
|
||||
|
||||
# FIXME
|
||||
# i don't know if this works
|
||||
# i basically just copied activerecord code to get a rough idea what they do.
|
||||
# i don't have postgres available, so i won't be the one to write this.
|
||||
# maybe codes below gets some postgres/datamapper user going, though.
|
||||
class PostgresAdapter < DataObjectsAdapter
|
||||
|
||||
# taken from http://github.com/godfat/dm-mapping/tree/master
|
||||
def storage_names(repository = :default)
|
||||
sql = <<-SQL
|
||||
SELECT table_name FROM "information_schema"."tables"
|
||||
WHERE table_schema = current_schema() and table_type = 'BASE TABLE'
|
||||
SQL
|
||||
select(sql)
|
||||
end
|
||||
|
||||
def truncate_table(table_name)
|
||||
execute("TRUNCATE TABLE #{quote_name(table_name)} RESTART IDENTITY CASCADE;")
|
||||
end
|
||||
|
||||
# override to use a single statement
|
||||
def truncate_tables(table_names)
|
||||
quoted_names = table_names.collect { |n| quote_name(n) }.join(', ')
|
||||
execute("TRUNCATE TABLE #{quoted_names} RESTART IDENTITY;")
|
||||
end
|
||||
|
||||
# FIXME
|
||||
# copied from activerecord
|
||||
def supports_disable_referential_integrity?
|
||||
version = select("SHOW server_version")[0][0].split('.')
|
||||
(version[0].to_i >= 8 && version[1].to_i >= 1) ? true : false
|
||||
rescue
|
||||
return false
|
||||
end
|
||||
|
||||
# FIXME
|
||||
# copied unchanged from activerecord
|
||||
def disable_referential_integrity(repository = :default)
|
||||
if supports_disable_referential_integrity? then
|
||||
execute(storage_names(repository).collect do |name|
|
||||
"ALTER TABLE #{quote_name(name)} DISABLE TRIGGER ALL"
|
||||
end.join(";"))
|
||||
end
|
||||
yield
|
||||
ensure
|
||||
if supports_disable_referential_integrity? then
|
||||
execute(storage_names(repository).collect do |name|
|
||||
"ALTER TABLE #{quote_name(name)} ENABLE TRIGGER ALL"
|
||||
end.join(";"))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
module DatabaseCleaner
|
||||
module DataMapper
|
||||
class Truncation
|
||||
include ::DatabaseCleaner::DataMapper::Base
|
||||
include ::DatabaseCleaner::Generic::Truncation
|
||||
|
||||
def clean(repository = self.db)
|
||||
adapter = ::DataMapper.repository(repository).adapter
|
||||
adapter.disable_referential_integrity do
|
||||
adapter.truncate_tables(tables_to_truncate(repository))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def tables_to_truncate(repository = self.db)
|
||||
(@only || ::DataMapper.repository(repository).adapter.storage_names(repository)) - @tables_to_exclude
|
||||
end
|
||||
|
||||
# overwritten
|
||||
def migration_storage_names
|
||||
%w[migration_info]
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
module DatabaseCleaner
|
||||
module Mongo
|
||||
def self.available_strategies
|
||||
%w[truncation]
|
||||
end
|
||||
module Base
|
||||
def db=(desired_db)
|
||||
@db = desired_db
|
||||
end
|
||||
|
||||
def db
|
||||
@db || raise("You have not specified a database. (see Mongo::Database)")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,62 +0,0 @@
|
|||
require 'database_cleaner/mongo/base'
|
||||
require 'database_cleaner/generic/truncation'
|
||||
require 'database_cleaner/mongo/truncation_mixin'
|
||||
module DatabaseCleaner
|
||||
module Mongo
|
||||
class Truncation
|
||||
include ::DatabaseCleaner::Generic::Truncation
|
||||
include TruncationMixin
|
||||
include Base
|
||||
private
|
||||
|
||||
def database
|
||||
db
|
||||
end
|
||||
|
||||
def collections_cache
|
||||
@@collections_cache ||= {}
|
||||
end
|
||||
|
||||
def mongoid_collection_names
|
||||
@@mongoid_collection_names ||= Hash.new{|h,k| h[k]=[]}.tap do |names|
|
||||
ObjectSpace.each_object(Class) do |klass|
|
||||
(names[klass.db.name] << klass.collection_name) if valid_collection_name?(klass)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def not_caching(db_name, list)
|
||||
@@not_caching ||= {}
|
||||
|
||||
unless @@not_caching.has_key?(db_name)
|
||||
@@not_caching[db_name] = true
|
||||
|
||||
puts "Not caching collection names for db #{db_name}. Missing these from models: #{list}"
|
||||
end
|
||||
end
|
||||
|
||||
def collections
|
||||
return collections_cache[database.name] if collections_cache.has_key?(database.name)
|
||||
db_collections = database.collections.select { |c| c.name !~ /^system\./ }
|
||||
|
||||
missing_collections = mongoid_collection_names[database.name] - db_collections.map(&:name)
|
||||
|
||||
if missing_collections.empty?
|
||||
collections_cache[database.name] = db_collections
|
||||
else
|
||||
not_caching(database.name, missing_collections)
|
||||
end
|
||||
|
||||
db_collections
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def valid_collection_name?(klass)
|
||||
klass.ancestors.map(&:to_s).include?('Mongoid::Document') &&
|
||||
!klass.embedded &&
|
||||
!klass.collection_name.empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
module DatabaseCleaner
|
||||
module Mongo
|
||||
module TruncationMixin
|
||||
|
||||
def clean
|
||||
if @only
|
||||
collections.each { |c| c.send(truncate_method_name) if @only.include?(c.name) }
|
||||
else
|
||||
collections.each { |c| c.send(truncate_method_name) unless @tables_to_exclude.include?(c.name) }
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def collections
|
||||
database.collections.select { |c| c.name !~ /^system\./ }
|
||||
end
|
||||
|
||||
def truncate_method_name
|
||||
# This constant only exists in the 2.x series.
|
||||
defined?(::Mongo::VERSION) ? :delete_many : :remove
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,20 +0,0 @@
|
|||
require 'database_cleaner/generic/base'
|
||||
module DatabaseCleaner
|
||||
module MongoMapper
|
||||
def self.available_strategies
|
||||
%w[truncation]
|
||||
end
|
||||
|
||||
module Base
|
||||
include ::DatabaseCleaner::Generic::Base
|
||||
|
||||
def db=(desired_db)
|
||||
@db = desired_db
|
||||
end
|
||||
|
||||
def db
|
||||
@db ||= :default
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,19 +0,0 @@
|
|||
require 'database_cleaner/mongo_mapper/base'
|
||||
require 'database_cleaner/generic/truncation'
|
||||
require 'database_cleaner/mongo/truncation_mixin'
|
||||
|
||||
module DatabaseCleaner
|
||||
module MongoMapper
|
||||
class Truncation
|
||||
include ::DatabaseCleaner::MongoMapper::Base
|
||||
include ::DatabaseCleaner::Generic::Truncation
|
||||
include ::DatabaseCleaner::Mongo::TruncationMixin
|
||||
|
||||
private
|
||||
|
||||
def database
|
||||
::MongoMapper.database
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,20 +0,0 @@
|
|||
require 'database_cleaner/generic/base'
|
||||
module DatabaseCleaner
|
||||
module Mongoid
|
||||
def self.available_strategies
|
||||
%w[truncation]
|
||||
end
|
||||
|
||||
module Base
|
||||
include ::DatabaseCleaner::Generic::Base
|
||||
|
||||
def db=(desired_db)
|
||||
@db = desired_db
|
||||
end
|
||||
|
||||
def db
|
||||
@db || :default
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,49 +0,0 @@
|
|||
require 'database_cleaner/mongoid/base'
|
||||
require 'database_cleaner/generic/truncation'
|
||||
require 'database_cleaner/mongo/truncation_mixin'
|
||||
require 'database_cleaner/mongo2/truncation_mixin'
|
||||
require 'database_cleaner/moped/truncation_base'
|
||||
require 'mongoid/version'
|
||||
|
||||
module DatabaseCleaner
|
||||
module Mongoid
|
||||
class Truncation
|
||||
include ::DatabaseCleaner::Mongoid::Base
|
||||
include ::DatabaseCleaner::Generic::Truncation
|
||||
|
||||
if ::Mongoid::VERSION < '3'
|
||||
|
||||
include ::DatabaseCleaner::Mongo::TruncationMixin
|
||||
|
||||
private
|
||||
|
||||
def database
|
||||
::Mongoid.database
|
||||
end
|
||||
|
||||
elsif ::Mongoid::VERSION < '5'
|
||||
|
||||
include ::DatabaseCleaner::Moped::TruncationBase
|
||||
|
||||
private
|
||||
|
||||
def session
|
||||
::Mongoid::VERSION > "5.0.0" ? ::Mongoid.default_client : ::Mongoid.default_session
|
||||
end
|
||||
|
||||
def database
|
||||
if not(@db.nil? or @db == :default)
|
||||
::Mongoid.databases[@db]
|
||||
else
|
||||
::Mongoid.database
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
|
||||
include ::DatabaseCleaner::Mongo2::TruncationMixin
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,39 +0,0 @@
|
|||
require 'database_cleaner/generic/base'
|
||||
|
||||
module DatabaseCleaner
|
||||
module Moped
|
||||
def self.available_strategies
|
||||
%w[truncation]
|
||||
end
|
||||
|
||||
module Base
|
||||
include ::DatabaseCleaner::Generic::Base
|
||||
|
||||
def db=(desired_db)
|
||||
@db = desired_db
|
||||
end
|
||||
|
||||
def db
|
||||
@db ||= :default
|
||||
end
|
||||
|
||||
def host_port=(desired_host)
|
||||
@host = desired_host
|
||||
end
|
||||
|
||||
def host
|
||||
@host ||= '127.0.0.1:27017'
|
||||
end
|
||||
|
||||
def db_version
|
||||
@db_version ||= session.command('buildinfo' => 1)['version']
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def session
|
||||
@session ||= ::Moped::Session.new([host], database: db)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
require 'database_cleaner/moped/truncation_base'
|
||||
|
||||
module DatabaseCleaner
|
||||
module Moped
|
||||
class Truncation
|
||||
include ::DatabaseCleaner::Moped::TruncationBase
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,44 +0,0 @@
|
|||
require 'database_cleaner/moped/base'
|
||||
require 'database_cleaner/generic/truncation'
|
||||
|
||||
module DatabaseCleaner
|
||||
module Moped
|
||||
module TruncationBase
|
||||
include ::DatabaseCleaner::Moped::Base
|
||||
include ::DatabaseCleaner::Generic::Truncation
|
||||
|
||||
def clean
|
||||
if @only
|
||||
collections.each { |c| session[c].find.remove_all if @only.include?(c) }
|
||||
else
|
||||
collections.each { |c| session[c].find.remove_all unless @tables_to_exclude.include?(c) }
|
||||
end
|
||||
wait_for_removals_to_finish
|
||||
true
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def collections
|
||||
if db != :default
|
||||
session.use(db)
|
||||
end
|
||||
|
||||
if db_version.split('.').first.to_i >= 3
|
||||
session.command(listCollections: 1, filter: { 'name' => { '$not' => /.?system\.|\$/ } })['cursor']['firstBatch'].map do |collection|
|
||||
collection['name']
|
||||
end
|
||||
else
|
||||
session['system.namespaces'].find(name: { '$not' => /\.system\.|\$/ }).to_a.map do |collection|
|
||||
_, name = collection['name'].split('.', 2)
|
||||
name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def wait_for_removals_to_finish
|
||||
session.command(getlasterror: 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,62 +0,0 @@
|
|||
require 'database_cleaner/generic/base'
|
||||
module DatabaseCleaner
|
||||
module Neo4j
|
||||
def self.available_strategies
|
||||
%w[transaction truncation deletion]
|
||||
end
|
||||
|
||||
module Base
|
||||
include ::DatabaseCleaner::Generic::Base
|
||||
|
||||
def db=(desired_db)
|
||||
@db = desired_db == :default ? nil : desired_db
|
||||
end
|
||||
|
||||
def db
|
||||
@db ||= nil
|
||||
end
|
||||
|
||||
def start
|
||||
if db_type == :embedded_db and not session.running?
|
||||
session.start
|
||||
else
|
||||
session
|
||||
end
|
||||
end
|
||||
|
||||
def database
|
||||
db && default_db.merge(db) || default_db
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def default_db
|
||||
{:type => default_db_type, :path => default_db_path}
|
||||
end
|
||||
|
||||
def default_db_type
|
||||
:server_db
|
||||
end
|
||||
|
||||
def default_db_path(type = default_db_type)
|
||||
type == :server_db ? 'http://localhost:7475/' : './db/test'
|
||||
end
|
||||
|
||||
def db_type
|
||||
database[:type]
|
||||
end
|
||||
|
||||
def db_path
|
||||
database[:path]
|
||||
end
|
||||
|
||||
def db_params
|
||||
database.reject!{|key, value| [:type, :path].include? key }
|
||||
end
|
||||
|
||||
def session
|
||||
@session ||= ::Neo4j::Session.open(db_type, db_path, db_params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
require 'database_cleaner/neo4j/base'
|
||||
require 'neo4j-core'
|
||||
|
||||
module DatabaseCleaner
|
||||
module Neo4j
|
||||
class Deletion
|
||||
include ::DatabaseCleaner::Neo4j::Base
|
||||
|
||||
def clean
|
||||
::Neo4j::Transaction.run do
|
||||
session._query('MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE n,r')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
require 'database_cleaner/neo4j/base'
|
||||
require 'database_cleaner/generic/transaction'
|
||||
require 'neo4j-core'
|
||||
|
||||
module DatabaseCleaner
|
||||
module Neo4j
|
||||
class Transaction
|
||||
include ::DatabaseCleaner::Generic::Transaction
|
||||
include ::DatabaseCleaner::Neo4j::Base
|
||||
|
||||
attr_accessor :tx
|
||||
|
||||
def start
|
||||
super
|
||||
rollback
|
||||
self.tx = ::Neo4j::Transaction.new
|
||||
end
|
||||
|
||||
def clean
|
||||
rollback
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def rollback
|
||||
if tx
|
||||
tx.failure
|
||||
tx.close
|
||||
end
|
||||
ensure
|
||||
self.tx = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
require 'database_cleaner/neo4j/base'
|
||||
require 'database_cleaner/neo4j/deletion'
|
||||
|
||||
module DatabaseCleaner
|
||||
module Neo4j
|
||||
class Truncation < DatabaseCleaner::Neo4j::Deletion
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,19 +0,0 @@
|
|||
require 'database_cleaner/redis/truncation'
|
||||
|
||||
module DatabaseCleaner
|
||||
module Ohm
|
||||
def self.available_strategies
|
||||
%w(truncation)
|
||||
end
|
||||
|
||||
class Truncation < ::DatabaseCleaner::Redis::Truncation
|
||||
|
||||
private
|
||||
|
||||
def default_redis
|
||||
::Ohm.redis
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,37 +0,0 @@
|
|||
require 'database_cleaner/generic/base'
|
||||
|
||||
module DatabaseCleaner
|
||||
module Redis
|
||||
def self.available_strategies
|
||||
%w{truncation}
|
||||
end
|
||||
|
||||
module Base
|
||||
include ::DatabaseCleaner::Generic::Base
|
||||
|
||||
def db=(desired_db)
|
||||
@db = desired_db
|
||||
end
|
||||
|
||||
def db
|
||||
@db ||= :default
|
||||
end
|
||||
|
||||
alias url db
|
||||
|
||||
private
|
||||
|
||||
def connection
|
||||
@connection ||= begin
|
||||
if url == :default
|
||||
::Redis.new
|
||||
elsif db.is_a?(::Redis) # pass directly the connection
|
||||
db
|
||||
else
|
||||
::Redis.new(:url => url)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,26 +0,0 @@
|
|||
require 'database_cleaner/redis/base'
|
||||
require 'database_cleaner/generic/truncation'
|
||||
|
||||
module DatabaseCleaner
|
||||
module Redis
|
||||
class Truncation
|
||||
include ::DatabaseCleaner::Redis::Base
|
||||
include ::DatabaseCleaner::Generic::Truncation
|
||||
|
||||
def clean
|
||||
if @only
|
||||
@only.each do |term|
|
||||
connection.keys(term).each { |k| connection.del k }
|
||||
end
|
||||
elsif @tables_to_exclude
|
||||
keys_except = []
|
||||
@tables_to_exclude.each { |term| keys_except += connection.keys(term) }
|
||||
connection.keys.each { |k| connection.del(k) unless keys_except.include?(k) }
|
||||
else
|
||||
connection.flushdb
|
||||
end
|
||||
connection.quit unless url == :default
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,22 +0,0 @@
|
|||
require 'database_cleaner/generic/base'
|
||||
module DatabaseCleaner
|
||||
module Sequel
|
||||
def self.available_strategies
|
||||
%w(truncation transaction deletion)
|
||||
end
|
||||
|
||||
module Base
|
||||
include ::DatabaseCleaner::Generic::Base
|
||||
|
||||
def db=(desired_db)
|
||||
@db = desired_db
|
||||
end
|
||||
|
||||
def db
|
||||
return @db if @db && @db != :default
|
||||
raise "As you have more than one active sequel database you have to specify the one to use manually!" if ::Sequel::DATABASES.count > 1
|
||||
::Sequel::DATABASES.first || :default
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,47 +0,0 @@
|
|||
require 'database_cleaner/sequel/base'
|
||||
require 'database_cleaner/generic/truncation'
|
||||
require 'database_cleaner/sequel/truncation'
|
||||
|
||||
module DatabaseCleaner::Sequel
|
||||
class Deletion < Truncation
|
||||
def disable_referential_integrity(tables)
|
||||
case db.database_type
|
||||
when :postgres
|
||||
db.run('SET CONSTRAINTS ALL DEFERRED')
|
||||
tables_to_truncate(db).each do |table|
|
||||
db.run("ALTER TABLE \"#{table}\" DISABLE TRIGGER ALL")
|
||||
end
|
||||
when :mysql
|
||||
old = db.fetch('SELECT @@FOREIGN_KEY_CHECKS').first[:@@FOREIGN_KEY_CHECKS]
|
||||
db.run('SET FOREIGN_KEY_CHECKS = 0')
|
||||
end
|
||||
yield
|
||||
ensure
|
||||
case db.database_type
|
||||
when :postgres
|
||||
tables.each do |table|
|
||||
db.run("ALTER TABLE \"#{table}\" ENABLE TRIGGER ALL")
|
||||
end
|
||||
when :mysql
|
||||
db.run("SET FOREIGN_KEY_CHECKS = #{old}")
|
||||
end
|
||||
end
|
||||
|
||||
def delete_tables(db, tables)
|
||||
tables.each do |table|
|
||||
db[table.to_sym].delete
|
||||
end
|
||||
end
|
||||
|
||||
def clean
|
||||
return unless dirty?
|
||||
|
||||
tables = tables_to_truncate(db)
|
||||
db.transaction do
|
||||
disable_referential_integrity(tables) do
|
||||
delete_tables(db, tables)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,40 +0,0 @@
|
|||
require 'database_cleaner/sequel/base'
|
||||
module DatabaseCleaner
|
||||
module Sequel
|
||||
class Transaction
|
||||
include ::DatabaseCleaner::Sequel::Base
|
||||
|
||||
def self.check_fiber_brokenness
|
||||
if !@checked_fiber_brokenness && Fiber.new { Thread.current }.resume != Thread.current
|
||||
raise RuntimeError, "This ruby engine's Fibers are not compatible with Sequel's connection pool. " +
|
||||
"To work around this, please use DatabaseCleaner.cleaning with a block instead of " +
|
||||
"DatabaseCleaner.start and DatabaseCleaner.clean"
|
||||
end
|
||||
@checked_fiber_brokenness = true
|
||||
end
|
||||
|
||||
def start
|
||||
self.class.check_fiber_brokenness
|
||||
|
||||
@fibers ||= []
|
||||
db = self.db
|
||||
f = Fiber.new do
|
||||
db.transaction(:rollback => :always, :savepoint => true) do
|
||||
Fiber.yield
|
||||
end
|
||||
end
|
||||
f.resume
|
||||
@fibers << f
|
||||
end
|
||||
|
||||
def clean
|
||||
f = @fibers.pop
|
||||
f.resume
|
||||
end
|
||||
|
||||
def cleaning
|
||||
self.db.transaction(:rollback => :always, :savepoint => true) { yield }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,79 +0,0 @@
|
|||
require 'database_cleaner/generic/truncation'
|
||||
require 'database_cleaner/sequel/base'
|
||||
|
||||
module DatabaseCleaner
|
||||
module Sequel
|
||||
class Truncation
|
||||
include ::DatabaseCleaner::Sequel::Base
|
||||
include ::DatabaseCleaner::Generic::Truncation
|
||||
|
||||
def start
|
||||
@last_txid = txid
|
||||
end
|
||||
|
||||
def clean
|
||||
return unless dirty?
|
||||
|
||||
tables = tables_to_truncate(db)
|
||||
|
||||
# Count rows before truncating
|
||||
if pre_count?
|
||||
tables = pre_count_tables(tables)
|
||||
end
|
||||
|
||||
case db.database_type
|
||||
when :postgres
|
||||
# PostgreSQL requires all tables with FKs to be truncates in the same command, or have the CASCADE keyword
|
||||
# appended. Bulk truncation without CASCADE is:
|
||||
# * Safer. Tables outside of tables_to_truncate won't be affected.
|
||||
# * Faster. Less roundtrips to the db.
|
||||
unless tables.empty?
|
||||
tables_sql = tables.map { |t| %("#{t}") }.join(',')
|
||||
db.run "TRUNCATE TABLE #{tables_sql} RESTART IDENTITY;"
|
||||
end
|
||||
else
|
||||
truncate_tables(db, tables)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pre_count_tables tables
|
||||
tables.reject { |table| db[table.to_sym].count == 0 }
|
||||
end
|
||||
|
||||
def truncate_tables(db, tables)
|
||||
tables.each do |table|
|
||||
db[table.to_sym].truncate
|
||||
if db.database_type == :sqlite && db.table_exists?(:sqlite_sequence)
|
||||
db[:sqlite_sequence].where(name: table).delete
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def dirty?
|
||||
@last_txid != txid || @last_txid.nil?
|
||||
end
|
||||
|
||||
def txid
|
||||
case db.database_type
|
||||
when :postgres
|
||||
db.fetch('SELECT txid_snapshot_xmax(txid_current_snapshot()) AS txid').first[:txid]
|
||||
end
|
||||
end
|
||||
|
||||
def tables_to_truncate(db)
|
||||
(@only || db.tables.map(&:to_s)) - @tables_to_exclude
|
||||
end
|
||||
|
||||
# overwritten
|
||||
def migration_storage_names
|
||||
%w(schema_info schema_migrations)
|
||||
end
|
||||
|
||||
def pre_count?
|
||||
@pre_count == true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue