database_cleaner 0.5.2 → 0.6.0.rc.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.
Files changed (71) hide show
  1. data/Gemfile.lock +145 -0
  2. data/History.txt +24 -1
  3. data/README.textile +48 -0
  4. data/Rakefile +4 -0
  5. data/TODO +3 -0
  6. data/VERSION.yml +3 -2
  7. data/examples/Gemfile +46 -0
  8. data/examples/Gemfile.lock +145 -0
  9. data/examples/config/database.yml +7 -0
  10. data/examples/config/database.yml.example +8 -0
  11. data/examples/db/activerecord_one.db +0 -0
  12. data/examples/db/activerecord_two.db +0 -0
  13. data/examples/db/datamapper_default.db +0 -0
  14. data/examples/db/datamapper_one.db +0 -0
  15. data/examples/db/datamapper_two.db +0 -0
  16. data/examples/db/sqlite_databases_go_here +0 -0
  17. data/examples/features/example_multiple_db.feature +23 -0
  18. data/examples/features/example_multiple_orm.feature +22 -0
  19. data/examples/features/step_definitions/activerecord_steps.rb +31 -0
  20. data/examples/features/step_definitions/couchpotato_steps.rb +31 -0
  21. data/examples/features/step_definitions/datamapper_steps.rb +37 -0
  22. data/examples/features/step_definitions/mongoid_steps.rb +23 -0
  23. data/examples/features/step_definitions/mongomapper_steps.rb +31 -0
  24. data/examples/features/step_definitions/translation_steps.rb +55 -0
  25. data/examples/features/support/env.rb +49 -10
  26. data/examples/lib/activerecord_models.rb +34 -5
  27. data/examples/lib/couchpotato_models.rb +46 -6
  28. data/examples/lib/datamapper_models.rb +37 -3
  29. data/examples/lib/mongoid_models.rb +28 -2
  30. data/examples/lib/mongomapper_models.rb +35 -1
  31. data/features/cleaning_multiple_dbs.feature +20 -0
  32. data/features/cleaning_multiple_orms.feature +29 -0
  33. data/features/step_definitions/database_cleaner_steps.rb +20 -13
  34. data/features/support/feature_runner.rb +39 -0
  35. data/lib/database_cleaner/active_record/base.rb +46 -0
  36. data/lib/database_cleaner/active_record/transaction.rb +10 -10
  37. data/lib/database_cleaner/active_record/truncation.rb +17 -7
  38. data/lib/database_cleaner/base.rb +129 -0
  39. data/lib/database_cleaner/configuration.rb +45 -97
  40. data/lib/database_cleaner/couch_potato/base.rb +7 -0
  41. data/lib/database_cleaner/couch_potato/truncation.rb +4 -2
  42. data/lib/database_cleaner/cucumber.rb +0 -1
  43. data/lib/database_cleaner/data_mapper/base.rb +21 -0
  44. data/lib/database_cleaner/data_mapper/transaction.rb +10 -5
  45. data/lib/database_cleaner/data_mapper/truncation.rb +52 -19
  46. data/lib/database_cleaner/generic/base.rb +23 -0
  47. data/lib/database_cleaner/generic/truncation.rb +43 -0
  48. data/lib/database_cleaner/mongo_mapper/base.rb +20 -0
  49. data/lib/database_cleaner/mongo_mapper/truncation.rb +9 -3
  50. data/lib/database_cleaner/mongoid/base.rb +20 -0
  51. data/lib/database_cleaner/mongoid/truncation.rb +9 -5
  52. data/spec/database_cleaner/active_record/base_spec.rb +130 -0
  53. data/spec/database_cleaner/active_record/truncation_spec.rb +19 -18
  54. data/spec/database_cleaner/base_spec.rb +441 -0
  55. data/spec/database_cleaner/configuration_spec.rb +255 -68
  56. data/spec/database_cleaner/couch_potato/truncation_spec.rb +4 -3
  57. data/spec/database_cleaner/data_mapper/base_spec.rb +30 -0
  58. data/spec/database_cleaner/data_mapper/transaction_spec.rb +23 -0
  59. data/spec/database_cleaner/data_mapper/truncation_spec.rb +11 -0
  60. data/spec/database_cleaner/generic/base_spec.rb +22 -0
  61. data/spec/database_cleaner/generic/truncation_spec.rb +68 -0
  62. data/spec/database_cleaner/mongo_mapper/base_spec.rb +33 -0
  63. data/spec/database_cleaner/mongo_mapper/mongo_examples.rb +8 -0
  64. data/spec/database_cleaner/mongo_mapper/truncation_spec.rb +11 -18
  65. data/spec/database_cleaner/shared_strategy_spec.rb +13 -0
  66. data/spec/rcov.opts +1 -0
  67. data/spec/spec.opts +1 -0
  68. data/spec/spec_helper.rb +10 -3
  69. metadata +76 -8
  70. data/examples/features/step_definitions/example_steps.rb +0 -8
  71. data/lib/database_cleaner/truncation_base.rb +0 -41
