activerecord 3.0.0 → 3.0.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (46) hide show
  1. data/CHANGELOG +42 -0
  2. data/examples/performance.rb +18 -1
  3. data/lib/active_record.rb +3 -3
  4. data/lib/active_record/aggregations.rb +2 -2
  5. data/lib/active_record/association_preload.rb +1 -1
  6. data/lib/active_record/associations.rb +59 -26
  7. data/lib/active_record/associations/association_collection.rb +28 -18
  8. data/lib/active_record/associations/association_proxy.rb +4 -4
  9. data/lib/active_record/associations/belongs_to_association.rb +3 -3
  10. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +10 -13
  11. data/lib/active_record/associations/has_many_through_association.rb +2 -3
  12. data/lib/active_record/associations/has_one_association.rb +6 -6
  13. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  14. data/lib/active_record/attribute_methods/before_type_cast.rb +1 -4
  15. data/lib/active_record/attribute_methods/primary_key.rb +4 -3
  16. data/lib/active_record/autosave_association.rb +7 -7
  17. data/lib/active_record/base.rb +71 -47
  18. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +6 -8
  19. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +2 -2
  20. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +5 -9
  21. data/lib/active_record/connection_adapters/mysql_adapter.rb +7 -7
  22. data/lib/active_record/connection_adapters/sqlite_adapter.rb +2 -2
  23. data/lib/active_record/dynamic_finder_match.rb +20 -17
  24. data/lib/active_record/dynamic_scope_match.rb +6 -15
  25. data/lib/active_record/fixtures.rb +3 -5
  26. data/lib/active_record/locking/optimistic.rb +1 -1
  27. data/lib/active_record/locking/pessimistic.rb +4 -4
  28. data/lib/active_record/nested_attributes.rb +17 -13
  29. data/lib/active_record/persistence.rb +7 -8
  30. data/lib/active_record/railties/databases.rake +7 -7
  31. data/lib/active_record/relation.rb +16 -18
  32. data/lib/active_record/relation/batches.rb +1 -1
  33. data/lib/active_record/relation/calculations.rb +37 -28
  34. data/lib/active_record/relation/finder_methods.rb +19 -19
  35. data/lib/active_record/relation/predicate_builder.rb +8 -1
  36. data/lib/active_record/relation/query_methods.rb +100 -75
  37. data/lib/active_record/relation/spawn_methods.rb +50 -39
  38. data/lib/active_record/serialization.rb +1 -1
  39. data/lib/active_record/session_store.rb +4 -4
  40. data/lib/active_record/transactions.rb +6 -6
  41. data/lib/active_record/validations.rb +1 -1
  42. data/lib/active_record/validations/uniqueness.rb +6 -1
  43. data/lib/active_record/version.rb +2 -2
  44. data/lib/rails/generators/active_record.rb +2 -10
  45. data/lib/rails/generators/active_record/migration.rb +15 -0
  46. metadata +15 -14
@@ -75,10 +75,7 @@ module ActiveRecord
75
75
  @queue = @connection_mutex.new_cond
76
76
 
77
77
  # default 5 second timeout unless on ruby 1.9
78
- @timeout =
79
- if RUBY_VERSION < '1.9'
80
- spec.config[:wait_timeout] || 5
81
- end
78
+ @timeout = spec.config[:wait_timeout] || 5
82
79
 
83
80
  # default max pool size to 5
84
81
  @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
@@ -161,7 +158,6 @@ module ActiveRecord
161
158
  keys = @reserved_connections.keys - Thread.list.find_all { |t|
162
159
  t.alive?
163
160
  }.map { |thread| thread.object_id }
164
-
165
161
  keys.each do |key|
166
162
  checkin @reserved_connections[key]
167
163
  @reserved_connections.delete(key)
@@ -194,16 +190,18 @@ module ActiveRecord
194
190
  checkout_new_connection
195
191
  end
196
192
  return conn if conn
197
- # No connections available; wait for one
198
- if @queue.wait(@timeout)
193
+
194
+ @queue.wait(@timeout)
195
+
196
+ if(@checked_out.size < @connections.size)
199
197
  next
200
198
  else
201
- # try looting dead threads
202
199
  clear_stale_cached_connections!
203
200
  if @size == @checked_out.size
204
201
  raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
205
202
  end
206
203
  end
204
+
207
205
  end
208
206
  end
209
207
  end
