tooky-database_cleaner 0.4.1

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.
@@ -0,0 +1,58 @@
1
+ 0.4.x (In Git)
2
+
3
+ == 0.4.1 2010-01-07
4
+
5
+ === Bufixes
6
+ * Postgres tables with FKs now truncate (added TRUNCADE CASCADE) using Datamapper. (Ben Mabey)
7
+
8
+ == 0.4.0 2009-12-23 (The MongoMapper Edition)
9
+
10
+ === New features
11
+ * MongoMapper support for the truncation strategy. (Aubrey Holland)
12
+
13
+ == 0.3.0 2009-12-20
14
+
15
+ === New features
16
+ * DataMapper 0.10.0 Compatible. (Martin Gamsjaeger)
17
+ === Bufixes
18
+ * Postgres tables with FKs now truncate (added TRUNCADE CASCADE). (Vika - yozhyk on github)
19
+
20
+ == 0.2.3 2009-05-30
21
+
22
+ === New features
23
+ * Support for SQL Server truncation (Adam Meehan)
24
+
25
+ == 0.2.2 2009-05-08
26
+ === Bufixes
27
+ * Added proper gemspec description and summary. (Ben Mabey, thanks to Martin Gamsjaeger)
28
+
29
+ === New features
30
+
31
+ == 0.2.1 2009-05-08
32
+ === Bufixes
33
+ * Removed extraneous TruncationBase class definition. (Ben Mabey)
34
+
35
+ == 0.2.0 2009-05-08 - The Datamapper Release
36
+
37
+ === New features
38
+ * DataMapper strategies (Martin Gamsjaeger)
39
+ * Transaction
40
+ * Truncation - working SQLite3, MySQL adapters. Experimental Postgres adapter (not tested).
41
+
42
+ == 0.1.3 2009-04-30
43
+
44
+ === New features
45
+ * PostgresSQLAdapter for AR to support the truncation strategy. (Alberto Perdomo)
46
+ === Bufixes
47
+ * Added missing quotes around table names in truncation calls. (Michael MacDonald)
48
+
49
+ == 0.1.2 2009-03-05
50
+ === New features
51
+ * JDBC Adapter to enable AR truncation strategy to work. (Kamal Fariz Mahyuddin)
52
+
53
+ == 0.1.1 2009-03-04 - Initial Release (Ben Mabey)
54
+ * Basic infrastructure
55
+ * Features, RSpec code examples
56
+ * ActiveRecord strategies
57
+ * Truncation - with MySQL, and SQLite3 adapters.
58
+ * 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.
@@ -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
+ ActiveRecord, DataMapper and MongoMapper 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.
@@ -0,0 +1,46 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |s|
6
+ s.name = "tooky-database_cleaner"
7
+ s.summary = %Q{Strategies for cleaning databases. Can be used to ensure a clean state for testing.}
8
+ s.email = "steve.tooke@gmail.com"
9
+ s.homepage = "http://github.com/tooky/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/TODO ADDED
File without changes
@@ -0,0 +1,5 @@
1
+ ---
2
+ :minor: 4
3
+ :patch: 1
4
+ :build:
5
+ :major: 0
@@ -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|mongomapper 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,10 @@
1
+ require 'mongo_mapper'
2
+
3
+ ::MongoMapper.connection = Mongo::Connection.new('127.0.0.1')
4
+ ::MongoMapper.database = 'database_cleaner_test'
5
+
6
+ class Widget
7
+ include MongoMapper::Document
8
+ key :id, Integer
9
+ key :name, String
10
+ end
@@ -0,0 +1,19 @@
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 |
19
+ | MongoMapper | truncation |
@@ -0,0 +1,25 @@
1
+ Given /^I am using (ActiveRecord|DataMapper|MongoMapper)$/ 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)} CASCADE;")
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,104 @@
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
+ module MongoMapper
20
+ def self.available_strategies
21
+ %w[truncation]
22
+ end
23
+ end
24
+
25
+ class << self
26
+
27
+ def create_strategy(*args)
28
+ strategy, *strategy_args = args
29
+ orm_strategy(strategy).new(*strategy_args)
30
+ end
31
+
32
+ def clean_with(*args)
33
+ strategy = create_strategy(*args)
34
+ strategy.clean
35
+ strategy
36
+ end
37
+
38
+ def strategy=(args)
39
+ strategy, *strategy_args = args
40
+ if strategy.is_a?(Symbol)
41
+ @strategy = create_strategy(*args)
42
+ elsif strategy_args.empty?
43
+ @strategy = strategy
44
+ else
45
+ raise ArgumentError, "You must provide a strategy object, or a symbol for a know strategy along with initialization params."
46
+ end
47
+ end
48
+
49
+ def orm=(orm_string)
50
+ @orm = orm_string
51
+ end
52
+
53
+ def start
54
+ strategy.start
55
+ end
56
+
57
+ def clean
58
+ strategy.clean
59
+ end
60
+
61
+ private
62
+
63
+ def strategy
64
+ return @strategy if @strategy
65
+ raise NoStrategySetError, "Please set a strategy with DatabaseCleaner.strategy=."
66
+ end
67
+
68
+ def orm_strategy(strategy)
69
+ require "database_cleaner/#{orm}/#{strategy}"
70
+ orm_module.const_get(strategy.to_s.capitalize)
71
+ rescue LoadError => e
72
+ raise UnknownStrategySpecified, "The '#{strategy}' strategy does not exist for the #{orm} ORM! Available strategies: #{orm_module.available_strategies.join(', ')}"
73
+ end
74
+
75
+
76
+ def orm
77
+ @orm ||=begin
78
+ if defined? ::ActiveRecord
79
+ 'active_record'
80
+ elsif defined? ::DataMapper
81
+ 'data_mapper'
82
+ elsif defined? ::MongoMapper
83
+ 'mongo_mapper'
84
+ else
85
+ raise NoORMDetected, "No known ORM was detected! Is ActiveRecord or DataMapper loaded?"
86
+ end
87
+ end
88
+ end
89
+
90
+
91
+ def orm_module
92
+ case orm
93
+ when 'active_record'
94
+ DatabaseCleaner::ActiveRecord
95
+ when 'data_mapper'
96
+ DatabaseCleaner::DataMapper
97
+ when 'mongo_mapper'
98
+ DatabaseCleaner::MongoMapper
99
+ end
100
+ end
101
+
102
+ end
103
+
104
+ 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};")
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
@@ -0,0 +1,21 @@
1
+ require 'database_cleaner/truncation_base'
2
+
3
+ module DatabaseCleaner
4
+ module MongoMapper
5
+ class Truncation < DatabaseCleaner::TruncationBase
6
+ def clean
7
+ connection.db(database).collections.each { |c| c.remove }
8
+ end
9
+
10
+ private
11
+
12
+ def connection
13
+ ::MongoMapper.connection
14
+ end
15
+
16
+ def database
17
+ ::MongoMapper.database.name
18
+ end
19
+ end
20
+ end
21
+ 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
@@ -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,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tooky-database_cleaner
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.1
5
+ platform: ruby
6
+ authors:
7
+ - Ben Mabey
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-11 00:00:00 +00: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: steve.tooke@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.textile
25
+ - TODO
26
+ files:
27
+ - History.txt
28
+ - README.textile
29
+ - Rakefile
30
+ - VERSION.yml
31
+ - cucumber.yml
32
+ - examples/features/example.feature
33
+ - examples/features/step_definitions/example_steps.rb
34
+ - examples/features/support/env.rb
35
+ - examples/lib/activerecord.rb
36
+ - examples/lib/datamapper.rb
37
+ - examples/lib/mongomapper.rb
38
+ - features/cleaning.feature
39
+ - features/step_definitions/database_cleaner_steps.rb
40
+ - features/support/env.rb
41
+ - lib/database_cleaner.rb
42
+ - lib/database_cleaner/active_record/transaction.rb
43
+ - lib/database_cleaner/active_record/truncation.rb
44
+ - lib/database_cleaner/configuration.rb
45
+ - lib/database_cleaner/cucumber.rb
46
+ - lib/database_cleaner/data_mapper/transaction.rb
47
+ - lib/database_cleaner/data_mapper/truncation.rb
48
+ - lib/database_cleaner/mongo_mapper/truncation.rb
49
+ - lib/database_cleaner/truncation_base.rb
50
+ - spec/database_cleaner/active_record/truncation_spec.rb
51
+ - spec/database_cleaner/configuration_spec.rb
52
+ - spec/spec.opts
53
+ - spec/spec_helper.rb
54
+ - LICENSE
55
+ - TODO
56
+ has_rdoc: true
57
+ homepage: http://github.com/tooky/database_cleaner
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.3.5
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Strategies for cleaning databases. Can be used to ensure a clean state for testing.
84
+ test_files:
85
+ - spec/database_cleaner/active_record/truncation_spec.rb
86
+ - spec/database_cleaner/configuration_spec.rb
87
+ - spec/spec_helper.rb
88
+ - examples/features/step_definitions/example_steps.rb
89
+ - examples/features/support/env.rb
90
+ - examples/lib/activerecord.rb
91
+ - examples/lib/datamapper.rb
92
+ - examples/lib/mongomapper.rb