bitfluent-database_cleaner 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. data/History.txt +100 -0
  2. data/LICENSE +20 -0
  3. data/README.textile +118 -0
  4. data/Rakefile +46 -0
  5. data/TODO +0 -0
  6. data/VERSION.yml +5 -0
  7. data/cucumber.yml +1 -0
  8. data/examples/features/example.feature +11 -0
  9. data/examples/features/step_definitions/example_steps.rb +8 -0
  10. data/examples/features/support/env.rb +23 -0
  11. data/examples/lib/activerecord_models.rb +12 -0
  12. data/examples/lib/couchpotato_models.rb +21 -0
  13. data/examples/lib/datamapper_models.rb +16 -0
  14. data/examples/lib/mongoid_models.rb +23 -0
  15. data/examples/lib/mongomapper_models.rb +17 -0
  16. data/features/cleaning.feature +21 -0
  17. data/features/step_definitions/database_cleaner_steps.rb +25 -0
  18. data/features/support/env.rb +7 -0
  19. data/lib/database_cleaner.rb +3 -0
  20. data/lib/database_cleaner/active_record/transaction.rb +26 -0
  21. data/lib/database_cleaner/active_record/truncation.rb +106 -0
  22. data/lib/database_cleaner/configuration.rb +129 -0
  23. data/lib/database_cleaner/couch_potato/truncation.rb +26 -0
  24. data/lib/database_cleaner/cucumber.rb +8 -0
  25. data/lib/database_cleaner/data_mapper/transaction.rb +23 -0
  26. data/lib/database_cleaner/data_mapper/truncation.rb +142 -0
  27. data/lib/database_cleaner/mongo_mapper/truncation.rb +30 -0
  28. data/lib/database_cleaner/mongoid/truncation.rb +23 -0
  29. data/lib/database_cleaner/truncation_base.rb +41 -0
  30. data/spec/database_cleaner/active_record/transaction_spec.rb +65 -0
  31. data/spec/database_cleaner/active_record/truncation_spec.rb +66 -0
  32. data/spec/database_cleaner/configuration_spec.rb +107 -0
  33. data/spec/database_cleaner/couch_potato/truncation_spec.rb +40 -0
  34. data/spec/database_cleaner/mongo_mapper/truncation_spec.rb +81 -0
  35. data/spec/database_cleaner/mongoid/truncation_spec.rb +84 -0
  36. data/spec/spec.opts +6 -0
  37. data/spec/spec_helper.rb +12 -0
  38. metadata +118 -0