@@ -1,16 +1,50 @@
1
1
  require "dm-core"
2
+ require "dm-transactions"
3
+
4
+ #Datamapper 1.0 requires you to require dm-migrations to automigrate
5
+ require "dm-migrations"
2
6
 
3
7
  # only to please activerecord API used in database_cleaner/examples/features/step_definitions
4
8
  # yes, i know that's lazy ...
9
+
5
10
  require "dm-validations"
6
11
  require "dm-aggregates"
7
12
 
8
- DataMapper.setup(:default, "sqlite3::memory:")
13
+ DataMapper.setup(:default, "sqlite3:#{DB_DIR}/datamapper_default.db")
14
+ DataMapper.setup(:one, "sqlite3:#{DB_DIR}/datamapper_one.db")
15
+ DataMapper.setup(:two, "sqlite3:#{DB_DIR}/datamapper_two.db")
16
+
17
+ class DataMapperWidget
18
+ include DataMapper::Resource
19
+
20
+ property :id, Serial
21
+ property :name, String
22
+ end
9
23
 
10
- class Widget
24
+ class DataMapperWidgetUsingDatabaseOne
11
25
  include DataMapper::Resource
26
+
27
+ def self.default_repository_name
28
+ :one
29
+ end
30
+
12
31
  property :id, Serial
13
32
  property :name, String
33
+
34
+ end
35
+
36
+ class DataMapperWidgetUsingDatabaseTwo
37
+ include DataMapper::Resource
38
+
39
+ def self.default_repository_name
40
+ :two
41
+ end
42
+
43
+ property :id, Serial
44
+ property :name, String
45
+
14
46
  end
15
47
 
16
- Widget.auto_migrate!
48
+ DataMapperWidget.auto_migrate!
49
+ DataMapperWidgetUsingDatabaseOne.auto_migrate!
50
+ DataMapperWidgetUsingDatabaseTwo.auto_migrate!
@@ -9,15 +9,41 @@ end
9
9
  #::MongoMapper.connection = Mongo::Connection.new('127.0.0.1')
10
10
  #::MongoMapper.database = 'database_cleaner_test'
11
11
 
12
- class Widget
12
+ class MongoidWidget
13
13
  include Mongoid::Document
14
14
  field :id, :type => Integer
15
15
  field :name
16
16
 
17
17
  class << self
18
- #mongomapper doesn't seem to provide this...
18
+ #mongoid doesn't seem to provide this...
19
19
  def create!(*args)
20
20
  new(*args).save!
21
21
  end
22
22
  end
23
23
  end
24
+
25
+ class MongoidWidgetUsingDatabaseOne
26
+ include Mongoid::Document
27
+ field :id, :type => Integer
28
+ field :name
29
+
30
+ class << self
31
+ #mongoid doesn't seem to provide this...
32
+ def create!(*args)
33
+ new(*args).save!
34
+ end
35
+ end
36
+ end
37
+
38
+ class MongoidWidgetUsingDatabaseTwo
39
+ include Mongoid::Document
40
+ field :id, :type => Integer
41
+ field :name
42
+
43
+ class << self
44
+ #mongoid doesn't seem to provide this...
45
+ def create!(*args)
46
+ new(*args).save!
47
+ end
48
+ end
49
+ end
@@ -3,7 +3,7 @@ require 'mongo_mapper'
3
3
  ::MongoMapper.connection = Mongo::Connection.new('127.0.0.1')
