activerecord 3.1.0.rc4 → 3.1.0.rc5

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 (44) hide show
  1. data/CHANGELOG +25 -0
  2. data/lib/active_record/associations.rb +6 -5
  3. data/lib/active_record/associations/alias_tracker.rb +12 -24
  4. data/lib/active_record/associations/association.rb +56 -6
  5. data/lib/active_record/associations/belongs_to_association.rb +1 -1
  6. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +10 -12
  7. data/lib/active_record/associations/collection_association.rb +44 -31
  8. data/lib/active_record/associations/collection_proxy.rb +11 -4
  9. data/lib/active_record/associations/has_one_association.rb +1 -1
  10. data/lib/active_record/associations/join_helper.rb +1 -2
  11. data/lib/active_record/associations/preloader/association.rb +3 -2
  12. data/lib/active_record/associations/singular_association.rb +12 -4
  13. data/lib/active_record/attribute_methods.rb +1 -1
  14. data/lib/active_record/attribute_methods/primary_key.rb +1 -1
  15. data/lib/active_record/attribute_methods/read.rb +3 -2
  16. data/lib/active_record/autosave_association.rb +1 -1
  17. data/lib/active_record/base.rb +78 -30
  18. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +18 -0
  19. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +6 -6
  20. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +4 -4
  21. data/lib/active_record/connection_adapters/column.rb +2 -0
  22. data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -1
  23. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1 -1
  24. data/lib/active_record/connection_adapters/sqlite_adapter.rb +5 -0
  25. data/lib/active_record/locking/optimistic.rb +1 -1
  26. data/lib/active_record/migration.rb +6 -2
  27. data/lib/active_record/migration/command_recorder.rb +1 -1
  28. data/lib/active_record/named_scope.rb +19 -0
  29. data/lib/active_record/nested_attributes.rb +18 -12
  30. data/lib/active_record/persistence.rb +8 -3
  31. data/lib/active_record/query_cache.rb +8 -0
  32. data/lib/active_record/railtie.rb +23 -0
  33. data/lib/active_record/railties/databases.rake +18 -13
  34. data/lib/active_record/reflection.rb +26 -26
  35. data/lib/active_record/relation.rb +20 -12
  36. data/lib/active_record/relation/batches.rb +0 -2
  37. data/lib/active_record/relation/calculations.rb +11 -5
  38. data/lib/active_record/relation/finder_methods.rb +1 -1
  39. data/lib/active_record/relation/query_methods.rb +16 -7
  40. data/lib/active_record/relation/spawn_methods.rb +1 -1
  41. data/lib/active_record/session_store.rb +19 -9
  42. data/lib/active_record/test_case.rb +0 -1
  43. data/lib/active_record/version.rb +1 -1
  44. metadata +15 -29
@@ -346,11 +346,11 @@ module ActiveRecord
346
346
 
347
347
  # Remove the given index from the table.
348
348
  #
349
- # Remove the suppliers_name_index in the suppliers table.
350
- # remove_index :suppliers, :name
351
- # Remove the index named accounts_branch_id_index in the accounts table.
349
+ # Remove the index_accounts_on_column in the accounts table.
350
+ # remove_index :accounts, :column
351
+ # Remove the index named index_accounts_on_branch_id in the accounts table.
352
352
  # remove_index :accounts, :column => :branch_id
353
- # Remove the index named accounts_branch_id_party_id_index in the accounts table.
353
+ # Remove the index named index_accounts_on_branch_id_and_party_id in the accounts table.
354
354
  # remove_index :accounts, :column => [:branch_id, :party_id]
355
355
  # Remove the index named by_branch_party in the accounts table.
356
356
  # remove_index :accounts, :name => :by_branch_party
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  module ActiveRecord
2
4
  # :stopdoc:
3
5
  module ConnectionAdapters
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- gem 'mysql2', '~> 0.3.0'
3
+ gem 'mysql2', '~> 0.3.6'
4
4
  require 'mysql2'
5
5
 
6
6
  module ActiveRecord
@@ -929,7 +929,7 @@ module ActiveRecord
929
929
 
930
930
  # Construct a clean list of column names from the ORDER BY clause, removing
