surgical_strike 0.7.4

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 (81) hide show
  1. data/Gemfile.lock +163 -0
  2. data/History.txt +210 -0
  3. data/LICENSE +20 -0
  4. data/README.textile +229 -0
  5. data/TODO +3 -0
  6. data/VERSION.yml +5 -0
  7. data/cucumber.yml +1 -0
  8. data/examples/Gemfile +1 -0
  9. data/examples/Gemfile.lock +1 -0
  10. data/examples/config/database.yml.example +8 -0
  11. data/examples/db/sqlite_databases_go_here +0 -0
  12. data/examples/features/example.feature +11 -0
  13. data/examples/features/example_multiple_db.feature +23 -0
  14. data/examples/features/example_multiple_orm.feature +22 -0
  15. data/examples/features/step_definitions/activerecord_steps.rb +31 -0
  16. data/examples/features/step_definitions/couchpotato_steps.rb +31 -0
  17. data/examples/features/step_definitions/datamapper_steps.rb +37 -0
  18. data/examples/features/step_definitions/mongoid_steps.rb +23 -0
  19. data/examples/features/step_definitions/mongomapper_steps.rb +31 -0
  20. data/examples/features/step_definitions/translation_steps.rb +55 -0
  21. data/examples/features/support/env.rb +62 -0
  22. data/examples/lib/activerecord_models.rb +41 -0
  23. data/examples/lib/couchpotato_models.rb +61 -0
  24. data/examples/lib/datamapper_models.rb +50 -0
  25. data/examples/lib/mongoid_models.rb +49 -0
  26. data/examples/lib/mongomapper_models.rb +51 -0
  27. data/features/cleaning.feature +22 -0
  28. data/features/cleaning_default_strategy.feature +19 -0
  29. data/features/cleaning_multiple_dbs.feature +21 -0
  30. data/features/cleaning_multiple_orms.feature +29 -0
  31. data/features/step_definitions/database_cleaner_steps.rb +32 -0
  32. data/features/support/env.rb +7 -0
  33. data/features/support/feature_runner.rb +39 -0
  34. data/lib/database_cleaner/active_record/base.rb +53 -0
  35. data/lib/database_cleaner/active_record/deletion.rb +67 -0
  36. data/lib/database_cleaner/active_record/surgicalstrike.rb +65 -0
  37. data/lib/database_cleaner/active_record/transaction.rb +28 -0
  38. data/lib/database_cleaner/active_record/truncation.rb +152 -0
  39. data/lib/database_cleaner/base.rb +138 -0
  40. data/lib/database_cleaner/configuration.rb +94 -0
  41. data/lib/database_cleaner/couch_potato/base.rb +7 -0
  42. data/lib/database_cleaner/couch_potato/truncation.rb +28 -0
  43. data/lib/database_cleaner/cucumber.rb +11 -0
  44. data/lib/database_cleaner/data_mapper/base.rb +21 -0
  45. data/lib/database_cleaner/data_mapper/transaction.rb +26 -0
  46. data/lib/database_cleaner/data_mapper/truncation.rb +175 -0
  47. data/lib/database_cleaner/generic/base.rb +12 -0
  48. data/lib/database_cleaner/generic/surgicalstrike.rb +47 -0
  49. data/lib/database_cleaner/generic/truncation.rb +37 -0
  50. data/lib/database_cleaner/mongo/truncation.rb +22 -0
  51. data/lib/database_cleaner/mongo_mapper/base.rb +20 -0
  52. data/lib/database_cleaner/mongo_mapper/truncation.rb +19 -0
  53. data/lib/database_cleaner/mongoid/base.rb +20 -0
  54. data/lib/database_cleaner/mongoid/truncation.rb +20 -0
  55. data/lib/database_cleaner/null_strategy.rb +15 -0
  56. data/lib/database_cleaner/sequel/base.rb +22 -0
  57. data/lib/database_cleaner/sequel/transaction.rb +25 -0
  58. data/lib/database_cleaner/sequel/truncation.rb +50 -0
  59. data/lib/surgical_strike.rb +3 -0
  60. data/spec/database_cleaner/active_record/base_spec.rb +144 -0
  61. data/spec/database_cleaner/active_record/transaction_spec.rb +77 -0
  62. data/spec/database_cleaner/active_record/truncation_spec.rb +76 -0
  63. data/spec/database_cleaner/base_spec.rb +493 -0
  64. data/spec/database_cleaner/configuration_spec.rb +294 -0
  65. data/spec/database_cleaner/couch_potato/truncation_spec.rb +41 -0
  66. data/spec/database_cleaner/data_mapper/base_spec.rb +30 -0
  67. data/spec/database_cleaner/data_mapper/transaction_spec.rb +23 -0
  68. data/spec/database_cleaner/data_mapper/truncation_spec.rb +11 -0
  69. data/spec/database_cleaner/generic/base_spec.rb +22 -0
  70. data/spec/database_cleaner/generic/truncation_spec.rb +78 -0
  71. data/spec/database_cleaner/mongo_mapper/base_spec.rb +33 -0
  72. data/spec/database_cleaner/mongo_mapper/mongo_examples.rb +8 -0
  73. data/spec/database_cleaner/mongo_mapper/truncation_spec.rb +74 -0
  74. data/spec/database_cleaner/sequel/base_spec.rb +32 -0
  75. data/spec/database_cleaner/sequel/transaction_spec.rb +21 -0
  76. data/spec/database_cleaner/sequel/truncation_spec.rb +13 -0
  77. data/spec/database_cleaner/shared_strategy_spec.rb +13 -0
  78. data/spec/rcov.opts +1 -0
  79. data/spec/spec.opts +7 -0
  80. data/spec/spec_helper.rb +19 -0
  81. metadata +162 -0
