surgical_strike 0.7.4
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.
- data/Gemfile.lock +163 -0
- data/History.txt +210 -0
- data/LICENSE +20 -0
- data/README.textile +229 -0
- data/TODO +3 -0
- data/VERSION.yml +5 -0
- data/cucumber.yml +1 -0
- data/examples/Gemfile +1 -0
- data/examples/Gemfile.lock +1 -0
- data/examples/config/database.yml.example +8 -0
- data/examples/db/sqlite_databases_go_here +0 -0
- data/examples/features/example.feature +11 -0
- data/examples/features/example_multiple_db.feature +23 -0
- data/examples/features/example_multiple_orm.feature +22 -0
- data/examples/features/step_definitions/activerecord_steps.rb +31 -0
- data/examples/features/step_definitions/couchpotato_steps.rb +31 -0
- data/examples/features/step_definitions/datamapper_steps.rb +37 -0
- data/examples/features/step_definitions/mongoid_steps.rb +23 -0
- data/examples/features/step_definitions/mongomapper_steps.rb +31 -0
- data/examples/features/step_definitions/translation_steps.rb +55 -0
- data/examples/features/support/env.rb +62 -0
- data/examples/lib/activerecord_models.rb +41 -0
- data/examples/lib/couchpotato_models.rb +61 -0
- data/examples/lib/datamapper_models.rb +50 -0
- data/examples/lib/mongoid_models.rb +49 -0
- data/examples/lib/mongomapper_models.rb +51 -0
- data/features/cleaning.feature +22 -0
- data/features/cleaning_default_strategy.feature +19 -0
- data/features/cleaning_multiple_dbs.feature +21 -0
- data/features/cleaning_multiple_orms.feature +29 -0
- data/features/step_definitions/database_cleaner_steps.rb +32 -0
- data/features/support/env.rb +7 -0
- data/features/support/feature_runner.rb +39 -0
- data/lib/database_cleaner/active_record/base.rb +53 -0
- data/lib/database_cleaner/active_record/deletion.rb +67 -0
- data/lib/database_cleaner/active_record/surgicalstrike.rb +65 -0
- data/lib/database_cleaner/active_record/transaction.rb +28 -0
- data/lib/database_cleaner/active_record/truncation.rb +152 -0
- data/lib/database_cleaner/base.rb +138 -0
- data/lib/database_cleaner/configuration.rb +94 -0
- data/lib/database_cleaner/couch_potato/base.rb +7 -0
- data/lib/database_cleaner/couch_potato/truncation.rb +28 -0
- data/lib/database_cleaner/cucumber.rb +11 -0
- data/lib/database_cleaner/data_mapper/base.rb +21 -0
- data/lib/database_cleaner/data_mapper/transaction.rb +26 -0
- data/lib/database_cleaner/data_mapper/truncation.rb +175 -0
- data/lib/database_cleaner/generic/base.rb +12 -0
- data/lib/database_cleaner/generic/surgicalstrike.rb +47 -0
- data/lib/database_cleaner/generic/truncation.rb +37 -0
- data/lib/database_cleaner/mongo/truncation.rb +22 -0
- data/lib/database_cleaner/mongo_mapper/base.rb +20 -0
- data/lib/database_cleaner/mongo_mapper/truncation.rb +19 -0
- data/lib/database_cleaner/mongoid/base.rb +20 -0
- data/lib/database_cleaner/mongoid/truncation.rb +20 -0
- data/lib/database_cleaner/null_strategy.rb +15 -0
- data/lib/database_cleaner/sequel/base.rb +22 -0
- data/lib/database_cleaner/sequel/transaction.rb +25 -0
- data/lib/database_cleaner/sequel/truncation.rb +50 -0
- data/lib/surgical_strike.rb +3 -0
- data/spec/database_cleaner/active_record/base_spec.rb +144 -0
- data/spec/database_cleaner/active_record/transaction_spec.rb +77 -0
- data/spec/database_cleaner/active_record/truncation_spec.rb +76 -0
- data/spec/database_cleaner/base_spec.rb +493 -0
- data/spec/database_cleaner/configuration_spec.rb +294 -0
- data/spec/database_cleaner/couch_potato/truncation_spec.rb +41 -0
- data/spec/database_cleaner/data_mapper/base_spec.rb +30 -0
- data/spec/database_cleaner/data_mapper/transaction_spec.rb +23 -0
- data/spec/database_cleaner/data_mapper/truncation_spec.rb +11 -0
- data/spec/database_cleaner/generic/base_spec.rb +22 -0
- data/spec/database_cleaner/generic/truncation_spec.rb +78 -0
- data/spec/database_cleaner/mongo_mapper/base_spec.rb +33 -0
- data/spec/database_cleaner/mongo_mapper/mongo_examples.rb +8 -0
- data/spec/database_cleaner/mongo_mapper/truncation_spec.rb +74 -0
- data/spec/database_cleaner/sequel/base_spec.rb +32 -0
- data/spec/database_cleaner/sequel/transaction_spec.rb +21 -0
- data/spec/database_cleaner/sequel/truncation_spec.rb +13 -0
- data/spec/database_cleaner/shared_strategy_spec.rb +13 -0
- data/spec/rcov.opts +1 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +19 -0
- metadata +162 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'database_cleaner/generic/base'
|
2
|
+
module DatabaseCleaner
|
3
|
+
module DataMapper
|
4
|
+
def self.available_strategies
|
5
|
+
%w[truncation transaction]
|
6
|
+
end
|
7
|
+
|
8
|
+
module Base
|
9
|
+
include ::DatabaseCleaner::Generic::Base
|
10
|
+
|
11
|
+
def db=(desired_db)
|
12
|
+
@db = desired_db
|
13
|
+
end
|
14
|
+
|
15
|
+
def db
|
16
|
+
@db || :default
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'database_cleaner/data_mapper/base'
|
2
|
+
|
3
|
+
module DatabaseCleaner::DataMapper
|
4
|
+
class Transaction
|
5
|
+
include ::DatabaseCleaner::DataMapper::Base
|
6
|
+
|
7
|
+
def start(repository = self.db)
|
8
|
+
::DataMapper.repository(repository) do |r|
|
9
|
+
transaction = DataMapper::Transaction.new(r)
|
10
|
+
transaction.begin
|
11
|
+
r.adapter.push_transaction(transaction)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def clean(repository = self.db)
|
16
|
+
::DataMapper.repository(repository) do |r|
|
17
|
+
adapter = r.adapter
|
18
|
+
while adapter.current_transaction
|
19
|
+
adapter.current_transaction.rollback
|
20
|
+
adapter.pop_transaction
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,175 @@
|
|
1
|
+
require "database_cleaner/generic/truncation"
|
2
|
+
require 'database_cleaner/data_mapper/base'
|
3
|
+
|
4
|
+
module DataMapper
|
5
|
+
module Adapters
|
6
|
+
|
7
|
+
class DataObjectsAdapter
|
8
|
+
|
9
|
+
def storage_names(repository = :default)
|
10
|
+
raise NotImplementedError
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
class MysqlAdapter < DataObjectsAdapter
|
16
|
+
|
17
|
+
# taken from http://github.com/godfat/dm-mapping/tree/master
|
18
|
+
def storage_names(repository = :default)
|
19
|
+
select 'SHOW TABLES'
|
20
|
+
end
|
21
|
+
|
22
|
+
def truncate_table(table_name)
|
23
|
+
execute("TRUNCATE TABLE #{quote_name(table_name)};")
|
24
|
+
end
|
25
|
+
|
26
|
+
# copied from activerecord
|
27
|
+
def disable_referential_integrity
|
28
|
+
old = select("SELECT @@FOREIGN_KEY_CHECKS;")
|
29
|
+
begin
|
30
|
+
execute("SET FOREIGN_KEY_CHECKS = 0;")
|
31
|
+
yield
|
32
|
+
ensure
|
33
|
+
execute("SET FOREIGN_KEY_CHECKS = ?", *old)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
class Sqlite3Adapter < DataObjectsAdapter
|
41
|
+
|
42
|
+
# taken from http://github.com/godfat/dm-mapping/tree/master
|
43
|
+
def storage_names(repository = :default)
|
44
|
+
# activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 177
|
45
|
+
sql = <<-SQL
|
46
|
+
SELECT name
|
47
|
+
FROM sqlite_master
|
48
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
49
|
+
SQL
|
50
|
+
# activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 181
|
51
|
+
select(sql)
|
52
|
+
end
|
53
|
+
|
54
|
+
def truncate_table(table_name)
|
55
|
+
execute("DELETE FROM #{quote_name(table_name)};")
|
56
|
+
execute("DELETE FROM sqlite_sequence where name = '#{table_name}';")
|
57
|
+
end
|
58
|
+
|
59
|
+
# this is a no-op copied from activerecord
|
60
|
+
# i didn't find out if/how this is possible
|
61
|
+
# activerecord also doesn't do more here
|
62
|
+
def disable_referential_integrity
|
63
|
+
yield
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
class SqliteAdapter < DataObjectsAdapter
|
69
|
+
# taken from http://github.com/godfat/dm-mapping/tree/master
|
70
|
+
def storage_names(repository = :default)
|
71
|
+
# activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 177
|
72
|
+
sql = <<-SQL
|
73
|
+
SELECT name
|
74
|
+
FROM sqlite_master
|
75
|
+
WHERE type = 'table' AND NOT name = 'sqlite_sequence'
|
76
|
+
SQL
|
77
|
+
# activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 181
|
78
|
+
select(sql)
|
79
|
+
end
|
80
|
+
|
81
|
+
def truncate_table(table_name)
|
82
|
+
execute("DELETE FROM #{quote_name(table_name)};")
|
83
|
+
execute("DELETE FROM sqlite_sequence where name = '#{table_name}';")
|
84
|
+
end
|
85
|
+
|
86
|
+
# this is a no-op copied from activerecord
|
87
|
+
# i didn't find out if/how this is possible
|
88
|
+
# activerecord also doesn't do more here
|
89
|
+
def disable_referential_integrity
|
90
|
+
yield
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
# FIXME
|
96
|
+
# i don't know if this works
|
97
|
+
# i basically just copied activerecord code to get a rough idea what they do.
|
98
|
+
# i don't have postgres available, so i won't be the one to write this.
|
99
|
+
# maybe codes below gets some postgres/datamapper user going, though.
|
100
|
+
class PostgresAdapter < DataObjectsAdapter
|
101
|
+
|
102
|
+
# taken from http://github.com/godfat/dm-mapping/tree/master
|
103
|
+
def storage_names(repository = :default)
|
104
|
+
sql = <<-SQL
|
105
|
+
SELECT table_name FROM "information_schema"."tables"
|
106
|
+
WHERE table_schema = current_schema() and table_type = 'BASE TABLE'
|
107
|
+
SQL
|
108
|
+
select(sql)
|
109
|
+
end
|
110
|
+
|
111
|
+
def truncate_table(table_name)
|
112
|
+
execute("TRUNCATE TABLE #{quote_name(table_name)} RESTART IDENTITY CASCADE;")
|
113
|
+
end
|
114
|
+
|
115
|
+
# FIXME
|
116
|
+
# copied from activerecord
|
117
|
+
def supports_disable_referential_integrity?
|
118
|
+
version = select("SHOW server_version")[0][0].split('.')
|
119
|
+
(version[0].to_i >= 8 && version[1].to_i >= 1) ? true : false
|
120
|
+
rescue
|
121
|
+
return false
|
122
|
+
end
|
123
|
+
|
124
|
+
# FIXME
|
125
|
+
# copied unchanged from activerecord
|
126
|
+
def disable_referential_integrity(repository = :default)
|
127
|
+
if supports_disable_referential_integrity? then
|
128
|
+
execute(storage_names(repository).collect do |name|
|
129
|
+
"ALTER TABLE #{quote_name(name)} DISABLE TRIGGER ALL"
|
130
|
+
end.join(";"))
|
131
|
+
end
|
132
|
+
yield
|
133
|
+
ensure
|
134
|
+
if supports_disable_referential_integrity? then
|
135
|
+
execute(storage_names(repository).collect do |name|
|
136
|
+
"ALTER TABLE #{quote_name(name)} ENABLE TRIGGER ALL"
|
137
|
+
end.join(";"))
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
|
147
|
+
module DatabaseCleaner
|
148
|
+
module DataMapper
|
149
|
+
class Truncation
|
150
|
+
include ::DatabaseCleaner::DataMapper::Base
|
151
|
+
include ::DatabaseCleaner::Generic::Truncation
|
152
|
+
|
153
|
+
def clean(repository = self.db)
|
154
|
+
adapter = ::DataMapper.repository(repository).adapter
|
155
|
+
adapter.disable_referential_integrity do
|
156
|
+
tables_to_truncate(repository).each do |table_name|
|
157
|
+
adapter.truncate_table table_name
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
|
164
|
+
def tables_to_truncate(repository = self.db)
|
165
|
+
(@only || ::DataMapper.repository(repository).adapter.storage_names(repository)) - @tables_to_exclude
|
166
|
+
end
|
167
|
+
|
168
|
+
# overwritten
|
169
|
+
def migration_storage_name
|
170
|
+
'migration_info'
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "database_cleaner"
|
2
|
+
|
3
|
+
module DatabaseCleaner
|
4
|
+
module Generic
|
5
|
+
module Dataholder
|
6
|
+
def checktarget(target)
|
7
|
+
if ($strike_targets.include?(target.class.table_name.upcase))
|
8
|
+
$vips.push(target)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
module Surgicalstrike
|
13
|
+
def initialize(opts={})
|
14
|
+
if !opts.empty? && !(opts.keys - [:only, :except]).empty?
|
15
|
+
raise ArgumentError, "The only valid options are :only and :except. You specified #{opts.keys.join(',')}."
|
16
|
+
end
|
17
|
+
if opts.has_key?(:only) && opts.has_key?(:except)
|
18
|
+
raise ArgumentError, "You may only specify either :only or :except. Doing both doesn't really make sense does it?"
|
19
|
+
end
|
20
|
+
|
21
|
+
@only = opts[:only]
|
22
|
+
@tables_to_exclude = (opts[:except] || []).dup
|
23
|
+
@tables_to_exclude << migration_storage_name if migration_storage_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def start
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
def clean
|
31
|
+
raise NotImplementedError
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def tables_to_strike(connection)
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
# overwrite in subclasses
|
41
|
+
# default implementation given because migration storage need not be present
|
42
|
+
def migration_storage_name
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module DatabaseCleaner
|
2
|
+
module Generic
|
3
|
+
module Truncation
|
4
|
+
def initialize(opts={})
|
5
|
+
if !opts.empty? && !(opts.keys - [:only, :except]).empty?
|
6
|
+
raise ArgumentError, "The only valid options are :only and :except. You specified #{opts.keys.join(',')}."
|
7
|
+
end
|
8
|
+
if opts.has_key?(:only) && opts.has_key?(:except)
|
9
|
+
raise ArgumentError, "You may only specify either :only or :except. Doing both doesn't really make sense does it?"
|
10
|
+
end
|
11
|
+
|
12
|
+
@only = opts[:only]
|
13
|
+
@tables_to_exclude = (opts[:except] || []).dup
|
14
|
+
@tables_to_exclude << migration_storage_name if migration_storage_name
|
15
|
+
end
|
16
|
+
|
17
|
+
def start
|
18
|
+
#included for compatability reasons, do nothing if you don't need to
|
19
|
+
end
|
20
|
+
|
21
|
+
def clean
|
22
|
+
raise NotImplementedError
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def tables_to_truncate
|
27
|
+
raise NotImplementedError
|
28
|
+
end
|
29
|
+
|
30
|
+
# overwrite in subclasses
|
31
|
+
# default implementation given because migration storage need not be present
|
32
|
+
def migration_storage_name
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module DatabaseCleaner
|
2
|
+
module Mongo
|
3
|
+
module Truncation
|
4
|
+
|
5
|
+
def clean
|
6
|
+
if @only
|
7
|
+
collections.each { |c| c.remove if @only.include?(c.name) }
|
8
|
+
else
|
9
|
+
collections.each { |c| c.remove unless @tables_to_exclude.include?(c.name) }
|
10
|
+
end
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def collections
|
17
|
+
database.collections.select { |c| c.name !~ /^system\./ }
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'database_cleaner/generic/base'
|
2
|
+
module DatabaseCleaner
|
3
|
+
module MongoMapper
|
4
|
+
def self.available_strategies
|
5
|
+
%w[truncation]
|
6
|
+
end
|
7
|
+
|
8
|
+
module Base
|
9
|
+
include ::DatabaseCleaner::Generic::Base
|
10
|
+
|
11
|
+
def db=(desired_db)
|
12
|
+
@db = desired_db
|
13
|
+
end
|
14
|
+
|
15
|
+
def db
|
16
|
+
@db || :default
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'database_cleaner/mongo_mapper/base'
|
2
|
+
require 'database_cleaner/generic/truncation'
|
3
|
+
require 'database_cleaner/mongo/truncation'
|
4
|
+
|
5
|
+
module DatabaseCleaner
|
6
|
+
module MongoMapper
|
7
|
+
class Truncation
|
8
|
+
include ::DatabaseCleaner::MongoMapper::Base
|
9
|
+
include ::DatabaseCleaner::Generic::Truncation
|
10
|
+
include ::DatabaseCleaner::Mongo::Truncation
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def database
|
15
|
+
::MongoMapper.database
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'database_cleaner/generic/base'
|
2
|
+
module DatabaseCleaner
|
3
|
+
module Mongoid
|
4
|
+
def self.available_strategies
|
5
|
+
%w[truncation]
|
6
|
+
end
|
7
|
+
|
8
|
+
module Base
|
9
|
+
include ::DatabaseCleaner::Generic::Base
|
10
|
+
|
11
|
+
def db=(desired_db)
|
12
|
+
@db = desired_db
|
13
|
+
end
|
14
|
+
|
15
|
+
def db
|
16
|
+
@db || :default
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'database_cleaner/mongoid/base'
|
2
|
+
require 'database_cleaner/generic/truncation'
|
3
|
+
require 'database_cleaner/mongo/truncation'
|
4
|
+
|
5
|
+
module DatabaseCleaner
|
6
|
+
module Mongoid
|
7
|
+
class Truncation
|
8
|
+
include ::DatabaseCleaner::Mongoid::Base
|
9
|
+
include ::DatabaseCleaner::Generic::Truncation
|
10
|
+
include ::DatabaseCleaner::Mongo::Truncation
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def database
|
15
|
+
::Mongoid.database
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'database_cleaner/generic/base'
|
2
|
+
module DatabaseCleaner
|
3
|
+
module Sequel
|
4
|
+
def self.available_strategies
|
5
|
+
%w[truncation transaction]
|
6
|
+
end
|
7
|
+
|
8
|
+
module Base
|
9
|
+
include ::DatabaseCleaner::Generic::Base
|
10
|
+
|
11
|
+
def db=(desired_db)
|
12
|
+
@db = desired_db
|
13
|
+
end
|
14
|
+
|
15
|
+
def db
|
16
|
+
return @db if @db && @db != :default
|
17
|
+
raise "As you have more than one active sequel database you have to specify the one to use manually!" if ::Sequel::DATABASES.count > 1
|
18
|
+
::Sequel::DATABASES.first || :default
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'database_cleaner/sequel/base'
|
2
|
+
module DatabaseCleaner
|
3
|
+
module Sequel
|
4
|
+
class Transaction
|
5
|
+
include ::DatabaseCleaner::Sequel::Base
|
6
|
+
|
7
|
+
def start
|
8
|
+
@fibers||= []
|
9
|
+
db= self.db
|
10
|
+
f= Fiber.new do
|
11
|
+
db.transaction(:rollback => :always, :savepoint => true) do
|
12
|
+
Fiber.yield
|
13
|
+
end
|
14
|
+
end
|
15
|
+
f.resume
|
16
|
+
@fibers<< f
|
17
|
+
end
|
18
|
+
|
19
|
+
def clean
|
20
|
+
f= @fibers.pop
|
21
|
+
f.resume
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "database_cleaner/generic/truncation"
|
2
|
+
require 'database_cleaner/sequel/base'
|
3
|
+
|
4
|
+
module DatabaseCleaner
|
5
|
+
module Sequel
|
6
|
+
class Truncation
|
7
|
+
include ::DatabaseCleaner::Sequel::Base
|
8
|
+
include ::DatabaseCleaner::Generic::Truncation
|
9
|
+
|
10
|
+
def clean
|
11
|
+
case db_type= db.url.sub(/:.+/,'').to_sym
|
12
|
+
when :postgres
|
13
|
+
# PostgreSQL requires all tables with FKs to be truncates in the same command, or have the CASCADE keyword
|
14
|
+
# appended. Bulk truncation without CASCADE is:
|
15
|
+
# * Safer. Tables outside of tables_to_truncate won't be affected.
|
16
|
+
# * Faster. Less roundtrips to the db.
|
17
|
+
unless (tables= tables_to_truncate(db)).empty?
|
18
|
+
all_tables= tables.map{|t| %["#{t}"]}.join ','
|
19
|
+
db.run "TRUNCATE TABLE #{all_tables};"
|
20
|
+
end
|
21
|
+
else
|
22
|
+
# Truncate each table normally
|
23
|
+
each_table do |db, table|
|
24
|
+
db[table].truncate
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def each_table
|
30
|
+
tables_to_truncate(db).each do |table|
|
31
|
+
yield db, table
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def tables_to_truncate(db)
|
38
|
+
(@only || db.tables) - @tables_to_exclude
|
39
|
+
end
|
40
|
+
|
41
|
+
# overwritten
|
42
|
+
def migration_storage_name
|
43
|
+
:schema_info
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_record'
|
3
|
+
require 'database_cleaner/active_record/base'
|
4
|
+
require 'database_cleaner/shared_strategy_spec'
|
5
|
+
|
6
|
+
module DatabaseCleaner
|
7
|
+
describe ActiveRecord do
|
8
|
+
it { should respond_to(:available_strategies) }
|
9
|
+
|
10
|
+
describe "config_file_location" do
|
11
|
+
subject { ActiveRecord.config_file_location }
|
12
|
+
|
13
|
+
it "should default to DatabaseCleaner.root / config / database.yml" do
|
14
|
+
ActiveRecord.config_file_location=nil
|
15
|
+
DatabaseCleaner.should_receive(:app_root).and_return("/path/to")
|
16
|
+
subject.should == '/path/to/config/database.yml'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
module ActiveRecord
|
23
|
+
class ExampleStrategy
|
24
|
+
include ::DatabaseCleaner::ActiveRecord::Base
|
25
|
+
end
|
26
|
+
|
27
|
+
describe ExampleStrategy do
|
28
|
+
let :config_location do
|
29
|
+
'/path/to/config/database.yml'
|
30
|
+
end
|
31
|
+
|
32
|
+
before { ::DatabaseCleaner::ActiveRecord.stub(:config_file_location).and_return(config_location) }
|
33
|
+
|
34
|
+
it_should_behave_like "a generic strategy"
|
35
|
+
|
36
|
+
describe "db" do
|
37
|
+
|
38
|
+
it "should store my desired db" do
|
39
|
+
subject.stub(:load_config)
|
40
|
+
|
41
|
+
subject.db = :my_db
|
42
|
+
subject.db.should == :my_db
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should default to :default" do
|
46
|
+
subject.db.should == :default
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should load_config when I set db" do
|
50
|
+
subject.should_receive(:load_config)
|
51
|
+
subject.db = :my_db
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "load_config" do
|
56
|
+
|
57
|
+
before do
|
58
|
+
subject.db = :my_db
|
59
|
+
yaml = <<-Y
|
60
|
+
my_db:
|
61
|
+
database: <%= "ONE".downcase %>
|
62
|
+
Y
|
63
|
+
File.stub(:file?).with(config_location).and_return(true)
|
64
|
+
IO.stub(:read).with(config_location).and_return(yaml)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should parse the config" do
|
68
|
+
YAML.should_receive(:load).and_return( {:nil => nil} )
|
69
|
+
subject.load_config
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should process erb in the config" do
|
73
|
+
transformed = <<-Y
|
74
|
+
my_db:
|
75
|
+
database: one
|
76
|
+
Y
|
77
|
+
YAML.should_receive(:load).with(transformed).and_return({ "my_db" => {"database" => "one"} })
|
78
|
+
subject.load_config
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should store the relevant config in connection_hash" do
|
82
|
+
subject.load_config
|
83
|
+
subject.connection_hash.should == {"database" => "one"}
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should skip config if config file is not available" do
|
87
|
+
File.should_receive(:file?).with(config_location).and_return(false)
|
88
|
+
subject.load_config
|
89
|
+
subject.connection_hash.should be_blank
|
90
|
+
end
|
91
|
+
|
92
|
+
it "skips the file when the db is set to :default" do
|
93
|
+
# to avoid https://github.com/bmabey/database_cleaner/issues/72
|
94
|
+
subject.db = :default
|
95
|
+
YAML.should_not_receive(:load)
|
96
|
+
subject.load_config
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "connection_hash" do
|
102
|
+
it "should store connection_hash" do
|
103
|
+
subject.connection_hash = { :key => "value" }
|
104
|
+
subject.connection_hash.should == { :key => "value" }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "create_connection_klass" do
|
109
|
+
it "should return a class" do
|
110
|
+
subject.create_connection_klass.should be_a(Class)
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should return a class extending ::ActiveRecord::Base" do
|
114
|
+
subject.create_connection_klass.ancestors.should include(::ActiveRecord::Base)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "connection_klass" do
|
119
|
+
it { expect{ subject.connection_klass }.to_not raise_error }
|
120
|
+
it "should default to ActiveRecord::Base" do
|
121
|
+
subject.connection_klass.should == ::ActiveRecord::Base
|
122
|
+
end
|
123
|
+
|
124
|
+
context "when connection_hash is set" do
|
125
|
+
let(:hash) { mock("hash") }
|
126
|
+
before { subject.stub(:connection_hash).and_return(hash) }
|
127
|
+
|
128
|
+
it "should create connection_klass if it doesnt exist if connection_hash is set" do
|
129
|
+
subject.should_receive(:create_connection_klass).and_return(mock('class').as_null_object)
|
130
|
+
subject.connection_klass
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should configure the class from create_connection_klass if connection_hash is set" do
|
134
|
+
klass = mock('klass')
|
135
|
+
klass.should_receive(:establish_connection).with(hash)
|
136
|
+
|
137
|
+
subject.should_receive(:create_connection_klass).and_return(klass)
|
138
|
+
subject.connection_klass
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|