931
931
  # any ASC/DESC modifiers
932
- order_columns = orders.collect { |s| s =~ /^(.+)\s+(ASC|DESC)\s*$/i ? $1 : s }
932
+ order_columns = orders.collect { |s| s.gsub(/\s+(ASC|DESC)\s*/i, '') }
933
933
  order_columns.delete_if { |c| c.blank? }
934
934
  order_columns = order_columns.zip((0...order_columns.size).to_a).map { |s,i| "#{s} AS alias_#{i}" }
935
935
 
@@ -155,6 +155,11 @@ module ActiveRecord
155
155
  end
156
156
  end
157
157
 
158
+ def type_cast(value, column) # :nodoc:
159
+ return super unless BigDecimal === value
160
+
161
+ value.to_f
162
+ end
158
163
 
159
164
  # DATABASE STATEMENTS ======================================
160
165
 
@@ -73,7 +73,7 @@ module ActiveRecord
73
73
  # <tt>locking_enabled?</tt> at this point as
74
74
  # <tt>@attributes</tt> may not have been initialized yet.
75
75
 
76
- if lock_optimistically && result.include?(self.class.locking_column)
76
+ if result.key?(self.class.locking_column) && lock_optimistically
77
77
  result[self.class.locking_column] ||= 0
78
78
  end
79
79
 
@@ -1,3 +1,5 @@
1
+ require "active_support/core_ext/module/delegation"
2
+ require "active_support/core_ext/class/attribute_accessors"
1
3
  require "active_support/core_ext/array/wrap"
2
4
 
3
5
  module ActiveRecord
@@ -116,8 +118,10 @@ module ActiveRecord
116
118
  # with the name of the column. Other options include
117
119
  # <tt>:name</tt> and <tt>:unique</tt> (e.g.
118
120
  # <tt>{ :name => "users_name_index", :unique => true }</tt>).
119
- # * <tt>remove_index(table_name, index_name)</tt>: Removes the index specified
120
- # by +index_name+.
121
+ # * <tt>remove_index(table_name, :column => column_name)</tt>: Removes the index
122
+ # specified by +column_name+.
123
+ # * <tt>remove_index(table_name, :name => index_name)</tt>: Removes the index
124
+ # specified by +index_name+.
121
125
  #
122
126
  # == Irreversible transformations
123
127
  #
@@ -48,7 +48,7 @@ module ActiveRecord
48
48
  super || delegate.respond_to?(*args)
49
49
  end
50
50
 
51
- [:create_table, :rename_table, :add_column, :remove_column, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column, :change_column_default].each do |method|
51
+ [:create_table, :change_table, :rename_table, :add_column, :remove_column, :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column, :change_column_default].each do |method|
52
52
  class_eval <<-EOV, __FILE__, __LINE__ + 1
53
53
  def #{method}(*args) # def create_table(*args)
54
54
  record(:"#{method}", args) # record(:create_table, args)
@@ -40,6 +40,25 @@ module ActiveRecord
40
40
  end
41
41
  end
42
42
 
43
+ ##
44
+ # Collects attributes from scopes that should be applied when creating
45
+ # an AR instance for the particular class this is called on.
46
+ def scope_attributes # :nodoc:
47
+ if current_scope
48
+ current_scope.scope_for_create
49
+ else
50
+ scope = relation.clone
51
+ scope.default_scoped = true
52
+ scope.scope_for_create
53
+ end
54
+ end
55
+
56
+ ##
57
+ # Are there default attributes associated with this scope?
58
+ def scope_attributes? # :nodoc:
59
+ current_scope || default_scopes.any?
60
+ end
61
+
43
62
  # Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query,
44
63
  # such as <tt>where(:color => :red).select('shirts.*').includes(:washing_instructions)</tt>.
45
64
  #
@@ -277,14 +277,14 @@ module ActiveRecord
277
277
  type = (reflection.collection? ? :collection : :one_to_one)
278
278
 
279
279
  # def pirate_attributes=(attributes)
280
- # assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
280
+ # assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options)
281
281
  # end
282
282
  class_eval <<-eoruby, __FILE__, __LINE__ + 1