@@ -0,0 +1,21 @@
1
+ require 'database_cleaner/generic/base'
2
+ module DatabaseCleaner
3
+ module DataMapper
4
+ def self.available_strategies
5
+ %w[truncation transaction]
6
+ end
7
+
8
+ module Base
9
+ include ::DatabaseCleaner::Generic::Base
10
+
11
+ def db=(desired_db)
12
+ @db = desired_db
13
+ end
14
+
15
+ def db
16
+ @db || :default
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,26 @@
1
+ require 'database_cleaner/data_mapper/base'
2
+
3
+ module DatabaseCleaner::DataMapper
4
+ class Transaction
5
+ include ::DatabaseCleaner::DataMapper::Base
6
+
7
+ def start(repository = self.db)
8
+ ::DataMapper.repository(repository) do |r|
9
+ transaction = DataMapper::Transaction.new(r)
10
+ transaction.begin
11
+ r.adapter.push_transaction(transaction)
12
+ end
13
+ end
14
+
15
+ def clean(repository = self.db)
16
+ ::DataMapper.repository(repository) do |r|
17
+ adapter = r.adapter
18
+ while adapter.current_transaction
19
+ adapter.current_transaction.rollback
20
+ adapter.pop_transaction
21
+ end
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,175 @@
1
+ require "database_cleaner/generic/truncation"
2
+ require 'database_cleaner/data_mapper/base'
3
+
4
+ module DataMapper
5
+ module Adapters
6
+
7
+ class DataObjectsAdapter
8
+
9
+ def storage_names(repository = :default)
10
+ raise NotImplementedError
11
+ end
12
+
13
+ end
14
+
15
+ class MysqlAdapter < DataObjectsAdapter
16
+
17
+ # taken from http://github.com/godfat/dm-mapping/tree/master
18
+ def storage_names(repository = :default)
19
+ select 'SHOW TABLES'
20
+ end
21
+
22
+ def truncate_table(table_name)
23
+ execute("TRUNCATE TABLE #{quote_name(table_name)};")
24
+ end
25
+
26
+ # copied from activerecord
27
+ def disable_referential_integrity
28
+ old = select("SELECT @@FOREIGN_KEY_CHECKS;")
29
+ begin
30
+ execute("SET FOREIGN_KEY_CHECKS = 0;")
31
+ yield
32
+ ensure
33
+ execute("SET FOREIGN_KEY_CHECKS = ?", *old)
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+
40
+ class Sqlite3Adapter < DataObjectsAdapter
41
+
42
+ # taken from http://github.com/godfat/dm-mapping/tree/master
43
+ def storage_names(repository = :default)
44
+ # activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 177
45
+ sql = <<-SQL
46
+ SELECT name
47
+ FROM sqlite_master
48
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
49
+ SQL
50
+ # activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 181
51
+ select(sql)
52
+ end
53
+
54
+ def truncate_table(table_name)
55
+ execute("DELETE FROM #{quote_name(table_name)};")
56
+ execute("DELETE FROM sqlite_sequence where name = '#{table_name}';")
57
+ end
58
+
59
+ # this is a no-op copied from activerecord
60
+ # i didn't find out if/how this is possible
61
+ # activerecord also doesn't do more here
62
+ def disable_referential_integrity
63
+ yield
64
+ end
65
+
66
+ end
67
+
68
+ class SqliteAdapter < DataObjectsAdapter
69
+ # taken from http://github.com/godfat/dm-mapping/tree/master
70
+ def storage_names(repository = :default)
71
+ # activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 177
72
+ sql = <<-SQL
73
+ SELECT name
74
+ FROM sqlite_master
75
+ WHERE type = 'table' AND NOT name = 'sqlite_sequence'
76
+ SQL
77
+ # activerecord-2.1.0/lib/active_record/connection_adapters/sqlite_adapter.rb: 181
78
+ select(sql)
79
+ end
80
+
81
+ def truncate_table(table_name)
82
+ execute("DELETE FROM #{quote_name(table_name)};")
83
+ execute("DELETE FROM sqlite_sequence where name = '#{table_name}';")
84
+ end
85
+
86
+ # this is a no-op copied from activerecord
87
+ # i didn't find out if/how this is possible
88
+ # activerecord also doesn't do more here
89
+ def disable_referential_integrity
90
+ yield
91
+ end
92
+
93
+ end
94
+
95
+ # FIXME
96
+ # i don't know if this works
97
+ # i basically just copied activerecord code to get a rough idea what they do.
98
+ # i don't have postgres available, so i won't be the one to write this.
99
+ # maybe codes below gets some postgres/datamapper user going, though.
100
+ class PostgresAdapter < DataObjectsAdapter
101
+
102
+ # taken from http://github.com/godfat/dm-mapping/tree/master
103
+ def storage_names(repository = :default)
104
+ sql = <<-SQL
105
+ SELECT table_name FROM "information_schema"."tables"
106
+ WHERE table_schema = current_schema() and table_type = 'BASE TABLE'
107
+ SQL
108
+ select(sql)
109
+ end
110
+
111
+ def truncate_table(table_name)
112
+ execute("TRUNCATE TABLE #{quote_name(table_name)} RESTART IDENTITY CASCADE;")
113
+ end
114
+
115
+ # FIXME
116
+ # copied from activerecord
117
+ def supports_disable_referential_integrity?
118
+ version = select("SHOW server_version")[0][0].split('.')
119
+ (version[0].to_i >= 8 && version[1].to_i >= 1) ? true : false
120
+ rescue
121
+ return false
122
+ end
123
+
124
+ # FIXME
125
+ # copied unchanged from activerecord
126
+ def disable_referential_integrity(repository = :default)
127
+ if supports_disable_referential_integrity? then
128
+ execute(storage_names(repository).collect do |name|
129
+ "ALTER TABLE #{quote_name(name)} DISABLE TRIGGER ALL"
130
+ end.join(";"))
131
+ end
132
+ yield
133
+ ensure
134
+ if supports_disable_referential_integrity? then
135
+ execute(storage_names(repository).collect do |name|
136
+ "ALTER TABLE #{quote_name(name)} ENABLE TRIGGER ALL"
137
+ end.join(";"))
138
+ end
139
+ end
140
+
141
+ end
142
+
143
+ end
144
+ end
145
+
146
+
147
+ module DatabaseCleaner
148
+ module DataMapper
149
+ class Truncation
150
+ include ::DatabaseCleaner::DataMapper::Base
151
+ include ::DatabaseCleaner::Generic::Truncation
152
+
153
+ def clean(repository = self.db)
154
+ adapter = ::DataMapper.repository(repository).adapter
155
+ adapter.disable_referential_integrity do
156
+ tables_to_truncate(repository).each do |table_name|
157
+ adapter.truncate_table table_name
158
+ end
159
+ end
160
+ end
161
+
162
+ private
163
+
164
+ def tables_to_truncate(repository = self.db)
165
+ (@only || ::DataMapper.repository(repository).adapter.storage_names(repository)) - @tables_to_exclude
166
+ end
167
+
168
+ # overwritten
169
+ def migration_storage_name
170
+ 'migration_info'
171
+ end
172
+
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,12 @@
1
+ require 'active_record'
2
+ require 'database_cleaner/active_record/base'
3
+ require 'erb'
4
+
5
+ module DatabaseCleaner
6
+ module ActiveRecord
7
+
8
+ def self.available_strategies
9
+ %w[truncation transaction deletion surgicalstrike]
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,47 @@
1
+ require "database_cleaner"
2
+
3
+ module DatabaseCleaner
4
+ module Generic
5
+ module Dataholder
6
+ def checktarget(target)
7
+ if ($strike_targets.include?(target.class.table_name.upcase))
8
+ $vips.push(target)
9
+ end
10
+ end
11
+ end
12
+ module Surgicalstrike
13
+ def initialize(opts={})
14
+ if !opts.empty? && !(opts.keys - [:only, :except]).empty?
15
+ raise ArgumentError, "The only valid options are :only and :except. You specified #{opts.keys.join(',')}."
16
+ end
17
+ if opts.has_key?(:only) && opts.has_key?(:except)
18
+ raise ArgumentError, "You may only specify either :only or :except. Doing both doesn't really make sense does it?"
19
+ end
20
+
21
+ @only = opts[:only]
22
+ @tables_to_exclude = (opts[:except] || []).dup
23
+ @tables_to_exclude << migration_storage_name if migration_storage_name
24
+ end
25
+
26
+ def start
27
+ raise NotImplementedError
28
+ end
29
+
30
+ def clean
31
+ raise NotImplementedError
32
+ end
33
+
34
+ private
35
+
36
+ def tables_to_strike(connection)
37
+ raise NotImplementedError
38
+ end
39
+
40
+ # overwrite in subclasses
41
+ # default implementation given because migration storage need not be present
42
+ def migration_storage_name
43
+ nil
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,37 @@
1
+ module DatabaseCleaner
2
+ module Generic
3
+ module Truncation
4
+ def initialize(opts={})
5
+ if !opts.empty? && !(opts.keys - [:only, :except]).empty?
6
+ raise ArgumentError, "The only valid options are :only and :except. You specified #{opts.keys.join(',')}."
7
+ end
8
+ if opts.has_key?(:only) && opts.has_key?(:except)
9
+ raise ArgumentError, "You may only specify either :only or :except. Doing both doesn't really make sense does it?"
10
+ end
11
+
12
+ @only = opts[:only]
13
+ @tables_to_exclude = (opts[:except] || []).dup
14
+ @tables_to_exclude << migration_storage_name if migration_storage_name
15
+ end
16
+
17
+ def start
18
+ #included for compatability reasons, do nothing if you don't need to
19
+ end
20
+
21
+ def clean
22
+ raise NotImplementedError
23
+ end
24
+
25
+ private
26
+ def tables_to_truncate
27
+ raise NotImplementedError
28
+ end
29
+
30
+ # overwrite in subclasses
31
+ # default implementation given because migration storage need not be present
32
+ def migration_storage_name
33
+ nil
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,22 @@
1
+ module DatabaseCleaner
2
+ module Mongo
3
+ module Truncation
4
+
5
+ def clean
6
+ if @only
7
+ collections.each { |c| c.remove if @only.include?(c.name) }
8
+ else
9
+ collections.each { |c| c.remove unless @tables_to_exclude.include?(c.name) }
10
+ end
11
+ true
12
+ end
13
+
14
+ private
15
+
16
+ def collections
17
+ database.collections.select { |c| c.name !~ /^system\./ }
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ require 'database_cleaner/generic/base'
2
+ module DatabaseCleaner
3
+ module MongoMapper
4
+ def self.available_strategies
5
+ %w[truncation]
6
+ end
7
+
8
+ module Base
9
+ include ::DatabaseCleaner::Generic::Base
10
+
11
+ def db=(desired_db)
12
+ @db = desired_db
13
+ end
14
+
15
+ def db
16
+ @db || :default
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,19 @@
1
+ require 'database_cleaner/mongo_mapper/base'
2
+ require 'database_cleaner/generic/truncation'
3
+ require 'database_cleaner/mongo/truncation'
4
+
5
+ module DatabaseCleaner
6
+ module MongoMapper
7
+ class Truncation
8
+ include ::DatabaseCleaner::MongoMapper::Base
9
+ include ::DatabaseCleaner::Generic::Truncation
10
+ include ::DatabaseCleaner::Mongo::Truncation
11
+
12
+ private
13
+
14
+ def database
15
+ ::MongoMapper.database
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ require 'database_cleaner/generic/base'
2
+ module DatabaseCleaner
3
+ module Mongoid
4
+ def self.available_strategies
5
+ %w[truncation]
6
+ end
7
+
8
+ module Base
9
+ include ::DatabaseCleaner::Generic::Base
10
+
11
+ def db=(desired_db)
12
+ @db = desired_db
13
+ end
14
+
15
+ def db
16
+ @db || :default
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ require 'database_cleaner/mongoid/base'
2
+ require 'database_cleaner/generic/truncation'
3
+ require 'database_cleaner/mongo/truncation'
4
+
5
+ module DatabaseCleaner
6
+ module Mongoid
7
+ class Truncation
8
+ include ::DatabaseCleaner::Mongoid::Base
9
+ include ::DatabaseCleaner::Generic::Truncation
10
+ include ::DatabaseCleaner::Mongo::Truncation
11
+
12
+ private
13
+
14
+ def database
15
+ ::Mongoid.database
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ module DatabaseCleaner
2
+ class NullStrategy
3
+ def self.start
4
+ # no-op
5
+ end
6
+
7
+ def self.db=(connection)
8
+ # no-op
9
+ end
10
+
11
+ def self.clean
12
+ # no-op
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ require 'database_cleaner/generic/base'
2
+ module DatabaseCleaner
3
+ module Sequel
4
+ def self.available_strategies
5
+ %w[truncation transaction]
6
+ end
7
+
8
+ module Base
9
+ include ::DatabaseCleaner::Generic::Base
10
+
11
+ def db=(desired_db)
12
+ @db = desired_db
13
+ end
14
+
15
+ def db
16
+ return @db if @db && @db != :default
17
+ raise "As you have more than one active sequel database you have to specify the one to use manually!" if ::Sequel::DATABASES.count > 1
18
+ ::Sequel::DATABASES.first || :default
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ require 'database_cleaner/sequel/base'
2
+ module DatabaseCleaner
3
+ module Sequel
4
+ class Transaction
5
+ include ::DatabaseCleaner::Sequel::Base
6
+
7
+ def start
8
+ @fibers||= []
9
+ db= self.db
10
+ f= Fiber.new do
11
+ db.transaction(:rollback => :always, :savepoint => true) do
12
+ Fiber.yield
13
+ end
14
+ end
15
+ f.resume
16
+ @fibers<< f
17
+ end
18
+
19
+ def clean
20
+ f= @fibers.pop
21
+ f.resume
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,50 @@
1
+ require "database_cleaner/generic/truncation"
2
+ require 'database_cleaner/sequel/base'
3
+
4
+ module DatabaseCleaner
5
+ module Sequel
6
+ class Truncation
7
+ include ::DatabaseCleaner::Sequel::Base
8
+ include ::DatabaseCleaner::Generic::Truncation
9
+
10
+ def clean
11
+ case db_type= db.url.sub(/:.+/,'').to_sym
12
+ when :postgres
13
+ # PostgreSQL requires all tables with FKs to be truncates in the same command, or have the CASCADE keyword
14
+ # appended. Bulk truncation without CASCADE is:
15
+ # * Safer. Tables outside of tables_to_truncate won't be affected.
16
+ # * Faster. Less roundtrips to the db.
17
+ unless (tables= tables_to_truncate(db)).empty?
18
+ all_tables= tables.map{|t| %["#{t}"]}.join ','
19
+ db.run "TRUNCATE TABLE #{all_tables};"
20
+ end
21
+ else
22
+ # Truncate each table normally
23
+ each_table do |db, table|
24
+ db[table].truncate
25
+ end
26
+ end
27
+ end
28
+
29
+ def each_table
30
+ tables_to_truncate(db).each do |table|
31
+ yield db, table
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def tables_to_truncate(db)
38
+ (@only || db.tables) - @tables_to_exclude
39
+ end
40
+
41
+ # overwritten
42
+ def migration_storage_name
43
+ :schema_info
44
+ end
45
+
46
+ end
47
+ end
48
+ end
49
+
50
+
@@ -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,144 @@
1
+ require 'spec_helper'
2
+ require 'active_record'
3
+ require 'database_cleaner/active_record/base'
4
+ require 'database_cleaner/shared_strategy_spec'
5
+
6
+ module DatabaseCleaner
7
+ describe ActiveRecord do
8
+ it { should respond_to(:available_strategies) }
9
+
10
+ describe "config_file_location" do
11
+ subject { ActiveRecord.config_file_location }
12
+
13
+ it "should default to DatabaseCleaner.root / config / database.yml" do
14
+ ActiveRecord.config_file_location=nil
15
+ DatabaseCleaner.should_receive(:app_root).and_return("/path/to")
16
+ subject.should == '/path/to/config/database.yml'
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ module ActiveRecord
23
+ class ExampleStrategy
24
+ include ::DatabaseCleaner::ActiveRecord::Base
25
+ end
26
+
27
+ describe ExampleStrategy do
28
+ let :config_location do
29
+ '/path/to/config/database.yml'
30
+ end
31
+
32
+ before { ::DatabaseCleaner::ActiveRecord.stub(:config_file_location).and_return(config_location) }
33
+
34
+ it_should_behave_like "a generic strategy"
35
+
36
+ describe "db" do
37
+
38
+ it "should store my desired db" do
39
+ subject.stub(:load_config)
40
+
41
+ subject.db = :my_db
42
+ subject.db.should == :my_db
43
+ end
44
+
45
+ it "should default to :default" do
46
+ subject.db.should == :default
47
+ end
48
+
49
+ it "should load_config when I set db" do
50
+ subject.should_receive(:load_config)
51
+ subject.db = :my_db
52
+ end
53
+ end
54
+
55
+ describe "load_config" do
56
+
57
+ before do
58
+ subject.db = :my_db
59
+ yaml = <<-Y
60
+ my_db:
61
+ database: <%= "ONE".downcase %>
62
+ Y
63
+ File.stub(:file?).with(config_location).and_return(true)
64
+ IO.stub(:read).with(config_location).and_return(yaml)
65
+ end
66
+
67
+ it "should parse the config" do
68
+ YAML.should_receive(:load).and_return( {:nil => nil} )
69
+ subject.load_config
70
+ end
71
+
72
+ it "should process erb in the config" do
73
+ transformed = <<-Y
74
+ my_db:
75
+ database: one
76
+ Y
77
+ YAML.should_receive(:load).with(transformed).and_return({ "my_db" => {"database" => "one"} })
78
+ subject.load_config
79
+ end
80
+
81
+ it "should store the relevant config in connection_hash" do
82
+ subject.load_config
83
+ subject.connection_hash.should == {"database" => "one"}
84
+ end
85
+
86
+ it "should skip config if config file is not available" do
87
+ File.should_receive(:file?).with(config_location).and_return(false)
88
+ subject.load_config
89
+ subject.connection_hash.should be_blank
90
+ end
91
+
92
+ it "skips the file when the db is set to :default" do
93
+ # to avoid https://github.com/bmabey/database_cleaner/issues/72
94
+ subject.db = :default
95
+ YAML.should_not_receive(:load)
96
+ subject.load_config
97
+ end
98
+
99
+ end
100
+
101
+ describe "connection_hash" do
102
+ it "should store connection_hash" do
103
+ subject.connection_hash = { :key => "value" }
104
+ subject.connection_hash.should == { :key => "value" }
105
+ end
106
+ end
107
+
108
+ describe "create_connection_klass" do
109
+ it "should return a class" do
110
+ subject.create_connection_klass.should be_a(Class)
111
+ end
112
+
113
+ it "should return a class extending ::ActiveRecord::Base" do
114
+ subject.create_connection_klass.ancestors.should include(::ActiveRecord::Base)
115
+ end
116
+ end
117
+
118
+ describe "connection_klass" do
119
+ it { expect{ subject.connection_klass }.to_not raise_error }
120
+ it "should default to ActiveRecord::Base" do
121
+ subject.connection_klass.should == ::ActiveRecord::Base
122
+ end
123
+
124
+ context "when connection_hash is set" do
125
+ let(:hash) { mock("hash") }
126
+ before { subject.stub(:connection_hash).and_return(hash) }
127
+
128
+ it "should create connection_klass if it doesnt exist if connection_hash is set" do
129
+ subject.should_receive(:create_connection_klass).and_return(mock('class').as_null_object)
130
+ subject.connection_klass
131
+ end
132
+
133
+ it "should configure the class from create_connection_klass if connection_hash is set" do
134
+ klass = mock('klass')
135
+ klass.should_receive(:establish_connection).with(hash)
136
+
137
+ subject.should_receive(:create_connection_klass).and_return(klass)
138
+ subject.connection_klass
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end