4
4
  ::MongoMapper.database = 'database_cleaner_test'
5
5
 
6
- class Widget
6
+ class MongoMapperWidget
7
7
  include MongoMapper::Document
8
8
  key :id, Integer
9
9
  key :name, String
@@ -15,3 +15,37 @@ class Widget
15
15
  end
16
16
  end
17
17
  end
18
+
19
+ class MongoMapperWidgetUsingDatabaseOne
20
+ include MongoMapper::Document
21
+
22
+ connection = Mongo::Connection.new('127.0.0.1')
23
+ set_database_name = 'database_cleaner_test_one'
24
+
25
+ key :id, Integer
26
+ key :name, String
27
+
28
+ class << self
29
+ #mongomapper doesn't seem to provide this...
30
+ def create!(*args)
31
+ new(*args).save!
32
+ end
33
+ end
34
+ end
35
+
36
+ class MongoMapperWidgetUsingDatabaseTwo
37
+ include MongoMapper::Document
38
+
39
+ connection = Mongo::Connection.new('127.0.0.1')
40
+ set_database_name = 'database_cleaner_test_two'
41
+
42
+ key :id, Integer
43
+ key :name, String
44
+
45
+ class << self
46
+ #mongomapper doesn't seem to provide this...
47
+ def create!(*args)
48
+ new(*args).save!
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,20 @@
1
+ Feature: multiple database cleaning
2
+ In order to ease example and feature writing
3
+ As a developer
4
+ I want to have my databases 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 clean databases
11
+ Then I should see all green
12
+
13
+ Examples:
14
+ | ORM | Strategy |
15
+ | ActiveRecord | truncation |
16
+ | DataMapper | truncation |
17
+ | MongoMapper | truncation |
18
+ | DataMapper | transaction |
19
+ # Not working...
20
+ #| ActiveRecord | transaction |
@@ -0,0 +1,29 @@
1
+ Feature: database cleaning using multiple ORMs
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 <ORM1> and <ORM2>
8
+
9
+ When I run my scenarios that rely on clean databases using multiple orms
10
+ Then I should see all green
11
+
12
+ Examples:
13
+ | ORM1 | ORM2 |
14
+ | ActiveRecord | DataMapper |
15
+ | ActiveRecord | MongoMapper |
16
+ | ActiveRecord | Mongoid |
17
+ | ActiveRecord | CouchPotato |
18
+ | DataMapper | ActiveRecord |
19
+ | DataMapper | MongoMapper |
20
+ | DataMapper | Mongoid |
21
+ | DataMapper | CouchPotato |
22
+ | MongoMapper | ActiveRecord |
23
+ | MongoMapper | DataMapper |
24
+ | MongoMapper | Mongoid |
25
+ | MongoMapper | CouchPotato |
26
+ | CouchPotato | ActiveRecord |
27
+ | CouchPotato | DataMapper |
28
+ | CouchPotato | MongoMapper |
29
+ | CouchPotato | Mongoid |
@@ -1,25 +1,32 @@
1
+
1
2
  Given /^I am using (ActiveRecord|DataMapper|MongoMapper|Mongoid|CouchPotato)$/ do |orm|
2
- @orm = orm
3
+ @feature_runner = FeatureRunner.new
4
+ @feature_runner.orm = orm
5
+ end
6
+
7
+ Given /^I am using (ActiveRecord|DataMapper|MongoMapper|CouchPotato|Mongoid) and (ActiveRecord|DataMapper|MongoMapper|CouchPotato|Mongoid)$/ do |orm1,orm2|
8
+ @feature_runner = FeatureRunner.new
9
+ @feature_runner.orm = orm1
10
+ @feature_runner.another_orm = orm2
3
11
  end