283
283
  if method_defined?(:#{association_name}_attributes=)
284
284
  remove_method(:#{association_name}_attributes=)
285
285
  end
286
286
  def #{association_name}_attributes=(attributes)
287
- assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
287
+ assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options)
288
288
  end
289
289
  eoruby
290
290
  else
@@ -319,21 +319,21 @@ module ActiveRecord
319
319
  # If the given attributes include a matching <tt>:id</tt> attribute, or
320
320
  # update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
321
321
  # then the existing record will be marked for destruction.
322
- def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
322
+ def assign_nested_attributes_for_one_to_one_association(association_name, attributes, assignment_opts = {})
323
323
  options = self.nested_attributes_options[association_name]
324
324
  attributes = attributes.with_indifferent_access
325
325
 
326
326
  if (options[:update_only] || !attributes['id'].blank?) && (record = send(association_name)) &&
327
327
  (options[:update_only] || record.id.to_s == attributes['id'].to_s)
328
- assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy]) unless call_reject_if(association_name, attributes)
328
+ assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy], assignment_opts) unless call_reject_if(association_name, attributes)
329
329
 
330
- elsif attributes['id'].present?
330
+ elsif attributes['id'].present? && !assignment_opts[:without_protection]
331
331
  raise_nested_attributes_record_not_found(association_name, attributes['id'])
332
332
 
333
333
  elsif !reject_new_record?(association_name, attributes)
334
334
  method = "build_#{association_name}"
335
335
  if respond_to?(method)
336
- send(method, attributes.except(*UNASSIGNABLE_KEYS))
336
+ send(method, attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
337
337
  else
338
338
  raise ArgumentError, "Cannot build association #{association_name}. Are you trying to build a polymorphic one-to-one association?"
339
339
  end
@@ -367,7 +367,7 @@ module ActiveRecord
367
367
  # { :name => 'John' },
368
368
  # { :id => '2', :_destroy => true }
369
369
  # ])
370
- def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
370
+ def assign_nested_attributes_for_collection_association(association_name, attributes_collection, assignment_opts = {})
371
371
  options = self.nested_attributes_options[association_name]
372
372
 
373
373
  unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
@@ -383,7 +383,7 @@ module ActiveRecord
383
383
  attributes_collection = if keys.include?('id') || keys.include?(:id)
384
384
  Array.wrap(attributes_collection)
385
385
  else
386
- attributes_collection.sort_by { |i, _| i.to_i }.map { |_, attributes| attributes }
386
+ attributes_collection.values
387
387
  end
388
388
  end
389
389
 
@@ -401,7 +401,7 @@ module ActiveRecord
401
401
 
402
402
  if attributes['id'].blank?
403
403
  unless reject_new_record?(association_name, attributes)
404
- association.build(attributes.except(*UNASSIGNABLE_KEYS))
404
+ association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
405
405
  end
406
406
  elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
407
407
  unless association.loaded? || call_reject_if(association_name, attributes)
@@ -418,8 +418,10 @@ module ActiveRecord
418
418
  end
419
419
 
420
420
  if !call_reject_if(association_name, attributes)
421
- assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
421
+ assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy], assignment_opts)
422
422
  end
423
+ elsif assignment_opts[:without_protection]
424
+ association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
423
425
  else
424
426
  raise_nested_attributes_record_not_found(association_name, attributes['id'])
425
427
  end
@@ -428,8 +430,8 @@ module ActiveRecord
428
430
 
429
431
  # Updates a record with the +attributes+ or marks it for destruction if
430
432
  # +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
431
- def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
432
- record.attributes = attributes.except(*UNASSIGNABLE_KEYS)
433
+ def assign_to_or_mark_for_destruction(record, attributes, allow_destroy, assignment_opts)
434
+ record.assign_attributes(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts)
433
435
  record.mark_for_destruction if has_destroy_flag?(attributes) && allow_destroy
434
436
  end
435
437
 
@@ -458,5 +460,9 @@ module ActiveRecord
458
460
  def raise_nested_attributes_record_not_found(association_name, record_id)
459
461
  raise RecordNotFound, "Couldn't find #{self.class.reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
460
462
  end
