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 CHANGED
@@ -3,7 +3,14 @@
3
3
  === New features
4
4
  === Bufixes
5
5
 
6
- == 0.1.3 2009-04-30
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 ( Ben Mabey )
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
@@ -43,4 +43,4 @@ rescue LoadError
43
43
  puts "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
44
44
  end
45
45
 
46
- task :default => :spec
46
+ task :default => [:spec, :features]
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 3
2
+ :patch: 0
3
3
  :major: 0
4
- :minor: 1
4
+ :minor: 2
@@ -1,16 +1,23 @@
1
1
  require 'rubygems'
2
2
  require 'spec/expectations'
3
3
 
4
- begin
5
- require "#{File.dirname(__FILE__)}/../../lib/#{ENV['ORM']}"
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
- DatabaseCleaner.strategy = ENV['STRATEGY'].to_sym
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!
@@ -14,3 +14,5 @@ Feature: database cleaning
14
14
  | ORM | Strategy |
15
15
  | ActiveRecord | transaction |
16
16
  | ActiveRecord | truncation |
17
+ | DataMapper | transaction |
18
+ | DataMapper | truncation |
@@ -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
- end
43
+ end
61
44
 
62
- private
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
- end
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
- 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
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
- end
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.1.3
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-04-30 00:00:00 -07:00
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