4
12
 
5
13
  Given /^the (.+) cleaning strategy$/ do |strategy|
6
- @strategy = strategy
14
+ @feature_runner.strategy = strategy
7
15
  end
8
16
 
9
17
  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 = `#{"jruby -S " if defined?(JRUBY_VERSION)}cucumber features`
15
- @status = $?.exitstatus
16
- end
18
+ @feature_runner.go 'example'
17
19
  end
18
20
 
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
21
+ When "I run my scenarios that rely on clean databases" do
22
+ @feature_runner.multiple_databases = true
23
+ @feature_runner.go 'example_multiple_db'
23
24
  end
24
25
 
26
+ When "I run my scenarios that rely on clean databases using multiple orms" do
27
+ @feature_runner.go 'example_multiple_orm'
28
+ end
25
29
 
30
+ Then "I should see all green" do
31
+ fail "Feature failed with :#{@feature_runner.output}" unless @feature_runner.exit_status == 0
32
+ end
@@ -0,0 +1,39 @@
1
+ class FeatureRunner
2
+ attr_accessor :orm
3
+ attr_accessor :another_orm
4
+ attr_accessor :multiple_databases
5
+ attr_accessor :strategy
6
+ attr_accessor :exit_status
7
+ attr_accessor :output
8
+
9
+ def strategy
10
+ @strategy || 'truncation'
11
+ end
12
+
13
+ def go(feature)
14
+ full_dir ||= File.expand_path(File.dirname(__FILE__) + "/../../examples/")
15
+ Dir.chdir(full_dir) do
16
+
17
+
18
+ ENV['ORM'] = orm
19
+ ENV['STRATEGY'] = strategy
20
+
21
+ if another_orm
22
+ ENV['ANOTHER_ORM'] = another_orm
23
+ else
24
+ ENV['ANOTHER_ORM'] = nil
25
+ end
26
+
27
+ if multiple_databases
28
+ ENV['MULTIPLE_DBS'] = "true"
29
+ else
30
+ ENV['MULTIPLE_DBS'] = nil
31
+ end
32
+
33
+ self.output = `#{"jruby -S " if defined?(JRUBY_VERSION)}cucumber features/#{feature}.feature`
34
+
35
+ self.exit_status = $?.exitstatus
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,46 @@
1
+ require 'database_cleaner/generic/base'
2
+ require 'active_record'
3
+
4
+ module DatabaseCleaner
5
+ module ActiveRecord
6
+
7
+ def self.available_strategies
8
+ %w[truncation transaction]
9
+ end
10
+
11
+ def self.config_file_location
12
+ "#{DatabaseCleaner.app_root}/config/database.yml"
13
+ end
14
+
15
+ module Base
16
+ include ::DatabaseCleaner::Generic::Base
17
+
18
+ attr_accessor :connection_hash
19
+
20
+ def db=(desired_db)
21
+ @db = desired_db
22
+ load_config
23
+ end
24
+
25
+ def db
26
+ @db || super
27
+ end
28
+
29
+ def load_config
30
+ connection_details = YAML::load(ERB.new(IO.read(ActiveRecord.config_file_location)).result)
31
+ self.connection_hash = connection_details[self.db.to_s]
32
+ end
33
+
34
+ def create_connection_klass
35
+ Class.new(::ActiveRecord::Base)
36
+ end
37
+
38
+ def connection_klass
39
+ return ::ActiveRecord::Base if connection_hash.nil?
40
+ klass = create_connection_klass
41
+ klass.send :establish_connection, connection_hash
42
+ klass
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,26 +1,26 @@
1
+ require 'database_cleaner/active_record/base'
1
2
  module DatabaseCleaner::ActiveRecord
2
3
  class Transaction
4
+ include ::DatabaseCleaner::ActiveRecord::Base
3
5
 
4
6
  def start