463
+
464
+ def unassignable_keys(assignment_opts)
465
+ assignment_opts[:without_protection] ? UNASSIGNABLE_KEYS - %w[id] : UNASSIGNABLE_KEYS
466
+ end
461
467
  end
462
468
  end
@@ -79,6 +79,8 @@ module ActiveRecord
79
79
  # Deletes the record in the database and freezes this instance to reflect
80
80
  # that no changes should be made (since they can't be persisted).
81
81
  def destroy
82
+ destroy_associations
83
+
82
84
  if persisted?
83
85
  IdentityMap.remove(self) if IdentityMap.enabled?
84
86
  pk = self.class.primary_key
@@ -282,6 +284,11 @@ module ActiveRecord
282
284
  end
283
285
 
284
286
  private
287
+
288
+ # A hook to be overridden by association modules.
289
+ def destroy_associations
290
+ end
291
+
285
292
  def create_or_update
286
293
  raise ReadOnlyRecord if readonly?
287
294
  result = new_record? ? create : update
@@ -317,9 +324,7 @@ module ActiveRecord
317
324
  # that a new instance, or one populated from a passed-in Hash, still has all the attributes
318
325
  # that instances loaded from the database would.
319
326
  def attributes_from_column_definition
320
- Hash[self.class.columns.map do |column|
321
- [column.name, column.default]
322
- end]
327
+ self.class.column_defaults.dup
323
328
  end
324
329
  end
325
330
  end
@@ -33,6 +33,14 @@ module ActiveRecord
33
33
  @target = target
34
34
  end
35
35
 
36
+ def method_missing(method_sym, *arguments, &block)
37
+ @target.send(method_sym, *arguments, &block)
38
+ end
39
+
40
+ def respond_to?(method_sym, include_private = false)
41
+ super || @target.respond_to?(method_sym)
42
+ end
43
+
36
44
  def each(&block)
37
45
  @target.each(&block)
38
46
  end
@@ -96,5 +96,28 @@ module ActiveRecord
96
96
  end
97
97
  end
98
98
  end
99
+
100
+ config.after_initialize do
101
+ container = :"activerecord.attributes"
102
+ lookup = I18n.t(container, :default => {})
103
+ if lookup.is_a?(Hash)
104
+ lookup.each do |key, value|
105
+ if value.is_a?(Hash) && value.any? { |k,v| v.is_a?(Hash) }
106
+ $stderr.puts "[DEPRECATION WARNING] Nested I18n namespace lookup under \"#{container}.#{key}\" is no longer supported"
107
+ end
108
+ end
109
+ end
110
+
111
+ container = :"activerecord.models"
112
+ lookup = I18n.t(container, :default => {})
113
+ if lookup.is_a?(Hash)
114
+ lookup.each do |key, value|
115
+ if value.is_a?(Hash) && !value.key?(:one)
116
+ $stderr.puts "[DEPRECATION WARNING] Nested I18n namespace lookup under \"#{container}.#{key}\" is no longer supported"
117
+ end
118
+ end
119
+ end
120
+ end
121
+
99
122
  end
100
123
  end
@@ -44,6 +44,12 @@ db_namespace = namespace :db do
44
44
  create_database(ActiveRecord::Base.configurations[Rails.env])
45
45
  end
46
46
 
47
+ def mysql_creation_options(config)
48
+ @charset = ENV['CHARSET'] || 'utf8'
49
+ @collation = ENV['COLLATION'] || 'utf8_unicode_ci'
50
+ {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)}
51
+ end
52
+
47
53
  def create_database(config)
48
54
  begin
49
55
  if config['adapter'] =~ /sqlite/
@@ -67,9 +73,6 @@ db_namespace = namespace :db do
67
73
  rescue
68
74
  case config['adapter']
69
75
  when /mysql/
70
- @charset = ENV['CHARSET'] || 'utf8'
71
- @collation = ENV['COLLATION'] || 'utf8_unicode_ci'
72
- creation_options = {:charset => (config['charset'] || @charset), :collation => (config['collation'] || @collation)}
73
76
  if config['adapter'] =~ /jdbc/
74
77
  #FIXME After Jdbcmysql gives this class
75
78
  require 'active_record/railties/jdbcmysql_error'