@@ -67,8 +67,8 @@ module ActiveRecord
67
67
 
68
68
  begin
69
69
  require "active_record/connection_adapters/#{spec[:adapter]}_adapter"
70
- rescue LoadError
71
- raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{$!})"
70
+ rescue LoadError => e
71
+ raise "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e})"
72
72
  end
73
73
 
74
74
  adapter_method = "#{spec[:adapter]}_connection"
@@ -327,25 +327,21 @@ module ActiveRecord
327
327
  #
328
328
  # Note: SQLite doesn't support index length
329
329
  def add_index(table_name, column_name, options = {})
330
- options[:name] = options[:name].to_s if options.key?(:name)
331
-
332
330
  column_names = Array.wrap(column_name)
333
331
  index_name = index_name(table_name, :column => column_names)
334
332
 
335
333
  if Hash === options # legacy support, since this param was a string
336
334
  index_type = options[:unique] ? "UNIQUE" : ""
337
- index_name = options[:name] || index_name
335
+ index_name = options[:name].to_s if options.key?(:name)
338
336
  else
339
337
  index_type = options
340
338
  end
341
339
 
342
340
  if index_name.length > index_name_length
343
- @logger.warn("Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters. Skipping.")
344
- return
341
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters"
345
342
  end
346
343
  if index_name_exists?(table_name, index_name, false)
347
- @logger.warn("Index name '#{index_name}' on table '#{table_name}' already exists. Skipping.")
348
- return
344
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' already exists"
349
345
  end
350
346
  quoted_column_names = quoted_columns_for_index(column_names, options).join(", ")
351
347
 
@@ -365,8 +361,7 @@ module ActiveRecord
365
361
  def remove_index(table_name, options = {})
366
362
  index_name = index_name(table_name, options)
367
363
  unless index_name_exists?(table_name, index_name, true)
368
- @logger.warn("Index name '#{index_name}' on table '#{table_name}' does not exist. Skipping.")
369
- return
364
+ raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
370
365
  end
371
366
  remove_index!(table_name, index_name)
372
367
  end
@@ -407,6 +402,7 @@ module ActiveRecord
407
402
  # as there's no way to determine the correct answer in that case.
408
403
  def index_name_exists?(table_name, index_name, default)
409
404
  return default unless respond_to?(:indexes)
405
+ index_name = index_name.to_s
410
406
  indexes(table_name).detect { |i| i.name == index_name }
411
407
  end
412
408
 
@@ -19,11 +19,11 @@ module ActiveRecord
19
19
  begin
20
20
  require 'mysql'
21
21
  rescue LoadError
22
- raise "!!! Missing the mysql gem. Add it to your Gemfile: gem 'mysql', '2.8.1'"
22
+ raise "!!! Missing the mysql2 gem. Add it to your Gemfile: gem 'mysql2'"
23
23
  end
24
24
 
25
25
  unless defined?(Mysql::Result) && Mysql::Result.method_defined?(:each_hash)
26
- raise "!!! Outdated mysql gem. Upgrade to 2.8.1 or later. In your Gemfile: gem 'mysql', '2.8.1'"
26
+ raise "!!! Outdated mysql gem. Upgrade to 2.8.1 or later. In your Gemfile: gem 'mysql', '2.8.1'. Or use gem 'mysql2'"
27
27
  end
28
28
  end
29
29
 
@@ -276,7 +276,7 @@ module ActiveRecord
276
276
  rows = []
277
277
  result.each { |row| rows << row }
278
278
  result.free
279
- @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
279
+ @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
280
280
  rows
281
281
  end
282
282
 
@@ -358,10 +358,10 @@ module ActiveRecord
358
358
  sql = "SHOW TABLES"
359
359
  end
360
360
 
361
- select_all(sql).inject("") do |structure, table|
361
+ select_all(sql).map do |table|
362
362
  table.delete('Table_type')
363
- structure += select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
364
- end
363
+ select_one("SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}")["Create Table"] + ";\n\n"
364
+ end.join("")
365
365
  end
366
366
 
367
367
  def recreate_database(name, options = {}) #:nodoc:
@@ -620,7 +620,7 @@ module ActiveRecord
620
620
  rows = []
621
621
  result.each_hash { |row| rows << row }
622
622
  result.free
623
- @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
623
+ @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
624
624
  rows
625
625
  end
626
626
 
@@ -360,8 +360,8 @@ module ActiveRecord
360
360
  end
