diff --git a/database_cleaner.gemspec b/database_cleaner.gemspec index d42a97e..f0eeed2 100644 --- a/database_cleaner.gemspec +++ b/database_cleaner.gemspec @@ -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' diff --git a/lib/database_cleaner/active_record/base.rb b/lib/database_cleaner/active_record/base.rb deleted file mode 100644 index 434ed55..0000000 --- a/lib/database_cleaner/active_record/base.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/active_record/deletion.rb b/lib/database_cleaner/active_record/deletion.rb deleted file mode 100644 index 3aa0944..0000000 --- a/lib/database_cleaner/active_record/deletion.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/active_record/transaction.rb b/lib/database_cleaner/active_record/transaction.rb deleted file mode 100644 index 540011a..0000000 --- a/lib/database_cleaner/active_record/transaction.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/active_record/truncation.rb b/lib/database_cleaner/active_record/truncation.rb deleted file mode 100644 index b142434..0000000 --- a/lib/database_cleaner/active_record/truncation.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/base.rb b/lib/database_cleaner/base.rb index 17b05a4..5712f54 100644 --- a/lib/database_cleaner/base.rb +++ b/lib/database_cleaner/base.rb @@ -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 diff --git a/lib/database_cleaner/couch_potato/base.rb b/lib/database_cleaner/couch_potato/base.rb deleted file mode 100644 index b53a52a..0000000 --- a/lib/database_cleaner/couch_potato/base.rb +++ /dev/null @@ -1,7 +0,0 @@ -module DatabaseCleaner - module CouchPotato - def self.available_strategies - %w[truncation] - end - end -end diff --git a/lib/database_cleaner/couch_potato/truncation.rb b/lib/database_cleaner/couch_potato/truncation.rb deleted file mode 100644 index 5a163aa..0000000 --- a/lib/database_cleaner/couch_potato/truncation.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/data_mapper/base.rb b/lib/database_cleaner/data_mapper/base.rb deleted file mode 100644 index 64a303c..0000000 --- a/lib/database_cleaner/data_mapper/base.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/data_mapper/transaction.rb b/lib/database_cleaner/data_mapper/transaction.rb deleted file mode 100644 index 31b3287..0000000 --- a/lib/database_cleaner/data_mapper/transaction.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/data_mapper/truncation.rb b/lib/database_cleaner/data_mapper/truncation.rb deleted file mode 100644 index ac9ce2e..0000000 --- a/lib/database_cleaner/data_mapper/truncation.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/mongo/base.rb b/lib/database_cleaner/mongo/base.rb deleted file mode 100644 index 809a6e2..0000000 --- a/lib/database_cleaner/mongo/base.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/mongo/truncation.rb b/lib/database_cleaner/mongo/truncation.rb deleted file mode 100644 index a09d1ff..0000000 --- a/lib/database_cleaner/mongo/truncation.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/mongo/truncation_mixin.rb b/lib/database_cleaner/mongo/truncation_mixin.rb deleted file mode 100644 index 755b3df..0000000 --- a/lib/database_cleaner/mongo/truncation_mixin.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/mongo_mapper/base.rb b/lib/database_cleaner/mongo_mapper/base.rb deleted file mode 100644 index fa8179b..0000000 --- a/lib/database_cleaner/mongo_mapper/base.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/mongo_mapper/truncation.rb b/lib/database_cleaner/mongo_mapper/truncation.rb deleted file mode 100644 index cf233e5..0000000 --- a/lib/database_cleaner/mongo_mapper/truncation.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/mongoid/base.rb b/lib/database_cleaner/mongoid/base.rb deleted file mode 100644 index 417d466..0000000 --- a/lib/database_cleaner/mongoid/base.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/mongoid/truncation.rb b/lib/database_cleaner/mongoid/truncation.rb deleted file mode 100644 index 6a6debb..0000000 --- a/lib/database_cleaner/mongoid/truncation.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/moped/base.rb b/lib/database_cleaner/moped/base.rb deleted file mode 100644 index 51b7a26..0000000 --- a/lib/database_cleaner/moped/base.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/moped/truncation.rb b/lib/database_cleaner/moped/truncation.rb deleted file mode 100644 index 3be3349..0000000 --- a/lib/database_cleaner/moped/truncation.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'database_cleaner/moped/truncation_base' - -module DatabaseCleaner - module Moped - class Truncation - include ::DatabaseCleaner::Moped::TruncationBase - end - end -end diff --git a/lib/database_cleaner/moped/truncation_base.rb b/lib/database_cleaner/moped/truncation_base.rb deleted file mode 100644 index 06ce53c..0000000 --- a/lib/database_cleaner/moped/truncation_base.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/neo4j/base.rb b/lib/database_cleaner/neo4j/base.rb deleted file mode 100644 index 8a794e3..0000000 --- a/lib/database_cleaner/neo4j/base.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/neo4j/deletion.rb b/lib/database_cleaner/neo4j/deletion.rb deleted file mode 100644 index c6166ef..0000000 --- a/lib/database_cleaner/neo4j/deletion.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/neo4j/transaction.rb b/lib/database_cleaner/neo4j/transaction.rb deleted file mode 100644 index b3e0dd9..0000000 --- a/lib/database_cleaner/neo4j/transaction.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/neo4j/truncation.rb b/lib/database_cleaner/neo4j/truncation.rb deleted file mode 100644 index 6d92e1c..0000000 --- a/lib/database_cleaner/neo4j/truncation.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/ohm/truncation.rb b/lib/database_cleaner/ohm/truncation.rb deleted file mode 100644 index a0d0adf..0000000 --- a/lib/database_cleaner/ohm/truncation.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/redis/base.rb b/lib/database_cleaner/redis/base.rb deleted file mode 100644 index 3013240..0000000 --- a/lib/database_cleaner/redis/base.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/redis/truncation.rb b/lib/database_cleaner/redis/truncation.rb deleted file mode 100644 index 32badcc..0000000 --- a/lib/database_cleaner/redis/truncation.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/sequel/base.rb b/lib/database_cleaner/sequel/base.rb deleted file mode 100644 index 1fe091a..0000000 --- a/lib/database_cleaner/sequel/base.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/sequel/deletion.rb b/lib/database_cleaner/sequel/deletion.rb deleted file mode 100644 index 58e240d..0000000 --- a/lib/database_cleaner/sequel/deletion.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/sequel/transaction.rb b/lib/database_cleaner/sequel/transaction.rb deleted file mode 100644 index 03e1a6d..0000000 --- a/lib/database_cleaner/sequel/transaction.rb +++ /dev/null @@ -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 diff --git a/lib/database_cleaner/sequel/truncation.rb b/lib/database_cleaner/sequel/truncation.rb deleted file mode 100644 index 397be89..0000000 --- a/lib/database_cleaner/sequel/truncation.rb +++ /dev/null @@ -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