@@ -80,7 +83,7 @@ db_namespace = namespace :db do
80
83
  access_denied_error = 1045
81
84
  begin
82
85
  ActiveRecord::Base.establish_connection(config.merge('database' => nil))
83
- ActiveRecord::Base.connection.create_database(config['database'], creation_options)
86
+ ActiveRecord::Base.connection.create_database(config['database'], mysql_creation_options(config))
84
87
  ActiveRecord::Base.establish_connection(config)
85
88
  rescue error_class => sqlerr
86
89
  if sqlerr.errno == access_denied_error
@@ -112,7 +115,8 @@ db_namespace = namespace :db do
112
115
  end
113
116
  end
114
117
  else
115
- $stderr.puts "#{config['database']} already exists"
118
+ # Bug with 1.9.2 Calling return within begin still executes else
119
+ $stderr.puts "#{config['database']} already exists" unless config['adapter'] =~ /sqlite/
116
120
  end
117
121
  end
118
122
 
@@ -377,8 +381,7 @@ db_namespace = namespace :db do
377
381
  dbfile = abcs[Rails.env]['database'] || abcs[Rails.env]['dbfile']
378
382
  `sqlite3 #{dbfile} .schema > db/#{Rails.env}_structure.sql`
379
383
  when 'sqlserver'
380
- `scptxfr /s #{abcs[Rails.env]['host']} /d #{abcs[Rails.env]['database']} /I /f db\\#{Rails.env}_structure.sql /q /A /r`
381
- `scptxfr /s #{abcs[Rails.env]['host']} /d #{abcs[Rails.env]['database']} /I /F db\ /q /A /r`
384
+ `smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f db\\#{Rails.env}_structure.sql -A -U`
382
385
  when "firebird"
383
386
  set_firebird_env(abcs[Rails.env])
384
387
  db_string = firebird_db_string(abcs[Rails.env])
@@ -423,7 +426,7 @@ db_namespace = namespace :db do
423
426
  dbfile = abcs['test']['database'] || abcs['test']['dbfile']
424
427
  `sqlite3 #{dbfile} < #{Rails.root}/db/#{Rails.env}_structure.sql`
425
428
  when 'sqlserver'
426
- `osql -E -S #{abcs['test']['host']} -d #{abcs['test']['database']} -i db\\#{Rails.env}_structure.sql`
429
+ `sqlcmd -S #{abcs['test']['host']} -d #{abcs['test']['database']} -U #{abcs['test']['username']} -P #{abcs['test']['password']} -i db\\#{Rails.env}_structure.sql`
427
430
  when 'oci', 'oracle'
428
431
  ActiveRecord::Base.establish_connection(:test)
429
432
  IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split(";\n\n").each do |ddl|
@@ -444,7 +447,7 @@ db_namespace = namespace :db do
444
447
  case abcs['test']['adapter']
445
448
  when /mysql/
446
449
  ActiveRecord::Base.establish_connection(:test)
447
- ActiveRecord::Base.connection.recreate_database(abcs['test']['database'], abcs['test'])
450
+ ActiveRecord::Base.connection.recreate_database(abcs['test']['database'], mysql_creation_options(abcs['test']))
448
451
  when /postgresql/
449
452
  ActiveRecord::Base.clear_active_connections!
450
453
  drop_database(abcs['test'])
@@ -453,9 +456,11 @@ db_namespace = namespace :db do
453
456
  dbfile = abcs['test']['database'] || abcs['test']['dbfile']
454
457
  File.delete(dbfile) if File.exist?(dbfile)
455
458
  when 'sqlserver'
456
- dropfkscript = "#{abcs['test']['host']}.#{abcs['test']['database']}.DP1".gsub(/\\/,'-')
457
- `osql -E -S #{abcs['test']['host']} -d #{abcs['test']['database']} -i db\\#{dropfkscript}`
458
- `osql -E -S #{abcs['test']['host']} -d #{abcs['test']['database']} -i db\\#{Rails.env}_structure.sql`
459
+ test = abcs.deep_dup['test']
460
+ test_database = test['database']
461
+ test['database'] = 'master'
462
+ ActiveRecord::Base.establish_connection(test)
463
+ ActiveRecord::Base.connection.recreate_database!(test_database)
459
464
  when "oci", "oracle"