361
361
 
362
362
  def copy_table_contents(from, to, columns, rename = {}) #:nodoc:
363
- column_mappings = Hash[*columns.map {|name| [name, name]}.flatten]
364
- rename.inject(column_mappings) {|map, a| map[a.last] = a.first; map}
363
+ column_mappings = Hash[columns.map {|name| [name, name]}]
364
+ rename.each { |a| column_mappings[a.last] = a.first }
365
365
  from_columns = columns(from).collect {|col| col.name}
366
366
  columns = columns.find_all{|col| from_columns.include?(column_mappings[col])}
367
367
  quoted_columns = columns.map { |col| quote_column_name(col) } * ','
@@ -6,40 +6,43 @@ module ActiveRecord
6
6
  #
7
7
  class DynamicFinderMatch
8
8
  def self.match(method)
9
- df_match = self.new(method)
10
- df_match.finder ? df_match : nil
11
- end
12
-
13
- def initialize(method)
14
- @finder = :first
15
- @bang = false
16
- @instantiator = nil
9
+ finder = :first
10
+ bang = false
11
+ instantiator = nil
17
12
 
18
13
  case method.to_s
19
- when /^find_(all_by|last_by|by)_([_a-zA-Z]\w*)$/
20
- @finder = :last if $1 == 'last_by'
21
- @finder = :all if $1 == 'all_by'
14
+ when /^find_(all_|last_)?by_([_a-zA-Z]\w*)$/
15
+ finder = :last if $1 == 'last_'
16
+ finder = :all if $1 == 'all_'
22
17
  names = $2
23
18
  when /^find_by_([_a-zA-Z]\w*)\!$/
24
- @bang = true
19
+ bang = true
25
20
  names = $1
26
21
  when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
27
- @instantiator = $1 == 'initialize' ? :new : :create
22
+ instantiator = $1 == 'initialize' ? :new : :create
28
23
  names = $2
29
24
  else
30
- @finder = nil
25
+ return nil
31
26
  end
32
- @attribute_names = names && names.split('_and_')
27
+
28
+ new(finder, instantiator, bang, names.split('_and_'))
29
+ end
30
+
31
+ def initialize(finder, instantiator, bang, attribute_names)
32
+ @finder = finder
33
+ @instantiator = instantiator
34
+ @bang = bang
35
+ @attribute_names = attribute_names
33
36
  end
34
37
 
35
38
  attr_reader :finder, :attribute_names, :instantiator
36
39
 
37
40
  def finder?
38
- !@finder.nil? && @instantiator.nil?
41
+ @finder && !@instantiator
39
42
  end
40
43
 
41
44
  def instantiator?
42
- @finder == :first && !@instantiator.nil?
45
+ @finder == :first && @instantiator
43
46
  end
44
47
 
45
48
  def creator?
@@ -8,25 +8,16 @@ module ActiveRecord
8
8
  # scope except that it's dynamic.
9
9
  class DynamicScopeMatch
10
10
  def self.match(method)
11
- ds_match = self.new(method)
12
- ds_match.scope ? ds_match : nil
11
+ return unless method.to_s =~ /^scoped_by_([_a-zA-Z]\w*)$/
12
+ new(true, $1 && $1.split('_and_'))
13
13
  end
14
14
 
15
- def initialize(method)
16
- @scope = true
17
- case method.to_s
18
- when /^scoped_by_([_a-zA-Z]\w*)$/
19
- names = $1
20
- else
21
- @scope = nil
22
- end
23
- @attribute_names = names && names.split('_and_')
15
+ def initialize(scope, attribute_names)
16
+ @scope = scope
17
+ @attribute_names = attribute_names
24
18
  end
25
19
 
26
20
  attr_reader :scope, :attribute_names
27
-
28
- def scope?
29
- !@scope.nil?
30
- end
21
+ alias :scope? :scope
31
22
  end
32
23
  end
@@ -704,11 +704,9 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
704
704
  end
705
705
 
706
706
  def read_yaml_fixture_files
707
- yaml_string = ""
708
- Dir["#{@fixture_path}/**/*.yml"].select { |f| test(?f, f) }.each do |subfixture_path|
709
- yaml_string << IO.read(subfixture_path)
710
- end
711
- yaml_string << IO.read(yaml_file_path)
707
+ yaml_string = (Dir["#{@fixture_path}/**/*.yml"].select { |f|
708
+ File.file?(f)
709
+ } + [yaml_file_path]).map { |file_path| IO.read(file_path) }.join
712
710
 
