bmabey-database_cleaner 0.1.3 → 0.2.0
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/History.txt +9 -2
- data/Rakefile +1 -1
- data/VERSION.yml +2 -2
- data/examples/features/support/env.rb +17 -10
- data/examples/lib/datamapper.rb +16 -0
- data/features/cleaning.feature +2 -0
- data/lib/database_cleaner/active_record/truncation.rb +10 -23
- data/lib/database_cleaner/configuration.rb +51 -10
- data/lib/database_cleaner/data_mapper/transaction.rb +18 -1
- data/lib/database_cleaner/data_mapper/truncation.rb +142 -0
- data/lib/database_cleaner/truncation_base.rb +40 -0
- data/spec/database_cleaner/active_record/truncation_spec.rb +2 -2
- metadata +6 -2
data/History.txt
CHANGED
@@ -3,7 +3,14 @@
|
|
3
3
|
=== New features
|
4
4
|
=== Bufixes
|
5
5
|
|
6
|
-
== 0.
|
6
|
+
== 0.2.0 2009-05-08 - The Datamapper Release
|
7
|
+
|
8
|
+
=== New features
|
9
|
+
* DataMapper strategies (Martin Gamsjaeger)
|
10
|
+
* Transaction
|
11
|
+
* Truncation - working SQLite3, MySQL adapters. Experimental Postgres adapter (not tested).
|
12
|
+
|
13
|
+
== 0.1.3 2009-04-30
|
7
14
|
|
8
15
|
=== New features
|
9
16
|
* PostgresSQLAdapter for AR to support the truncation strategy. (Alberto Perdomo)
|
@@ -14,7 +21,7 @@
|
|
14
21
|
=== New features
|
15
22
|
* JDBC Adapter to enable AR truncation strategy to work. (Kamal Fariz Mahyuddin)
|
16
23
|
|
17
|
-
== 0.1.1 2009-03-04 - Initial Release (
|
24
|
+
== 0.1.1 2009-03-04 - Initial Release (Ben Mabey)
|
18
25
|
* Basic infrastructure
|
19
26
|
* Features, RSpec code examples
|
20
27
|
* ActiveRecord strategies
|
data/Rakefile
CHANGED
data/VERSION.yml
CHANGED
@@ -1,16 +1,23 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'spec/expectations'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
rescue LoadError
|
7
|
-
raise "I don't have the setup for the '#{ENV['ORM']}' ORM!"
|
8
|
-
end
|
9
|
-
|
10
|
-
$:.unshift(File.dirname(__FILE__) + '/../../../lib')
|
11
|
-
require 'database_cleaner'
|
12
|
-
require 'database_cleaner/cucumber'
|
4
|
+
orm = ENV['ORM']
|
5
|
+
strategy = ENV['STRATEGY']
|
13
6
|
|
14
|
-
|
7
|
+
if orm && strategy
|
8
|
+
|
9
|
+
begin
|
10
|
+
require "#{File.dirname(__FILE__)}/../../lib/#{orm}"
|
11
|
+
rescue LoadError
|
12
|
+
raise "You don't have the #{orm} ORM installed"
|
13
|
+
end
|
15
14
|
|
15
|
+
$:.unshift(File.dirname(__FILE__) + '/../../../lib')
|
16
|
+
require 'database_cleaner'
|
17
|
+
require 'database_cleaner/cucumber'
|
16
18
|
|
19
|
+
DatabaseCleaner.strategy = strategy.to_sym
|
20
|
+
|
21
|
+
else
|
22
|
+
raise "Run 'ORM=activerecord|datamapper STRATEGY=transaction|truncation cucumber examples/features'"
|
23
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "dm-core"
|
2
|
+
|
3
|
+
# only to please activerecord API used in database_cleaner/examples/features/step_definitions
|
4
|
+
# yes, i know that's lazy ...
|
5
|
+
require "dm-validations"
|
6
|
+
require "dm-aggregates"
|
7
|
+
|
8
|
+
DataMapper.setup(:default, "sqlite3::memory:")
|
9
|
+
|
10
|
+
class Widget
|
11
|
+
include DataMapper::Resource
|
12
|
+
property :id, Serial
|
13
|
+
property :name, String
|
14
|
+
end
|
15
|
+
|
16
|
+
Widget.auto_migrate!
|
data/features/cleaning.feature
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
require "database_cleaner/truncation_base"
|
1
2
|
|
2
3
|
module ActiveRecord
|
3
4
|
module ConnectionAdapters
|
5
|
+
|
4
6
|
class MysqlAdapter
|
5
7
|
def truncate_table(table_name)
|
6
8
|
execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
|
@@ -25,31 +27,12 @@ module ActiveRecord
|
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
28
|
-
|
29
30
|
end
|
30
|
-
|
31
31
|
end
|
32
32
|
|
33
33
|
|
34
34
|
module DatabaseCleaner::ActiveRecord
|
35
|
-
class Truncation
|
36
|
-
|
37
|
-
def initialize(options={})
|
38
|
-
if !options.empty? && !(options.keys - [:only, :except]).empty?
|
39
|
-
raise ArgumentError, "The only valid options are :only and :except. You specified #{options.keys.join(',')}."
|
40
|
-
end
|
41
|
-
if options.has_key?(:only) && options.has_key?(:except)
|
42
|
-
raise ArgumentError, "You may only specify either :only or :either. Doing both doesn't really make sense does it?"
|
43
|
-
end
|
44
|
-
|
45
|
-
@only = options[:only]
|
46
|
-
@tables_to_exclude = (options[:except] || []) << 'schema_migrations'
|
47
|
-
end
|
48
|
-
|
49
|
-
def start
|
50
|
-
# no-op
|
51
|
-
end
|
52
|
-
|
35
|
+
class Truncation < ::DatabaseCleaner::TruncationBase
|
53
36
|
|
54
37
|
def clean
|
55
38
|
connection.disable_referential_integrity do
|
@@ -57,9 +40,9 @@ module DatabaseCleaner::ActiveRecord
|
|
57
40
|
connection.truncate_table table_name
|
58
41
|
end
|
59
42
|
end
|
60
|
-
|
43
|
+
end
|
61
44
|
|
62
|
-
|
45
|
+
private
|
63
46
|
|
64
47
|
def tables_to_truncate
|
65
48
|
(@only || connection.tables) - @tables_to_exclude
|
@@ -69,8 +52,12 @@ module DatabaseCleaner::ActiveRecord
|
|
69
52
|
::ActiveRecord::Base.connection
|
70
53
|
end
|
71
54
|
|
72
|
-
|
55
|
+
# overwritten
|
56
|
+
def migration_storage_name
|
57
|
+
'schema_migrations'
|
58
|
+
end
|
73
59
|
|
60
|
+
end
|
74
61
|
end
|
75
62
|
|
76
63
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module DatabaseCleaner
|
2
|
-
|
2
|
+
|
3
3
|
class NoStrategySetError < StandardError; end
|
4
4
|
class NoORMDetected < StandardError; end
|
5
5
|
class UnknownStrategySpecified < ArgumentError; end
|
@@ -12,7 +12,7 @@ module DatabaseCleaner
|
|
12
12
|
|
13
13
|
module DataMapper
|
14
14
|
def self.available_strategies
|
15
|
-
%w[]
|
15
|
+
%w[truncation transaction]
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
@@ -69,14 +69,14 @@ module DatabaseCleaner
|
|
69
69
|
|
70
70
|
def orm
|
71
71
|
@orm ||=begin
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
72
|
+
if defined? ::ActiveRecord
|
73
|
+
'active_record'
|
74
|
+
elsif defined? ::DataMapper
|
75
|
+
'data_mapper'
|
76
|
+
else
|
77
|
+
raise NoORMDetected, "No known ORM was detected! Is ActiveRecord or DataMapper loaded?"
|
78
|
+
end
|
79
|
+
end
|
80
80
|
end
|
81
81
|
|
82
82
|
|
@@ -91,4 +91,45 @@ module DatabaseCleaner
|
|
91
91
|
|
92
92
|
end
|
93
93
|
|
94
|
+
|
95
|
+
class TruncationBase
|
96
|
+
|
97
|
+
def initialize(options = {})
|
98
|
+
if !options.empty? && !(options.keys - [:only, :except]).empty?
|
99
|
+
raise ArgumentError, "The only valid options are :only and :except. You specified #{options.keys.join(',')}."
|
100
|
+
end
|
101
|
+
if options.has_key?(:only) && options.has_key?(:except)
|
102
|
+
raise ArgumentError, "You may only specify either :only or :either. Doing both doesn't really make sense does it?"
|
103
|
+
end
|
104
|
+
|
105
|
+
@only = options[:only]
|
106
|
+
@tables_to_exclude = (options[:except] || [])
|
107
|
+
if migration_storage = migration_storage_name
|
108
|
+
@tables_to_exclude << migration_storage
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def start
|
113
|
+
# no-op
|
114
|
+
end
|
115
|
+
|
116
|
+
def clean
|
117
|
+
raise NotImplementedError
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def tables_to_truncate
|
124
|
+
raise NotImplementedError
|
125
|
+
end
|
126
|
+
|
127
|
+
# overwrite in subclasses
|
128
|
+
# default implementation given because migration storage need not be present
|
129
|
+
def migration_storage_name
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
94
135
|
end
|
@@ -1,6 +1,23 @@
|
|
1
1
|
module DatabaseCleaner::DataMapper
|
2
2
|
class Transaction
|
3
3
|
|
4
|
-
|
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
|
5
21
|
|
22
|
+
end
|
6
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
|
+
query 'SHOW TABLES'
|
19
|
+
end
|
20
|
+
|
21
|
+
def truncate_table(table_name)
|
22
|
+
execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
|
23
|
+
end
|
24
|
+
|
25
|
+
# copied from activerecord
|
26
|
+
def disable_referential_integrity
|
27
|
+
old = query("SELECT @@FOREIGN_KEY_CHECKS;")
|
28
|
+
begin
|
29
|
+
execute("SET FOREIGN_KEY_CHECKS = 0;")
|
30
|
+
yield
|
31
|
+
ensure
|
32
|
+
execute("SET FOREIGN_KEY_CHECKS = #{old};")
|
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
|
+
query sql
|
50
|
+
end
|
51
|
+
|
52
|
+
def truncate_table(table_name)
|
53
|
+
execute("DELETE FROM #{quote_table_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
|
+
query(sql)
|
80
|
+
end
|
81
|
+
|
82
|
+
def truncate_table(table_name)
|
83
|
+
execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
|
84
|
+
end
|
85
|
+
|
86
|
+
# FIXME
|
87
|
+
# copied from activerecord
|
88
|
+
def supports_disable_referential_integrity?
|
89
|
+
version = query("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_table_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_table_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
|
@@ -0,0 +1,40 @@
|
|
1
|
+
class TruncationBase
|
2
|
+
|
3
|
+
def initialize(options = {})
|
4
|
+
if !options.empty? && !(options.keys - [:only, :except]).empty?
|
5
|
+
raise ArgumentError, "The only valid options are :only and :except. You specified #{options.keys.join(',')}."
|
6
|
+
end
|
7
|
+
if options.has_key?(:only) && options.has_key?(:except)
|
8
|
+
raise ArgumentError, "You may only specify either :only or :either. Doing both doesn't really make sense does it?"
|
9
|
+
end
|
10
|
+
|
11
|
+
@only = options[:only]
|
12
|
+
@tables_to_exclude = (options[:except] || [])
|
13
|
+
if migration_storage = migration_storage_name
|
14
|
+
@tables_to_exclude << migration_storage
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
# no-op
|
20
|
+
end
|
21
|
+
|
22
|
+
def clean
|
23
|
+
raise NotImplementedError
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def tables_to_truncate
|
30
|
+
raise NotImplementedError
|
31
|
+
end
|
32
|
+
|
33
|
+
# overwrite in subclasses
|
34
|
+
# default implementation given because migration storage need not be present
|
35
|
+
def migration_storage_name
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
@@ -13,7 +13,7 @@ end
|
|
13
13
|
|
14
14
|
module DatabaseCleaner
|
15
15
|
module ActiveRecord
|
16
|
-
|
16
|
+
|
17
17
|
describe Truncation do
|
18
18
|
before(:each) do
|
19
19
|
@connection = mock('connection')
|
@@ -50,7 +50,7 @@ module DatabaseCleaner
|
|
50
50
|
end
|
51
51
|
|
52
52
|
it "should raise an error when :only and :except options are used" do
|
53
|
-
running {
|
53
|
+
running {
|
54
54
|
Truncation.new(:except => ['widgets'], :only => ['widgets'])
|
55
55
|
}.should raise_error(ArgumentError)
|
56
56
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bmabey-database_cleaner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Mabey
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-05-08 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -32,6 +32,7 @@ files:
|
|
32
32
|
- examples/features/step_definitions/example_steps.rb
|
33
33
|
- examples/features/support/env.rb
|
34
34
|
- examples/lib/activerecord.rb
|
35
|
+
- examples/lib/datamapper.rb
|
35
36
|
- features/cleaning.feature
|
36
37
|
- features/step_definitions/database_cleaner_steps.rb
|
37
38
|
- features/support/env.rb
|
@@ -41,6 +42,8 @@ files:
|
|
41
42
|
- lib/database_cleaner/configuration.rb
|
42
43
|
- lib/database_cleaner/cucumber.rb
|
43
44
|
- lib/database_cleaner/data_mapper/transaction.rb
|
45
|
+
- lib/database_cleaner/data_mapper/truncation.rb
|
46
|
+
- lib/database_cleaner/truncation_base.rb
|
44
47
|
- spec/database_cleaner/active_record/truncation_spec.rb
|
45
48
|
- spec/database_cleaner/configuration_spec.rb
|
46
49
|
- spec/spec.opts
|
@@ -79,3 +82,4 @@ test_files:
|
|
79
82
|
- examples/features/step_definitions/example_steps.rb
|
80
83
|
- examples/features/support/env.rb
|
81
84
|
- examples/lib/activerecord.rb
|
85
|
+
- examples/lib/datamapper.rb
|