@@ -0,0 +1,25 @@
1
+ Given /^I am using (ActiveRecord|DataMapper|MongoMapper|Mongoid|CouchPotato)$/ do |orm|
2
+ @orm = orm
3
+ end
4
+
5
+ Given /^the (.+) cleaning strategy$/ do |strategy|
6
+ @strategy = strategy
7
+ end
8
+
9
+ When "I run my scenarios that rely on a clean database" do
10
+ full_dir ||= File.expand_path(File.dirname(__FILE__) + "/../../examples/")
11
+ Dir.chdir(full_dir) do
12
+ ENV['ORM'] = @orm.downcase
13
+ ENV['STRATEGY'] = @strategy
14
+ @out = `#{"jruby -S " if defined?(JRUBY_VERSION)}cucumber features`
15
+ @status = $?.exitstatus
16
+ end
17
+ end
18
+
19
+ Then "I should see all green" do
20
+ unless @status == 0
21
+ raise "Expected to see all green but we saw FAIL! Output:\n#{@out}"
22
+ end
23
+ end
24
+
25
+
@@ -0,0 +1,7 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
+ require 'database_cleaner'
3
+
4
+ require 'spec/expectations'
5
+
6
+ require 'test/unit/assertions'
7
+
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
2
+ require 'database_cleaner/configuration'
3
+
@@ -0,0 +1,26 @@
1
+ module DatabaseCleaner::ActiveRecord
2
+ class Transaction
3
+
4
+ def start
5
+ if ActiveRecord::Base.connection.respond_to?(:increment_open_transactions)
6
+ ActiveRecord::Base.connection.increment_open_transactions
7
+ else
8
+ ActiveRecord::Base.__send__(:increment_open_transactions)
9
+ end
10
+
11
+ ActiveRecord::Base.connection.begin_db_transaction
12
+ end
13
+
14
+
15
+ def clean
16
+ ActiveRecord::Base.connection.rollback_db_transaction
17
+
18
+ if ActiveRecord::Base.connection.respond_to?(:decrement_open_transactions)
19
+ ActiveRecord::Base.connection.decrement_open_transactions
20
+ else
21
+ ActiveRecord::Base.__send__(:decrement_open_transactions)
22
+ end
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,106 @@
1
+ require 'active_record/base'
2
+ require 'active_record/connection_adapters/abstract_adapter'
3
+ require "database_cleaner/truncation_base"
4
+
5
+ module ActiveRecord
6
+ module ConnectionAdapters
7
+
8
+ class AbstractAdapter
9
+ end
10
+
11
+ class SQLiteAdapter < AbstractAdapter
12
+ end
13
+
14
+ class MysqlAdapter < AbstractAdapter
15
+ def truncate_table(table_name)
16
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
17
+ end
18
+ end
19
+
20
+ class Mysql2Adapter < AbstractAdapter
21
+ def truncate_table(table_name)
22
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
23
+ end
24
+ end
25
+
26
+ class SQLite3Adapter < SQLiteAdapter
27
+ def truncate_table(table_name)
28
+ execute("DELETE FROM #{quote_table_name(table_name)};")
29
+ end
30
+ end
31
+
32
+ class JdbcAdapter < AbstractAdapter
33
+ def truncate_table(table_name)
34
+ begin
35
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
36
+ rescue ActiveRecord::StatementInvalid
37
+ execute("DELETE FROM #{quote_table_name(table_name)};")
38
+ end
39
+ end
40
+ end
41
+
42
+ class PostgreSQLAdapter < AbstractAdapter
43
+
44
+ def self.db_version
45
+ @db_version ||= ActiveRecord::Base.connection.select_values(
46
+ "SELECT CHARACTER_VALUE
47
+ FROM INFORMATION_SCHEMA.SQL_IMPLEMENTATION_INFO
48
+ WHERE IMPLEMENTATION_INFO_NAME = 'DBMS VERSION' ").to_s
49
+ end
50
+
51
+ def self.cascade
52
+ @cascade ||= db_version >= "08.02" ? "CASCADE" : ""
53
+ end
54
+
55
+ def truncate_table(table_name)
56
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)} #{self.class.cascade};")
57
+ end
58
+
59
+ end
60
+
61
+ class SQLServerAdapter < AbstractAdapter
62
+ def truncate_table(table_name)
63
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
64
+ end
65
+ end
66
+
67
+ class OracleEnhancedAdapter < AbstractAdapter
68
+ def truncate_table(table_name)
69
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)}")
70
+ end
71
+ end
72
+
73
+ end
74
+ end
75
+
76
+
77
+ module DatabaseCleaner::ActiveRecord
78
+ class Truncation < ::DatabaseCleaner::TruncationBase
79
+
80
+ def clean
81
+ connection.disable_referential_integrity do
82
+ tables_to_truncate.each do |table_name|
83
+ connection.truncate_table table_name
84
+ end
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def tables_to_truncate
91
+ (@only || connection.tables) - @tables_to_exclude
92
+ end
93
+
94
+ def connection
95
+ ::ActiveRecord::Base.connection
96
+ end
97
+
98
+ # overwritten
99
+ def migration_storage_name
100
+ 'schema_migrations'
101
+ end
102
+
103
+ end
104
+ end
105
+
106
+
@@ -0,0 +1,129 @@
1
+ module DatabaseCleaner
2
+
3
+ class NoStrategySetError < StandardError; end
4
+ class NoORMDetected < StandardError; end
5
+ class UnknownStrategySpecified < ArgumentError; end
6
+
7
+ module ActiveRecord
8
+ def self.available_strategies
9
+ %w[truncation transaction]
10
+ end
11
+ end
12
+
13
+ module DataMapper
14
+ def self.available_strategies
15
+ %w[truncation transaction]
16
+ end
17
+ end
18
+
19
+
20
+ module MongoMapper
21
+ def self.available_strategies
22
+ %w[truncation]
23
+ end
24
+ end
25
+
26
+ module Mongoid
27
+ def self.available_strategies
28
+ %w[truncation]
29
+ end
30
+ end
31
+
32
+ module CouchPotato
33
+ def self.available_strategies
34
+ %w[truncation]
35
+ end
36
+ end
37
+
38
+ class << self
39
+
40
+ def create_strategy(*args)
41
+ strategy, *strategy_args = args
42
+ orm_strategy(strategy).new(*strategy_args)
43
+ end
44
+
45
+ def clean_with(*args)
46
+ strategy = create_strategy(*args)
47
+ strategy.clean
48
+ strategy
49
+ end
50
+
51
+ alias clean_with! clean_with
52
+
53
+ def strategy=(args)
54
+ strategy, *strategy_args = args
55
+ if strategy.is_a?(Symbol)
56
+ @strategy = create_strategy(*args)
57
+ elsif strategy_args.empty?
58
+ @strategy = strategy
59
+ else
60
+ raise ArgumentError, "You must provide a strategy object, or a symbol for a know strategy along with initialization params."
61
+ end
62
+ end
63
+
64
+ def orm=(orm_string)
65
+ @orm = orm_string
66
+ end
67
+
68
+ def start
69
+ strategy.start
70
+ end
71
+
72
+ def clean
73
+ strategy.clean
74
+ end
75
+
76
+ alias clean! clean
77
+
78
+ private
79
+
80
+ def strategy
81
+ return @strategy if @strategy
82
+ raise NoStrategySetError, "Please set a strategy with DatabaseCleaner.strategy=."
83
+ end
84
+
85
+ def orm_strategy(strategy)
86
+ require "database_cleaner/#{orm}/#{strategy}"
87
+ orm_module.const_get(strategy.to_s.capitalize)
88
+ rescue LoadError => e
89
+ raise UnknownStrategySpecified, "The '#{strategy}' strategy does not exist for the #{orm} ORM! Available strategies: #{orm_module.available_strategies.join(', ')}"
90
+ end
91
+
92
+
93
+ def orm
94
+ @orm ||=begin
95
+ if defined? ::DataMapper
96
+ 'data_mapper'
97
+ elsif defined? ::MongoMapper
98
+ 'mongo_mapper'
99
+ elsif defined? ::Mongoid
100
+ 'mongoid'
101
+ elsif defined? ::CouchPotato
102
+ 'couch_potato'
103
+ elsif defined? ::ActiveRecord
104
+ 'active_record'
105
+ else
106
+ raise NoORMDetected, "No known ORM was detected! Is ActiveRecord, DataMapper, MongoMapper, Mongoid, or CouchPotato loaded?"
107
+ end
108
+ end
109
+ end
110
+
111
+
112
+ def orm_module
113
+ case orm
114
+ when 'active_record'
115
+ DatabaseCleaner::ActiveRecord
116
+ when 'data_mapper'
117
+ DatabaseCleaner::DataMapper
118
+ when 'mongo_mapper'
119
+ DatabaseCleaner::MongoMapper
120
+ when 'mongoid'
121
+ DatabaseCleaner::Mongoid
122
+ when 'couch_potato'
123
+ DatabaseCleaner::CouchPotato
124
+ end
125
+ end
126
+
127
+ end
128
+
129
+ end
@@ -0,0 +1,26 @@
1
+ require 'database_cleaner/truncation_base'
2
+
3
+ module DatabaseCleaner
4
+ module CouchPotato
5
+ class Truncation < DatabaseCleaner::TruncationBase
6
+ def initialize(options = {})
7
+ if options.has_key?(:only) || options.has_key?(:except)
8
+ raise ArgumentError, "The :only and :except options are not available for use with CouchPotato/CouchDB."
9
+ elsif !options.empty?
10
+ raise ArgumentError, "Unsupported option. You specified #{options.keys.join(',')}."
11
+ end
12
+ super
13
+ end
14
+
15
+ def clean
16
+ database.recreate!
17
+ end
18
+
19
+ private
20
+
21
+ def database
22
+ ::CouchPotato.couchrest_database
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,8 @@
1
+
2
+ Before do
3
+ DatabaseCleaner.start
4
+ end
5
+
6
+ After do
7
+ DatabaseCleaner.clean
8
+ end
@@ -0,0 +1,23 @@
1
+ module DatabaseCleaner::DataMapper
2
+ class Transaction
3
+
4
+ def start(repo = :default)
5
+ DataMapper.repository(repo) do |r|
6
+ transaction = DataMapper::Transaction.new(r)
7
+ transaction.begin
8
+ r.adapter.push_transaction(transaction)
9
+ end
10
+ end
11
+
12
+ def clean(repo = :default)
13
+ DataMapper.repository(repo) do |r|
14
+ adapter = r.adapter
15
+ while adapter.current_transaction
16
+ adapter.current_transaction.rollback
17
+ adapter.pop_transaction
18
+ end
19
+ end
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,142 @@
1
+ require "database_cleaner/truncation_base"
2
+
3
+ module DataMapper
4
+ module Adapters
5
+
6
+ class DataObjectsAdapter
7
+
8
+ def storage_names(repository = :default)
9
+ raise NotImplementedError
10
+ end
11
+
12
+ end
13
+
14
+ class MysqlAdapter < DataObjectsAdapter
15
+
16
+ # taken from http://github.com/godfat/dm-mapping/tree/master
17
+ def storage_names(repository = :default)
18
+ select 'SHOW TABLES'
19
+ end
20
+
21
+ def truncate_table(table_name)
22
+ execute("TRUNCATE TABLE #{quote_name(table_name)};")
23
+ end
24
+
25
+ # copied from activerecord
26
+ def disable_referential_integrity
27
+ old = select("SELECT @@FOREIGN_KEY_CHECKS;")
28
+ begin
29
+ execute("SET FOREIGN_KEY_CHECKS = 0;")
30
+ yield
31
+ ensure
32
+ execute("SET FOREIGN_KEY_CHECKS = #{old.first};")
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ class Sqlite3Adapter < DataObjectsAdapter
39
+
40
+ # taken from http://github.com/godfat/dm-mapping/tree/master
41
+ def storage_names(repository = :default)
42
+ # activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 177
43
+ sql = <<-SQL.compress_lines
44
+ SELECT name
45
+ FROM sqlite_master
46
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
47
+ SQL
48
+ # activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 181
49
+ select sql
50
+ end
51
+
52
+ def truncate_table(table_name)
53
+ execute("DELETE FROM #{quote_name(table_name)};")
54
+ end
55
+
56
+ # this is a no-op copied from activerecord
57
+ # i didn't find out if/how this is possible
58
+ # activerecord also doesn't do more here
59
+ def disable_referential_integrity
60
+ yield
61
+ end
62
+
63
+ end
64
+
65
+
66
+ # FIXME
67
+ # i don't know if this works
68
+ # i basically just copied activerecord code to get a rough idea what they do.
69
+ # i don't have postgres available, so i won't be the one to write this.
70
+ # maybe codes below gets some postgres/datamapper user going, though.
71
+ class PostgresAdapter < DataObjectsAdapter
72
+
73
+ # taken from http://github.com/godfat/dm-mapping/tree/master
74
+ def storage_names(repository = :default)
75
+ sql = <<-SQL.compress_lines
76
+ SELECT table_name FROM "information_schema"."tables"
77
+ WHERE table_schema = current_schema()
78
+ SQL
79
+ select(sql)
80
+ end
81
+
82
+ def truncate_table(table_name)
83
+ execute("TRUNCATE TABLE #{quote_name(table_name)} CASCADE;")
84
+ end
85
+
86
+ # FIXME
87
+ # copied from activerecord
88
+ def supports_disable_referential_integrity?
89
+ version = select("SHOW server_version")[0][0].split('.')
90
+ (version[0].to_i >= 8 && version[1].to_i >= 1) ? true : false
91
+ rescue
92
+ return false
93
+ end
94
+
95
+ # FIXME
96
+ # copied unchanged from activerecord
97
+ def disable_referential_integrity(repository = :default)
98
+ if supports_disable_referential_integrity? then
99
+ execute(storage_names(repository).collect do |name|
100
+ "ALTER TABLE #{quote_name(name)} DISABLE TRIGGER ALL"
101
+ end.join(";"))
102
+ end
103
+ yield
104
+ ensure
105
+ if supports_disable_referential_integrity? then
106
+ execute(storage_names(repository).collect do |name|
107
+ "ALTER TABLE #{quote_name(name)} ENABLE TRIGGER ALL"
108
+ end.join(";"))
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+ end
116
+
117
+
118
+ module DatabaseCleaner::DataMapper
119
+ class Truncation < ::DatabaseCleaner::TruncationBase
120
+
121
+ def clean(repository = :default)
122
+ adapter = DataMapper.repository(repository).adapter
123
+ adapter.disable_referential_integrity do
124
+ tables_to_truncate.each do |table_name|
125
+ adapter.truncate_table table_name
126
+ end
127
+ end
128
+ end
129
+
130
+ private
131
+
132
+ def tables_to_truncate(repository = :default)
133
+ (@only || DataMapper.repository(repository).adapter.storage_names(repository)) - @tables_to_exclude
134
+ end
135
+
136
+ # overwritten
137
+ def migration_storage_name
138
+ 'migration_info'
139
+ end
140
+
141
+ end
142
+ end