713
711
  if yaml = parse_yaml_string(yaml_string)
714
712
  # If the file is an ordered map, extract its children.
@@ -109,7 +109,7 @@ module ActiveRecord
109
109
  def destroy #:nodoc:
110
110
  return super unless locking_enabled?
111
111
 
112
- unless new_record?
112
+ if persisted?
113
113
  lock_col = self.class.locking_column
114
114
  previous_value = send(lock_col).to_i
115
115
 
@@ -14,8 +14,8 @@ module ActiveRecord
14
14
  # Example:
15
15
  # Account.transaction do
16
16
  # # select * from accounts where name = 'shugo' limit 1 for update
17
- # shugo = Account.find(:first, :conditions => "name = 'shugo'", :lock => true)
18
- # yuko = Account.find(:first, :conditions => "name = 'yuko'", :lock => true)
17
+ # shugo = Account.where("name = 'shugo'").lock(true).first
18
+ # yuko = Account.where("name = 'shugo'").lock(true).first
19
19
  # shugo.balance -= 100
20
20
  # shugo.save!
21
21
  # yuko.balance += 100
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  # This may be better if you don't need to lock every row. Example:
27
27
  # Account.transaction do
28
28
  # # select * from accounts where ...
29
- # accounts = Account.find(:all, :conditions => ...)
29
+ # accounts = Account.where(...).all
30
30
  # account1 = accounts.detect { |account| ... }
31
31
  # account2 = accounts.detect { |account| ... }
32
32
  # # select * from accounts where id=? for update
@@ -47,7 +47,7 @@ module ActiveRecord
47
47
  # or pass true for "FOR UPDATE" (the default, an exclusive row lock). Returns
48
48
  # the locked record.
49
49
  def lock!(lock = true)
50
- reload(:lock => lock) unless new_record?
50
+ reload(:lock => lock) if persisted?
51
51
  self
52
52
  end
53
53
  end
@@ -293,12 +293,10 @@ module ActiveRecord
293
293
 
294
294
  if check_existing_record && (record = send(association_name)) &&
295
295
  (options[:update_only] || record.id.to_s == attributes['id'].to_s)
296
- assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy])
296
+ assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
297
297
 
298
- elsif attributes['id']
299
- existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id'])
300
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
301
- self.send(association_name.to_s+'=', existing_record)
298
+ elsif !attributes['id'].blank?
299
+ raise_nested_attributes_record_not_found(association_name, attributes['id'])
302
300
 
303
301
  elsif !reject_new_record?(association_name, attributes)
304
302
  method = "build_#{association_name}"
@@ -349,7 +347,12 @@ module ActiveRecord
349
347
  end
350
348
 
351
349
  if attributes_collection.is_a? Hash
352
- attributes_collection = attributes_collection.sort_by { |index, _| index.to_i }.map { |_, attributes| attributes }
350
+ keys = attributes_collection.keys
351
+ attributes_collection = if keys.include?('id') || keys.include?(:id)
352
+ Array.wrap(attributes_collection)
353
+ else
354
+ attributes_collection.sort_by { |i, _| i.to_i }.map { |_, attributes| attributes }
355
+ end
353
356
  end
354
357
 
355
358
  association = send(association_name)
@@ -369,15 +372,12 @@ module ActiveRecord
369
372
  association.build(attributes.except(*UNASSIGNABLE_KEYS))
370
373
  end
371
374
 
372
- elsif existing_records.count == 0 #Existing record but not yet associated
373
- existing_record = self.class.reflect_on_association(association_name).klass.find(attributes['id'])
374
- association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
375
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
376
-
377
375
  elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
378
- association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
376
+ association.send(:add_record_to_target_with_callbacks, existing_record) if !association.loaded? && !call_reject_if(association_name, attributes)
379
377
  assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
380
378
 
379
+ else
380
+ raise_nested_attributes_record_not_found(association_name, attributes['id'])
381
381
  end
382
382
  end
383
383
  end
@@ -397,7 +397,7 @@ module ActiveRecord
397
397
  ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
398
398
  end
399
399
 
400
- # Determines if a new record should be built by checking for
400
+ # Determines if a new record should be build by checking for
401
401
  # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
402
402
  # association and evaluates to +true+.
