database_cleaner 0.2.3

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 ADDED
@@ -0,0 +1,41 @@
1
+ 0.2.x (In Git)
2
+
3
+ == 0.2.3 2009-05-30
4
+
5
+ === New features
6
+ * Support for SQL Server truncation (Adam Meehan)
7
+
8
+ == 0.2.2 2009-05-08
9
+ === Bufixes
10
+ * Added proper gemspec description and summary. (Ben Mabey, thanks to Martin Gamsjaeger)
11
+
12
+ === New features
13
+
14
+ == 0.2.1 2009-05-08
15
+ === Bufixes
16
+ * Removed extraneous TruncationBase class definition. (Ben Mabey)
17
+
18
+ == 0.2.0 2009-05-08 - The Datamapper Release
19
+
20
+ === New features
21
+ * DataMapper strategies (Martin Gamsjaeger)
22
+ * Transaction
23
+ * Truncation - working SQLite3, MySQL adapters. Experimental Postgres adapter (not tested).
24
+
25
+ == 0.1.3 2009-04-30
26
+
27
+ === New features
28
+ * PostgresSQLAdapter for AR to support the truncation strategy. (Alberto Perdomo)
29
+ === Bufixes
30
+ * Added missing quotes around table names in truncation calls. (Michael MacDonald)
31
+
32
+ == 0.1.2 2009-03-05
33
+ === New features
34
+ * JDBC Adapter to enable AR truncation strategy to work. (Kamal Fariz Mahyuddin)
35
+
36
+ == 0.1.1 2009-03-04 - Initial Release (Ben Mabey)
37
+ * Basic infrastructure
38
+ * Features, RSpec code examples
39
+ * ActiveRecord strategies
40
+ * Truncation - with MySQL, and SQLite3 adapters.
41
+ * Transaction - wrap your modifications and roll them back.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Ben Mabey
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,118 @@
1
+ h1. Database Cleaner
2
+
3
+ Database Cleaner is a set of strategies for cleaning your database in Ruby.
4
+ The original use case was to ensure a clean state during tests. Each strategy
5
+ is a small amount of code but is code that is usually needed in any ruby app
6
+ that is testing with a database.
7
+
8
+ Both ActiveRecord and DataMapper are supported.
9
+
10
+ h2. How to use
11
+
12
+ <pre>
13
+ require 'database_cleaner'
14
+ DatabaseCleaner.strategy = :truncation
15
+
16
+ # then, whenever you need to clean the DB
17
+ DatabaseCleaner.clean
18
+ </pre>
19
+
20
+ With the :truncation strategy you can also pass in options, for example:
21
+ <pre>
22
+ DatabaseCleaner.strategy = :truncation, {:only => %w[widgets dogs some_other_table]}
23
+ </pre>
24
+
25
+ <pre>
26
+ DatabaseCleaner.strategy = :truncation, {:except => %w[widgets]}
27
+ </pre>
28
+
29
+ (I should point out the truncation strategy will never truncate your schema_migrations table.)
30
+
31
+
32
+ Some strategies require that you call DatabaseCleaner.start before calling clean
33
+ (for example the :transaction one needs to know to open up a transaction). So
34
+ you would have:
35
+
36
+ <pre>
37
+ require 'database_cleaner'
38
+ DatabaseCleaner.strategy = :transaction
39
+
40
+ DatabaseCleaner.start # usually this is called in setup of a test
41
+ dirty_the_db
42
+ DatabaseCleaner.clean # cleanup of the test
43
+ </pre>
44
+
45
+ At times you may want to do a single clean with one strategy. For example, you may want
46
+ to start the process by truncating all the tables, but then use the faster transaction
47
+ strategy the remaining time. To accomplish this you can say:
48
+
49
+ <pre>
50
+ require 'database_cleaner'
51
+ DatabaseCleaner.clean_with :truncation
52
+ DatabaseCleaner.strategy = :transaction
53
+ # then make the DatabaseCleaner.start and DatabaseCleaner.clean calls appropriately
54
+ </pre>
55
+
56
+ Example usage with RSpec:
57
+
58
+ <pre>
59
+ Spec::Runner.configure do |config|
60
+
61
+ config.before(:suite) do
62
+ DatabaseCleaner.strategy = :transaction
63
+ DatabaseCleaner.clean_with(:truncation)
64
+ end
65
+
66
+ config.before(:each) do
67
+ DatabaseCleaner.start
68
+ end
69
+
70
+ config.after(:each) do
71
+ DatabaseCleaner.clean
72
+ end
73
+
74
+ end
75
+ </pre>
76
+
77
+ For use in Cucumber please see the section below.
78
+
79
+
80
+
81
+ h2. Why?
82
+
83
+ One of my motivations for writing this library was to have an easy way to
84
+ turn on what Rails calls "transactional_fixtures" in my non-rails
85
+ ActiveRecord projects. For example, Cucumber ships with a Rails world that
86
+ will wrap each scenario in a transaction. This is great, but what if you are
87
+ using ActiveRecord in a non-rails project? You used to have to copy-and-paste
88
+ the needed code, but with DatabaseCleaner you can now say:
89
+
90
+ <pre>
91
+ #env.rb
92
+ require 'database_cleaner'
93
+ require 'database_cleaner/cucumber'
94
+ DatabaseCleaner.strategy = :transaction
95
+ </pre>
96
+
97
+ Now lets say you are running your features and it requires that another process be
98
+ involved (i.e. Selenium running against your app's server.) You can simply change
99
+ your strategy type:
100
+
101
+ <pre>
102
+ #env.rb
103
+ require 'database_cleaner'
104
+ require 'database_cleaner/cucumber'
105
+ DatabaseCleaner.strategy = :truncation
106
+ </pre>
107
+
108
+ You can have the best of both worlds and use the best one for the job:
109
+ <pre>
110
+ #env.rb
111
+ require 'database_cleaner'
112
+ require 'database_cleaner/cucumber'
113
+ DatabaseCleaner.strategy = (ENV['SELENIUM'] == 'true') ? :truncation : :transaction
114
+ </pre>
115
+
116
+ h2. COPYRIGHT
117
+
118
+ Copyright (c) 2009 Ben Mabey. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |s|
6
+ s.name = "database_cleaner"
7
+ s.summary = %Q{Strategies for cleaning databases. Can be used to ensure a clean state for testing.}
8
+ s.email = "ben@benmabey.com"
9
+ s.homepage = "http://github.com/bmabey/database_cleaner"
10
+ s.description = "Strategies for cleaning databases. Can be used to ensure a clean state for testing."
11
+ s.files = FileList["[A-Z]*.*", "{examples,lib,features,spec}/**/*", "Rakefile", "cucumber.yml"]
12
+ s.authors = ["Ben Mabey"]
13
+ end
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
16
+ end
17
+
18
+ require 'rake/rdoctask'
19
+ Rake::RDocTask.new do |rdoc|
20
+ rdoc.rdoc_dir = 'rdoc'
21
+ rdoc.title = 'database_cleaner'
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.rdoc_files.include('README*')
24
+ rdoc.rdoc_files.include('lib/**/*.rb')
25
+ end
26
+
27
+ require 'spec/rake/spectask'
28
+ Spec::Rake::SpecTask.new(:spec) do |t|
29
+ t.libs << 'lib' << 'spec'
30
+ t.spec_files = FileList['spec/**/*_spec.rb']
31
+ end
32
+
33
+ Spec::Rake::SpecTask.new(:rcov) do |t|
34
+ t.libs << 'lib' << 'spec'
35
+ t.spec_files = FileList['spec/**/*_spec.rb']
36
+ t.rcov = true
37
+ end
38
+
39
+ begin
40
+ require 'cucumber/rake/task'
41
+ Cucumber::Rake::Task.new(:features)
42
+ rescue LoadError
43
+ puts "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
44
+ end
45
+
46
+ task :default => [:spec, :features]
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 2
3
+ :major: 0
4
+ :minor: 2
data/cucumber.yml ADDED
@@ -0,0 +1 @@
1
+ default: features
@@ -0,0 +1,11 @@
1
+ Feature: example
2
+ In order to test DataBase Cleaner
3
+ Here are some scenarios that rely on the DB being clean!
4
+
5
+ Scenario: dirty the db
6
+ When I create a widget
7
+ Then I should see 1 widget
8
+
9
+ Scenario: assume a clean db
10
+ When I create a widget
11
+ Then I should see 1 widget
@@ -0,0 +1,8 @@
1
+ When /^I create a widget$/ do
2
+ Widget.create!
3
+ end
4
+
5
+ Then /^I should see 1 widget$/ do
6
+ Widget.count.should == 1
7
+ end
8
+
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'spec/expectations'
3
+
4
+ orm = ENV['ORM']
5
+ strategy = ENV['STRATEGY']
6
+
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
14
+
15
+ $:.unshift(File.dirname(__FILE__) + '/../../../lib')
16
+ require 'database_cleaner'
17
+ require 'database_cleaner/cucumber'
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,12 @@
1
+ require 'activerecord'
2
+
3
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :dbfile => ":memory:")
4
+
5
+ ActiveRecord::Schema.define(:version => 1) do
6
+ create_table :widgets do |t|
7
+ t.string :name
8
+ end
9
+ end
10
+
11
+ class Widget < ActiveRecord::Base
12
+ 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!
@@ -0,0 +1,18 @@
1
+ Feature: database cleaning
2
+ In order to ease example and feature writing
3
+ As a developer
4
+ I want to have my database in a clean state
5
+
6
+ Scenario Outline: ruby app
7
+ Given I am using <ORM>
8
+ And the <Strategy> cleaning strategy
9
+
10
+ When I run my scenarios that rely on a clean database
11
+ Then I should see all green
12
+
13
+ Examples:
14
+ | ORM | Strategy |
15
+ | ActiveRecord | transaction |
16
+ | ActiveRecord | truncation |
17
+ | DataMapper | transaction |
18
+ | DataMapper | truncation |
@@ -0,0 +1,25 @@
1
+ Given /^I am using (ActiveRecord|DataMapper)$/ 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 = `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,69 @@
1
+ require "database_cleaner/truncation_base"
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+
6
+ class MysqlAdapter
7
+ def truncate_table(table_name)
8
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
9
+ end
10
+ end
11
+
12
+ class SQLite3Adapter
13
+ def truncate_table(table_name)
14
+ execute("DELETE FROM #{quote_table_name(table_name)};")
15
+ end
16
+ end
17
+
18
+ class JdbcAdapter
19
+ def truncate_table(table_name)
20
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
21
+ end
22
+ end
23
+
24
+ class PostgreSQLAdapter
25
+ def truncate_table(table_name)
26
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
27
+ end
28
+ end
29
+
30
+ class SQLServerAdapter
31
+ def truncate_table(table_name)
32
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+
39
+
40
+ module DatabaseCleaner::ActiveRecord
41
+ class Truncation < ::DatabaseCleaner::TruncationBase
42
+
43
+ def clean
44
+ connection.disable_referential_integrity do
45
+ tables_to_truncate.each do |table_name|
46
+ connection.truncate_table table_name
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def tables_to_truncate
54
+ (@only || connection.tables) - @tables_to_exclude
55
+ end
56
+
57
+ def connection
58
+ ::ActiveRecord::Base.connection
59
+ end
60
+
61
+ # overwritten
62
+ def migration_storage_name
63
+ 'schema_migrations'
64
+ end
65
+
66
+ end
67
+ end
68
+
69
+
@@ -0,0 +1,94 @@
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
+ class << self
20
+
21
+ def create_strategy(*args)
22
+ strategy, *strategy_args = args
23
+ orm_strategy(strategy).new(*strategy_args)
24
+ end
25
+
26
+ def clean_with(*args)
27
+ strategy = create_strategy(*args)
28
+ strategy.clean
29
+ strategy
30
+ end
31
+
32
+ def strategy=(args)
33
+ strategy, *strategy_args = args
34
+ if strategy.is_a?(Symbol)
35
+ @strategy = create_strategy(*args)
36
+ elsif strategy_args.empty?
37
+ @strategy = strategy
38
+ else
39
+ raise ArgumentError, "You must provide a strategy object, or a symbol for a know strategy along with initialization params."
40
+ end
41
+ end
42
+
43
+ def orm=(orm_string)
44
+ @orm = orm_string
45
+ end
46
+
47
+ def start
48
+ strategy.start
49
+ end
50
+
51
+ def clean
52
+ strategy.clean
53
+ end
54
+
55
+ private
56
+
57
+ def strategy
58
+ return @strategy if @strategy
59
+ raise NoStrategySetError, "Please set a strategy with DatabaseCleaner.strategy=."
60
+ end
61
+
62
+ def orm_strategy(strategy)
63
+ require "database_cleaner/#{orm}/#{strategy}"
64
+ orm_module.const_get(strategy.to_s.capitalize)
65
+ rescue LoadError => e
66
+ raise UnknownStrategySpecified, "The '#{strategy}' strategy does not exist for the #{orm} ORM! Available strategies: #{orm_module.available_strategies.join(', ')}"
67
+ end
68
+
69
+
70
+ def orm
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
80
+ end
81
+
82
+
83
+ def orm_module
84
+ case orm
85
+ when 'active_record'
86
+ DatabaseCleaner::ActiveRecord
87
+ when 'data_mapper'
88
+ DatabaseCleaner::DataMapper
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ 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
+ 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,41 @@
1
+ module DatabaseCleaner
2
+ class TruncationBase
3
+
4
+ def initialize(options = {})
5
+ if !options.empty? && !(options.keys - [:only, :except]).empty?
6
+ raise ArgumentError, "The only valid options are :only and :except. You specified #{options.keys.join(',')}."
7
+ end
8
+ if options.has_key?(:only) && options.has_key?(:except)
9
+ raise ArgumentError, "You may only specify either :only or :either. Doing both doesn't really make sense does it?"
10
+ end
11
+
12
+ @only = options[:only]
13
+ @tables_to_exclude = (options[:except] || [])
14
+ if migration_storage = migration_storage_name
15
+ @tables_to_exclude << migration_storage
16
+ end
17
+ end
18
+
19
+ def start
20
+ # no-op
21
+ end
22
+
23
+ def clean
24
+ raise NotImplementedError
25
+ end
26
+
27
+
28
+ private
29
+
30
+ def tables_to_truncate
31
+ raise NotImplementedError
32
+ end
33
+
34
+ # overwrite in subclasses
35
+ # default implementation given because migration storage need not be present
36
+ def migration_storage_name
37
+ nil
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+ require 'database_cleaner/active_record/truncation'
3
+ require 'active_record'
4
+ module ActiveRecord
5
+ module ConnectionAdapters
6
+ [MysqlAdapter, SQLite3Adapter, JdbcAdapter, PostgreSQLAdapter].each do |adapter|
7
+ describe adapter, "#truncate_table" do
8
+ it "should truncate the table"
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ module DatabaseCleaner
15
+ module ActiveRecord
16
+
17
+ describe Truncation do
18
+ before(:each) do
19
+ @connection = mock('connection')
20
+ @connection.stub!(:disable_referential_integrity).and_yield
21
+ ::ActiveRecord::Base.stub!(:connection).and_return(@connection)
22
+ end
23
+
24
+ it "should truncate all tables except for schema_migrations" do
25
+ @connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
26
+
27
+ @connection.should_receive(:truncate_table).with('widgets')
28
+ @connection.should_receive(:truncate_table).with('dogs')
29
+ @connection.should_not_receive(:truncate_table).with('schema_migrations')
30
+
31
+ Truncation.new.clean
32
+ end
33
+
34
+ it "should only truncate the tables specified in the :only option when provided" do
35
+ @connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
36
+
37
+ @connection.should_receive(:truncate_table).with('widgets')
38
+ @connection.should_not_receive(:truncate_table).with('dogs')
39
+
40
+ Truncation.new(:only => ['widgets']).clean
41
+ end
42
+
43
+ it "should not truncate the tables specified in the :except option" do
44
+ @connection.stub!(:tables).and_return(%w[schema_migrations widgets dogs])
45
+
46
+ @connection.should_receive(:truncate_table).with('dogs')
47
+ @connection.should_not_receive(:truncate_table).with('widgets')
48
+
49
+ Truncation.new(:except => ['widgets']).clean
50
+ end
51
+
52
+ it "should raise an error when :only and :except options are used" do
53
+ running {
54
+ Truncation.new(:except => ['widgets'], :only => ['widgets'])
55
+ }.should raise_error(ArgumentError)
56
+ end
57
+
58
+ it "should raise an error when invalid options are provided" do
59
+ running { Truncation.new(:foo => 'bar') }.should raise_error(ArgumentError)
60
+ end
61
+
62
+
63
+ end
64
+
65
+ end
66
+ end
@@ -0,0 +1,101 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'database_cleaner/active_record/transaction'
3
+ require 'database_cleaner/data_mapper/transaction'
4
+
5
+ describe DatabaseCleaner do
6
+
7
+ # These examples muck around with the constants for autodetection so we need to clean up....
8
+ before(:all) do
9
+ TempAR = ActiveRecord unless defined?(TempAR)
10
+ # need to add one for each ORM that we load in the spec helper...
11
+ end
12
+ after(:all) do
13
+ Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord) #want to make sure we have the real one...
14
+ ActiveRecord = TempAR
15
+ end
16
+
17
+ before(:each) do
18
+ DatabaseCleaner::ActiveRecord::Transaction.stub!(:new).and_return(@strategy = mock('strategy'))
19
+ Object.const_set('ActiveRecord', "just mocking out the constant here...") unless defined?(::ActiveRecord)
20
+ DatabaseCleaner.strategy = nil
21
+ DatabaseCleaner.orm = nil
22
+ end
23
+
24
+ describe ".create_strategy" do
25
+ it "should initialize and return the appropirate strategy" do
26
+ DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
27
+ result = DatabaseCleaner.create_strategy(:transaction, {'options' => 'hash'})
28
+
29
+ result.should == @strategy
30
+ end
31
+ end
32
+
33
+ describe ".clean_with" do
34
+ it "should initialize the appropirate strategy and clean with it" do
35
+ DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
36
+ @strategy.should_receive(:clean)
37
+
38
+ DatabaseCleaner.clean_with(:transaction, 'options' => 'hash')
39
+ end
40
+ end
41
+
42
+ describe ".strategy=" do
43
+ it "should initialize the appropirate strategy based on the ORM adapter detected" do
44
+ DatabaseCleaner::ActiveRecord::Transaction.should_receive(:new).with('options' => 'hash')
45
+ DatabaseCleaner.strategy = :transaction, {'options' => 'hash'}
46
+
47
+ Object.send(:remove_const, 'ActiveRecord')
48
+ Object.const_set('DataMapper', "just mocking out the constant here...")
49
+ DatabaseCleaner.orm = nil
50
+
51
+ DatabaseCleaner::DataMapper::Transaction.should_receive(:new).with(no_args)
52
+ DatabaseCleaner.strategy = :transaction
53
+ end
54
+
55
+ it "should raise an error when no ORM is detected" do
56
+ Object.send(:remove_const, 'ActiveRecord') if defined?(::ActiveRecord)
57
+ Object.send(:remove_const, 'DataMapper') if defined?(::DataMapper)
58
+
59
+ running { DatabaseCleaner.strategy = :transaction }.should raise_error(DatabaseCleaner::NoORMDetected)
60
+ end
61
+
62
+ it "should use the strategy version of the ORM specified with #orm=" do
63
+ DatabaseCleaner.orm = 'data_mapper'
64
+ DatabaseCleaner::DataMapper::Transaction.should_receive(:new)
65
+
66
+ DatabaseCleaner.strategy = :transaction
67
+ end
68
+
69
+ it "should raise an error when multiple args is passed in and the first is not a symbol" do
70
+ running { DatabaseCleaner.strategy=Object.new, {:foo => 'bar'} }.should raise_error(ArgumentError)
71
+ end
72
+
73
+ it "should raise an error when the specified strategy is not found" do
74
+ running { DatabaseCleaner.strategy = :foo }.should raise_error(DatabaseCleaner::UnknownStrategySpecified)
75
+ end
76
+
77
+ it "should allow any object to be set as the strategy" do
78
+ mock_strategy = mock('strategy')
79
+ running { DatabaseCleaner.strategy = mock_strategy }.should_not raise_error
80
+ end
81
+
82
+ end
83
+
84
+
85
+ %w[start clean].each do |strategy_method|
86
+ describe ".#{strategy_method}" do
87
+ it "should be delgated to the strategy set with strategy=" do
88
+ DatabaseCleaner.strategy = :transaction
89
+
90
+ @strategy.should_receive(strategy_method)
91
+
92
+ DatabaseCleaner.send(strategy_method)
93
+ end
94
+
95
+ it "should raise en error when no strategy has been set" do
96
+ running { DatabaseCleaner.send(strategy_method) }.should raise_error(DatabaseCleaner::NoStrategySetError)
97
+ end
98
+ end
99
+ end
100
+
101
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,6 @@
1
+ --colour
2
+ --format nested
3
+ --loadby
4
+ mtime
5
+ --reverse
6
+ --backtrace
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'activerecord'
4
+
5
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
6
+ require 'database_cleaner'
7
+
8
+ Spec::Runner.configure do |config|
9
+
10
+ end
11
+
12
+ alias running lambda
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: database_cleaner
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.3
5
+ platform: ruby
6
+ authors:
7
+ - Ben Mabey
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-30 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Strategies for cleaning databases. Can be used to ensure a clean state for testing.
17
+ email: ben@benmabey.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.textile
25
+ files:
26
+ - History.txt
27
+ - README.textile
28
+ - Rakefile
29
+ - VERSION.yml
30
+ - cucumber.yml
31
+ - examples/features/example.feature
32
+ - examples/features/step_definitions/example_steps.rb
33
+ - examples/features/support/env.rb
34
+ - examples/lib/activerecord.rb
35
+ - examples/lib/datamapper.rb
36
+ - features/cleaning.feature
37
+ - features/step_definitions/database_cleaner_steps.rb
38
+ - features/support/env.rb
39
+ - lib/database_cleaner.rb
40
+ - lib/database_cleaner/active_record/transaction.rb
41
+ - lib/database_cleaner/active_record/truncation.rb
42
+ - lib/database_cleaner/configuration.rb
43
+ - lib/database_cleaner/cucumber.rb
44
+ - lib/database_cleaner/data_mapper/transaction.rb
45
+ - lib/database_cleaner/data_mapper/truncation.rb
46
+ - lib/database_cleaner/truncation_base.rb
47
+ - spec/database_cleaner/active_record/truncation_spec.rb
48
+ - spec/database_cleaner/configuration_spec.rb
49
+ - spec/spec.opts
50
+ - spec/spec_helper.rb
51
+ - LICENSE
52
+ has_rdoc: true
53
+ homepage: http://github.com/bmabey/database_cleaner
54
+ licenses: []
55
+
56
+ post_install_message:
57
+ rdoc_options:
58
+ - --charset=UTF-8
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ version:
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ requirements: []
74
+
75
+ rubyforge_project:
76
+ rubygems_version: 1.3.3
77
+ signing_key:
78
+ specification_version: 3
79
+ summary: Strategies for cleaning databases. Can be used to ensure a clean state for testing.
80
+ test_files:
81
+ - spec/spec_helper.rb
82
+ - spec/database_cleaner/configuration_spec.rb
83
+ - spec/database_cleaner/active_record/truncation_spec.rb
84
+ - examples/lib/datamapper.rb
85
+ - examples/lib/activerecord.rb
86
+ - examples/features/step_definitions/example_steps.rb
87
+ - examples/features/support/env.rb