database_cleaner 0.5.2 → 0.6.0.rc.1

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