5
- if ActiveRecord::Base.connection.respond_to?(:increment_open_transactions)
6
- ActiveRecord::Base.connection.increment_open_transactions
7
+ if connection_klass.connection.respond_to?(:increment_open_transactions)
8
+ connection_klass.connection.increment_open_transactions
7
9
  else
8
- ActiveRecord::Base.__send__(:increment_open_transactions)
10
+ connection_klass.__send__(:increment_open_transactions)
9
11
  end
10
-
11
- ActiveRecord::Base.connection.begin_db_transaction
12
+ connection_klass.connection.begin_db_transaction
12
13
  end
13
14
 
14
15
 
15
16
  def clean
16
- ActiveRecord::Base.connection.rollback_db_transaction
17
+ connection_klass.connection.rollback_db_transaction
17
18
 
18
- if ActiveRecord::Base.connection.respond_to?(:decrement_open_transactions)
19
- ActiveRecord::Base.connection.decrement_open_transactions
19
+ if connection_klass.connection.respond_to?(:decrement_open_transactions)
20
+ connection_klass.connection.decrement_open_transactions
20
21
  else
21
- ActiveRecord::Base.__send__(:decrement_open_transactions)
22
+ connection_klass.__send__(:decrement_open_transactions)
22
23
  end
23
24
  end
24
25
  end
25
-
26
26
  end
@@ -1,6 +1,7 @@
1
1
  require 'active_record/base'
2
2
  require 'active_record/connection_adapters/abstract_adapter'
3
- require "database_cleaner/truncation_base"
3
+ require "database_cleaner/generic/truncation"
4
+ require 'database_cleaner/active_record/base'
4
5
 
5
6
  module ActiveRecord
6
7
  module ConnectionAdapters
@@ -17,6 +18,12 @@ module ActiveRecord
17
18
  end
18
19
  end
19
20
 
21
+ class Mysql2Adapter < AbstractAdapter
22
+ def truncate_table(table_name)
23
+ execute("TRUNCATE TABLE #{quote_table_name(table_name)};")
24
+ end
25
+ end
26
+
20
27
  class SQLite3Adapter < SQLiteAdapter
21
28
  def truncate_table(table_name)
22
29
  execute("DELETE FROM #{quote_table_name(table_name)};")
@@ -36,9 +43,9 @@ module ActiveRecord
36
43
  class PostgreSQLAdapter < AbstractAdapter
37
44
 
38
45
  def self.db_version