460
465
  ActiveRecord::Base.establish_connection(:test)
461
466
  ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl|
@@ -499,7 +504,7 @@ namespace :railties do
499
504
  # desc "Copies missing migrations from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2"
500
505
  task :migrations => :'db:load_config' do
501
506
  to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map {|n| n.strip }
502
- railties = {}
507
+ railties = ActiveSupport::OrderedHash.new
503
508
  Rails.application.railties.all do |railtie|
504
509
  next unless to_load == :all || to_load.include?(railtie.railtie_name)
505
510
 
@@ -81,12 +81,6 @@ module ActiveRecord
81
81
  # Abstract base class for AggregateReflection and AssociationReflection. Objects of
82
82
  # AggregateReflection and AssociationReflection are returned by the Reflection::ClassMethods.
83
83
  class MacroReflection
84
- attr_reader :active_record
85
-
86
- def initialize(macro, name, options, active_record)
87
- @macro, @name, @options, @active_record = macro, name, options, active_record
88
- end
89
-
90
84
  # Returns the name of the macro.
91
85
  #
92
86
  # <tt>composed_of :balance, :class_name => 'Money'</tt> returns <tt>:balance</tt>
@@ -105,6 +99,19 @@ module ActiveRecord
105
99
  # <tt>has_many :clients</tt> returns +{}+
106
100
  attr_reader :options
107
101
 
102
+ attr_reader :active_record
103
+
104
+ attr_reader :plural_name # :nodoc:
105
+
106
+ def initialize(macro, name, options, active_record)
107
+ @macro = macro
108
+ @name = name
109
+ @options = options
110
+ @active_record = active_record
111
+ @plural_name = active_record.pluralize_table_names ?
112
+ name.to_s.pluralize : name.to_s
113
+ end
114
+
108
115
  # Returns the class for the macro.
109
116
  #
110
117
  # <tt>composed_of :balance, :class_name => 'Money'</tt> returns the Money class
@@ -124,7 +131,11 @@ module ActiveRecord
124
131
  # Returns +true+ if +self+ and +other_aggregation+ have the same +name+ attribute, +active_record+ attribute,
125
132
  # and +other_aggregation+ has an options hash assigned to it.
126
133
  def ==(other_aggregation)
127
- other_aggregation.kind_of?(self.class) && name == other_aggregation.name && other_aggregation.options && active_record == other_aggregation.active_record
134
+ super ||
135
+ other_aggregation.kind_of?(self.class) &&
136
+ name == other_aggregation.name &&
137
+ other_aggregation.options &&
138
+ active_record == other_aggregation.active_record
128
139
  end
129
140
 
130
141
  def sanitized_conditions #:nodoc:
@@ -167,27 +178,16 @@ module ActiveRecord
167
178
  @collection = macro.in?([:has_many, :has_and_belongs_to_many])
168
179
  end
169
180
 
181
+ # This is a hack so that we can tell if build_association was overridden, in order to
182
+ # provide an appropriate deprecation if the overridden method ignored the &block. Please
183
+ # see Association#build_record for details.
184
+ attr_accessor :original_build_association_called # :nodoc
185
+
170
186
  # Returns a new, unsaved instance of the associated class. +options+ will
171
187
  # be passed to the class's constructor.
172
- def build_association(*options)
173
- klass.new(*options)
174
- end
175
-
176
- # Creates a new instance of the associated class, and immediately saves it
177
- # with ActiveRecord::Base#save. +options+ will be passed to the class's
178
- # creation method. Returns the newly created object.
179
- def create_association(*options)
180
- klass.create(*options)
181
- end
182
-
183
- # Creates a new instance of the associated class, and immediately saves it
184
- # with ActiveRecord::Base#save!. +options+ will be passed to the class's
185
- # creation method. If the created record doesn't pass validations, then an
186
- # exception will be raised.
187
- #
188
- # Returns the newly created object.
189
- def create_association!(*options)
190
- klass.create!(*options)
188
+ def build_association(*options, &block)
189
+ @original_build_association_called = true
190
+ klass.new(*options, &block)
191
191
  end
192
192
 
193
193
  def table_name