403
403
  def reject_new_record?(association_name, attributes)
@@ -413,5 +413,9 @@ module ActiveRecord
413
413
  end
414
414
  end
415
415
 
416
+ def raise_nested_attributes_record_not_found(association_name, record_id)
417
+ reflection = self.class.reflect_on_association(association_name)
418
+ raise RecordNotFound, "Couldn't find #{reflection.klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
419
+ end
416
420
  end
417
421
  end
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  # Returns true if this object hasn't been saved yet -- that is, a record
5
5
  # for the object doesn't exist in the data store yet; otherwise, returns false.
6
6
  def new_record?
7
- @new_record
7
+ !@persisted
8
8
  end
9
9
 
10
10
  # Returns true if this object has been destroyed, otherwise returns false.
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  # Returns if the record is persisted, i.e. it's not a new record and it was
16
16
  # not destroyed.
17
17
  def persisted?
18
- !(new_record? || destroyed?)
18
+ @persisted && !destroyed?
19
19
  end
20
20
 
21
21
  # :call-seq:
@@ -97,7 +97,7 @@ module ActiveRecord
97
97
  became = klass.new
98
98
  became.instance_variable_set("@attributes", @attributes)
99
99
  became.instance_variable_set("@attributes_cache", @attributes_cache)
100
- became.instance_variable_set("@new_record", new_record?)
100
+ became.instance_variable_set("@persisted", persisted?)
101
101
  became.instance_variable_set("@destroyed", destroyed?)
102
102
  became
103
103
  end
@@ -225,9 +225,8 @@ module ActiveRecord
225
225
  # @brake.touch
226
226
  def touch(name = nil)
227
227
  attributes = timestamp_attributes_for_update_in_model
228
- unless attributes.blank?
229
- attributes << name if name
230
-
228
+ attributes << name if name
229
+ unless attributes.empty?
231
230
  current_time = current_time_from_proper_timezone
232
231
  changes = {}
233
232
 
@@ -244,7 +243,7 @@ module ActiveRecord
244
243
  private
245
244
  def create_or_update
246
245
  raise ReadOnlyRecord if readonly?
247
- result = new_record? ? create : update
246
+ result = persisted? ? update : create
248
247
  result != false
249
248
  end
250
249
 
@@ -273,7 +272,7 @@ module ActiveRecord
273
272
 
274
273
  self.id ||= new_id
275
274
 
276
- @new_record = false
275
+ @persisted = true
277
276
  id
278
277
  end
279
278
 
@@ -45,8 +45,8 @@ namespace :db do
45
45
  # Create the SQLite database
46
46
  ActiveRecord::Base.establish_connection(config)
47
47
  ActiveRecord::Base.connection
48
- rescue
49
- $stderr.puts $!, *($!.backtrace)
48
+ rescue Exception => e
49
+ $stderr.puts e, *(e.backtrace)
50
50
  $stderr.puts "Couldn't create database for #{config.inspect}"
51
51
  end
52
52
  end
@@ -86,13 +86,13 @@ namespace :db do
86
86
  end
87
87
  end
88
88
  when 'postgresql'
89
- @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8'
89
+ @encoding = config['encoding'] || ENV['CHARSET'] || 'utf8'
90
90
  begin
91
91
  ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
92
92
  ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding))
93
93
  ActiveRecord::Base.establish_connection(config)
94
- rescue
95
- $stderr.puts $!, *($!.backtrace)
94
+ rescue Exception => e
95
+ $stderr.puts e, *(e.backtrace)
96
96
  $stderr.puts "Couldn't create database for #{config.inspect}"
97
97
  end
98
98
  end
@@ -335,7 +335,7 @@ namespace :db do
335
335
  if File.exists?(file)
336
336
  load(file)
337
337
  else
338
- abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/boot.rb to limit the frameworks that will be loaded}
338
+ abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded}
339
339
  end
340
340
  end
341
341
  end
@@ -369,7 +369,7 @@ namespace :db do
369
369
  db_string = firebird_db_string(abcs[Rails.env])
370
370
  sh "isql -a #{db_string} > #{Rails.root}/db/#{Rails.env}_structure.sql"
371
371
  else
372
- raise "Task not supported by '#{abcs["test"]["adapter"]}'"
372
+ raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'"
373
373
  end
374
374
 
375
375
  if ActiveRecord::Base.connection.supports_migrations?