39
- @db_version ||= ActiveRecord::Base.connection.select_values(
40
- "SELECT CHARACTER_VALUE
41
- FROM INFORMATION_SCHEMA.SQL_IMPLEMENTATION_INFO
46
+ @db_version ||= connection.select_values(
47
+ "SELECT CHARACTER_VALUE
48
+ FROM INFORMATION_SCHEMA.SQL_IMPLEMENTATION_INFO
42
49
  WHERE IMPLEMENTATION_INFO_NAME = 'DBMS VERSION' ").to_s
43
50
  end
44
51
 
@@ -69,7 +76,9 @@ end
69
76
 
70
77
 
71
78
  module DatabaseCleaner::ActiveRecord
72
- class Truncation < ::DatabaseCleaner::TruncationBase
79
+ class Truncation
80
+ include ::DatabaseCleaner::ActiveRecord::Base
81
+ include ::DatabaseCleaner::Generic::Truncation
73
82
 
74
83
  def clean
75
84
  connection.disable_referential_integrity do
@@ -82,11 +91,12 @@ module DatabaseCleaner::ActiveRecord
82
91
  private
83
92
 
84
93
  def tables_to_truncate
85
- (@only || connection.tables) - @tables_to_exclude
94
+ (@only || connection.tables) - @tables_to_exclude
86
95
  end
87
96
 
88
97
  def connection
89
- ::ActiveRecord::Base.connection
98
+ #::ActiveRecord::Base.connection
99
+ connection_klass.connection
90
100
  end
91
101
 
92
102
  # overwritten
@@ -0,0 +1,129 @@
1
+ module DatabaseCleaner
2
+ class Base
3
+
4
+ def initialize(desired_orm = nil,opts = {})
5
+ if desired_orm == :autodetect || desired_orm.nil?
6
+ autodetect
7
+ else
8
+ self.orm = desired_orm
9
+ end
10
+ self.db = opts[:connection] if opts.has_key? :connection
11
+ end
12
+
13
+ def db=(desired_db)
14
+ self.strategy_db = desired_db
15
+ @db = desired_db
16
+ end
17
+
18
+ def strategy_db=(desired_db)
19
+ if strategy.respond_to? :db=
20
+ strategy.db = desired_db
21
+ elsif desired_db!= :default
22
+ raise ArgumentError, "You must provide a strategy object that supports non default databases when you specify a database"
23
+ end
24
+ rescue NoStrategySetError
25
+ #handle NoStrategySetError by doing nothing at all
26
+ desired_db
27
+ end
28
+
29
+ def db
30
+ @db || :default
31
+ end
32
+
33
+ def create_strategy(*args)
34
+ strategy, *strategy_args = args
35
+ orm_strategy(strategy).new(*strategy_args)
36
+ end
37
+
38
+ def clean_with(*args)
39
+ strategy = create_strategy(*args)
40
+ strategy.clean
41
+ strategy
42
+ end
43
+
44
+ alias clean_with! clean_with
45
+
46
+ def strategy=(args)
47
+ strategy, *strategy_args = args
48
+ if strategy.is_a?(Symbol)
49
+ @strategy = create_strategy(*args)
50
+ elsif strategy_args.empty?
51
+ @strategy = strategy
52
+ else
53
+ raise ArgumentError, "You must provide a strategy object, or a symbol for a known strategy along with initialization params."
54
+ end
55
+
56
+ self.strategy_db = self.db
57
+
58
+ @strategy
59
+ end
60
+
61
+ def strategy
62
+ return @strategy if @strategy
63
+ raise NoStrategySetError, "Please set a strategy with DatabaseCleaner.strategy=."
64
+ end
65
+
66
+ def orm=(desired_orm)
67
+ @orm = desired_orm
68
+ end
69
+
70
+ def orm
71
+ @orm || autodetect
72
+ end
73
+
74
+ def start
75
+ strategy.start
76
+ end
77
+
78
+ def clean
79
+ strategy.clean
80
+ end
81
+
82
+ alias clean! clean
83
+
84
+ def auto_detected?
85
+ return true unless @autodetected.nil?
86
+ end
87
+
88
+ #TODO make strategies directly comparable
89
+ def ==(other)
90
+ self.orm == other.orm && self.db == other.db && self.strategy.class == other.strategy.class
91
+ end
92
+
93
+ private
94
+
95
+ def orm_module
96
+ ::DatabaseCleaner.orm_module(orm)
97
+ end
98
+
99
+ def orm_strategy(strategy)
100
+ require "database_cleaner/#{orm.to_s}/#{strategy.to_s}"
101
+ orm_module.const_get(strategy.to_s.capitalize)
102
+ rescue LoadError => e
103
+ if orm_module.respond_to? :available_strategies
104
+ raise UnknownStrategySpecified, "The '#{strategy}' strategy does not exist for the #{orm} ORM! Available strategies: #{orm_module.available_strategies.join(', ')}"
105
+ else
106
+ raise UnknownStrategySpecified, "The '#{strategy}' strategy does not exist for the #{orm} ORM!"
107
+ end
108
+ end
109
+
110
+ def autodetect
111
+ @orm ||= begin
112
+ @autodetected = true
113
+ if defined? ::ActiveRecord
114
+ :active_record
115
+ elsif defined? ::DataMapper
116
+ :data_mapper
117
+ elsif defined? ::MongoMapper
118
+ :mongo_mapper
119
+ elsif defined? ::Mongoid
120
+ :mongoid
121
+ elsif defined? ::CouchPotato
122
+ :couch_potato
123
+ else
124
+ raise NoORMDetected, "No known ORM was detected! Is ActiveRecord, DataMapper, MongoMapper, Mongoid, or CouchPotato loaded?"
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end