tooky-database_cleaner 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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