surgical_strike 0.7.4

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