bitfluent-database_cleaner 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
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