activerecord 3.0.0.beta4 → 3.0.0.rc

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 (69) hide show
  1. data/CHANGELOG +267 -254
  2. data/README.rdoc +222 -0
  3. data/examples/performance.rb +9 -9
  4. data/lib/active_record/aggregations.rb +3 -4
  5. data/lib/active_record/association_preload.rb +15 -10
  6. data/lib/active_record/associations.rb +54 -37
  7. data/lib/active_record/associations/association_collection.rb +43 -17
  8. data/lib/active_record/associations/association_proxy.rb +2 -0
  9. data/lib/active_record/associations/belongs_to_association.rb +1 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +22 -7
  12. data/lib/active_record/associations/has_many_association.rb +6 -1
  13. data/lib/active_record/associations/has_many_through_association.rb +1 -0
  14. data/lib/active_record/associations/has_one_association.rb +1 -0
  15. data/lib/active_record/associations/has_one_through_association.rb +1 -0
  16. data/lib/active_record/associations/through_association_scope.rb +3 -2
  17. data/lib/active_record/attribute_methods.rb +1 -0
  18. data/lib/active_record/autosave_association.rb +4 -6
  19. data/lib/active_record/base.rb +106 -240
  20. data/lib/active_record/callbacks.rb +4 -25
  21. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +22 -29
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +2 -8
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
  24. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +10 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +56 -7
  26. data/lib/active_record/connection_adapters/abstract_adapter.rb +10 -18
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +2 -2
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +65 -69
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +19 -6
  30. data/lib/active_record/connection_adapters/sqlite_adapter.rb +20 -46
  31. data/lib/active_record/counter_cache.rb +14 -4
  32. data/lib/active_record/dynamic_finder_match.rb +9 -0
  33. data/lib/active_record/dynamic_scope_match.rb +7 -0
  34. data/lib/active_record/errors.rb +3 -0
  35. data/lib/active_record/fixtures.rb +5 -6
  36. data/lib/active_record/locale/en.yml +1 -1
  37. data/lib/active_record/locking/optimistic.rb +1 -0
  38. data/lib/active_record/log_subscriber.rb +48 -0
  39. data/lib/active_record/migration.rb +64 -37
  40. data/lib/active_record/named_scope.rb +33 -19
  41. data/lib/active_record/nested_attributes.rb +17 -13
  42. data/lib/active_record/observer.rb +13 -6
  43. data/lib/active_record/persistence.rb +55 -22
  44. data/lib/active_record/query_cache.rb +1 -0
  45. data/lib/active_record/railtie.rb +14 -8
  46. data/lib/active_record/railties/controller_runtime.rb +2 -2
  47. data/lib/active_record/railties/databases.rake +63 -33
  48. data/lib/active_record/reflection.rb +46 -28
  49. data/lib/active_record/relation.rb +38 -24
  50. data/lib/active_record/relation/finder_methods.rb +5 -5
  51. data/lib/active_record/relation/predicate_builder.rb +2 -4
  52. data/lib/active_record/relation/query_methods.rb +134 -115
  53. data/lib/active_record/relation/spawn_methods.rb +1 -1
  54. data/lib/active_record/schema.rb +2 -0
  55. data/lib/active_record/schema_dumper.rb +15 -12
  56. data/lib/active_record/serialization.rb +2 -0
  57. data/lib/active_record/session_store.rb +93 -79
  58. data/lib/active_record/test_case.rb +3 -0
  59. data/lib/active_record/timestamp.rb +49 -29
  60. data/lib/active_record/transactions.rb +5 -2
  61. data/lib/active_record/validations.rb +5 -2
  62. data/lib/active_record/validations/associated.rb +1 -1
  63. data/lib/active_record/validations/uniqueness.rb +1 -1
  64. data/lib/active_record/version.rb +1 -1
  65. data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -6
  66. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  67. metadata +27 -14
  68. data/README +0 -351
  69. data/lib/active_record/railties/log_subscriber.rb +0 -32
@@ -1,4 +1,9 @@
1
1
  module ActiveRecord
2
+
3
+ # = Active Record Dynamic Finder Match
4
+ #
5
+ # Provides dynamic attribute-based finders such as <tt>find_by_country</tt>
6
+ # if, for example, the <tt>Person</tt> has an attribute with that name.
2
7
  class DynamicFinderMatch
3
8
  def self.match(method)
4
9
  df_match = self.new(method)
@@ -37,6 +42,10 @@ module ActiveRecord
37
42
  @finder == :first && !@instantiator.nil?
38
43
  end
39
44
 
45
+ def creator?
46
+ @finder == :first && @instantiator == :create
47
+ end
48
+
40
49
  def bang?
41
50
  @bang
42
51
  end
@@ -1,4 +1,11 @@
1
1
  module ActiveRecord
2
+
3
+ # = Active Record Dynamic Scope Match
4
+ #
5
+ # Provides dynamic attribute-based scopes such as <tt>scoped_by_price(4.99)</tt>
6
+ # if, for example, the <tt>Product</tt> has an attribute with that name. You can
7
+ # chain more <tt>scoped_by_* </tt> methods after the other. It acts like a named
8
+ # scope except that it's dynamic.
2
9
  class DynamicScopeMatch
3
10
  def self.match(method)
4
11
  ds_match = self.new(method)
@@ -1,4 +1,7 @@
1
1
  module ActiveRecord
2
+
3
+ # = Active Record Errors
4
+ #
2
5
  # Generic Active Record exception class.
3
6
  class ActiveRecordError < StandardError
4
7
  end
@@ -664,14 +664,13 @@ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
664
664
  end
665
665
 
666
666
  def has_primary_key_column?
667
- @has_primary_key_column ||= model_class && primary_key_name &&
668
- model_class.columns.find { |c| c.name == primary_key_name }
667
+ @has_primary_key_column ||= primary_key_name &&
668
+ model_class.columns.any? { |c| c.name == primary_key_name }
669
669
  end
670
670
 
671
671
  def timestamp_column_names
672
- @timestamp_column_names ||= %w(created_at created_on updated_at updated_on).select do |name|
673
- column_names.include?(name)
674
- end
672
+ @timestamp_column_names ||=
673
+ %w(created_at created_on updated_at updated_on) & column_names
675
674
  end
676
675
 
677
676
  def inheritance_column_name
@@ -872,7 +871,7 @@ module ActiveRecord
872
871
  table_names.each do |table_name|
873
872
  table_name = table_name.to_s.tr('./', '_')
874
873
 
875
- define_method(table_name) do |*fixtures|
874
+ redefine_method(table_name) do |*fixtures|
876
875
  force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload
877
876
 
878
877
  @fixture_cache[table_name] ||= {}
@@ -4,7 +4,7 @@ en:
4
4
  #created_at: "Created at"
5
5
  #updated_at: "Updated at"
6
6
 
7
- # ActiveRecord models configuration
7
+ # Active Record models configuration
8
8
  activerecord:
9
9
  errors:
10
10
  messages:
@@ -124,6 +124,7 @@ module ActiveRecord
124
124
  end
125
125
  end
126
126
 
127
+ @destroyed = true
127
128
  freeze
128
129
  end
129
130
 
@@ -0,0 +1,48 @@
1
+ module ActiveRecord
2
+ class LogSubscriber < ActiveSupport::LogSubscriber
3
+ def self.runtime=(value)
4
+ Thread.current["active_record_sql_runtime"] = value
5
+ end
6
+
7
+ def self.runtime
8
+ Thread.current["active_record_sql_runtime"] ||= 0
9
+ end
10
+
11
+ def self.reset_runtime
12
+ rt, self.runtime = runtime, 0
13
+ rt
14
+ end
15
+
16
+ def initialize
17
+ super
18
+ @odd_or_even = false
19
+ end
20
+
21
+ def sql(event)
22
+ self.class.runtime += event.duration
23
+ return unless logger.debug?
24
+
25
+ name = '%s (%.1fms)' % [event.payload[:name], event.duration]
26
+ sql = event.payload[:sql].squeeze(' ')
27
+
28
+ if odd?
29
+ name = color(name, CYAN, true)
30
+ sql = color(sql, nil, true)
31
+ else
32
+ name = color(name, MAGENTA, true)
33
+ end
34
+
35
+ debug " #{name} #{sql}"
36
+ end
37
+
38
+ def odd?
39
+ @odd_or_even = !@odd_or_even
40
+ end
41
+
42
+ def logger
43
+ ActiveRecord::Base.logger
44
+ end
45
+ end
46
+ end
47
+
48
+ ActiveRecord::LogSubscriber.attach_to :active_record
@@ -1,4 +1,5 @@
1
1
  require 'active_support/core_ext/kernel/singleton_class'
2
+ require 'active_support/core_ext/module/aliasing'
2
3
 
3
4
  module ActiveRecord
4
5
  # Exception that can be raised to stop migrations from going backwards.
@@ -29,11 +30,15 @@ module ActiveRecord
29
30
  end
30
31
  end
31
32
 
32
- # Migrations can manage the evolution of a schema used by several physical databases. It's a solution
33
- # to the common problem of adding a field to make a new feature work in your local database, but being unsure of how to
34
- # push that change to other developers and to the production server. With migrations, you can describe the transformations
35
- # in self-contained classes that can be checked into version control systems and executed against another database that
36
- # might be one, two, or five versions behind.
33
+ # = Active Record Migrations
34
+ #
35
+ # Migrations can manage the evolution of a schema used by several physical
36
+ # databases. It's a solution to the common problem of adding a field to make
37
+ # a new feature work in your local database, but being unsure of how to
38
+ # push that change to other developers and to the production server. With
39
+ # migrations, you can describe the transformations in self-contained classes
40
+ # that can be checked into version control systems and executed against
41
+ # another database that might be one, two, or five versions behind.
37
42
  #
38
43
  # Example of a simple migration:
39
44
  #
@@ -47,10 +52,13 @@ module ActiveRecord
47
52
  # end
48
53
  # end
49
54
  #
50
- # This migration will add a boolean flag to the accounts table and remove it if you're backing out of the migration.
51
- # It shows how all migrations have two class methods +up+ and +down+ that describes the transformations required to implement
52
- # or remove the migration. These methods can consist of both the migration specific methods like add_column and remove_column,
53
- # but may also contain regular Ruby code for generating data needed for the transformations.
55
+ # This migration will add a boolean flag to the accounts table and remove it
56
+ # if you're backing out of the migration. It shows how all migrations have
57
+ # two class methods +up+ and +down+ that describes the transformations
58
+ # required to implement or remove the migration. These methods can consist
59
+ # of both the migration specific methods like add_column and remove_column,
60
+ # but may also contain regular Ruby code for generating data needed for the
61
+ # transformations.
54
62
  #
55
63
  # Example of a more complex migration that also needs to initialize data:
56
64
  #
@@ -64,7 +72,9 @@ module ActiveRecord
64
72
  # t.integer :position
65
73
  # end
66
74
  #
67
- # SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
75
+ # SystemSetting.create :name => "notice",
76
+ # :label => "Use notice?",
77
+ # :value => 1
68
78
  # end
69
79
  #
70
80
  # def self.down
@@ -72,35 +82,49 @@ module ActiveRecord
72
82
  # end
73
83
  # end
74
84
  #
75
- # This migration first adds the system_settings table, then creates the very first row in it using the Active Record model
76
- # that relies on the table. It also uses the more advanced create_table syntax where you can specify a complete table schema
77
- # in one block call.
85
+ # This migration first adds the system_settings table, then creates the very
86
+ # first row in it using the Active Record model that relies on the table. It
87
+ # also uses the more advanced create_table syntax where you can specify a
88
+ # complete table schema in one block call.
78
89
  #
79
90
  # == Available transformations
80
91
  #
81
- # * <tt>create_table(name, options)</tt> Creates a table called +name+ and makes the table object available to a block
82
- # that can then add columns to it, following the same format as add_column. See example above. The options hash is for
83
- # fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create table definition.
92
+ # * <tt>create_table(name, options)</tt> Creates a table called +name+ and
93
+ # makes the table object available to a block that can then add columns to it,
94
+ # following the same format as add_column. See example above. The options hash
95
+ # is for fragments like "DEFAULT CHARSET=UTF-8" that are appended to the create
96
+ # table definition.
84
97
  # * <tt>drop_table(name)</tt>: Drops the table called +name+.
85
- # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+ to +new_name+.
86
- # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column to the table called +table_name+
98
+ # * <tt>rename_table(old_name, new_name)</tt>: Renames the table called +old_name+
99
+ # to +new_name+.
100
+ # * <tt>add_column(table_name, column_name, type, options)</tt>: Adds a new column
101
+ # to the table called +table_name+
87
102
  # named +column_name+ specified to be one of the following types:
88
- # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>, <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
89
- # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be specified by passing an
90
- # +options+ hash like <tt>{ :default => 11 }</tt>. Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g. <tt>{ :limit => 50, :null => false }</tt>)
91
- # -- see ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
92
- # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames a column but keeps the type and content.
93
- # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes the column to a different type using the same
94
- # parameters as add_column.
95
- # * <tt>remove_column(table_name, column_name)</tt>: Removes the column named +column_name+ from the table called +table_name+.
96
- # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index with the name of the column. Other options include
97
- # <tt>:name</tt> and <tt>:unique</tt> (e.g. <tt>{ :name => "users_name_index", :unique => true }</tt>).
98
- # * <tt>remove_index(table_name, index_name)</tt>: Removes the index specified by +index_name+.
103
+ # <tt>:string</tt>, <tt>:text</tt>, <tt>:integer</tt>, <tt>:float</tt>,
104
+ # <tt>:decimal</tt>, <tt>:datetime</tt>, <tt>:timestamp</tt>, <tt>:time</tt>,
105
+ # <tt>:date</tt>, <tt>:binary</tt>, <tt>:boolean</tt>. A default value can be
106
+ # specified by passing an +options+ hash like <tt>{ :default => 11 }</tt>.
107
+ # Other options include <tt>:limit</tt> and <tt>:null</tt> (e.g.
108
+ # <tt>{ :limit => 50, :null => false }</tt>) -- see
109
+ # ActiveRecord::ConnectionAdapters::TableDefinition#column for details.
110
+ # * <tt>rename_column(table_name, column_name, new_column_name)</tt>: Renames
111
+ # a column but keeps the type and content.
112
+ # * <tt>change_column(table_name, column_name, type, options)</tt>: Changes
113
+ # the column to a different type using the same parameters as add_column.
114
+ # * <tt>remove_column(table_name, column_name)</tt>: Removes the column named
115
+ # +column_name+ from the table called +table_name+.
116
+ # * <tt>add_index(table_name, column_names, options)</tt>: Adds a new index
117
+ # with the name of the column. Other options include
118
+ # <tt>:name</tt> and <tt>:unique</tt> (e.g.
119
+ # <tt>{ :name => "users_name_index", :unique => true }</tt>).
120
+ # * <tt>remove_index(table_name, index_name)</tt>: Removes the index specified
121
+ # by +index_name+.
99
122
  #
100
123
  # == Irreversible transformations
101
124
  #
102
- # Some transformations are destructive in a manner that cannot be reversed. Migrations of that kind should raise
103
- # an <tt>ActiveRecord::IrreversibleMigration</tt> exception in their +down+ method.
125
+ # Some transformations are destructive in a manner that cannot be reversed.
126
+ # Migrations of that kind should raise an <tt>ActiveRecord::IrreversibleMigration</tt>
127
+ # exception in their +down+ method.
104
128
  #
105
129
  # == Running migrations from within Rails
106
130
  #
@@ -110,13 +134,15 @@ module ActiveRecord
110
134
  # rails generate migration MyNewMigration
111
135
  #
112
136
  # where MyNewMigration is the name of your migration. The generator will
113
- # create an empty migration file <tt>timestamp_my_new_migration.rb</tt> in the <tt>db/migrate/</tt>
114
- # directory where <tt>timestamp</tt> is the UTC formatted date and time that the migration was generated.
137
+ # create an empty migration file <tt>timestamp_my_new_migration.rb</tt>
138
+ # in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
139
+ # UTC formatted date and time that the migration was generated.
115
140
  #
116
141
  # You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of
117
142
  # MyNewMigration.
118
143
  #
119
144
  # There is a special syntactic shortcut to generate migrations that add fields to a table.
145
+ #
120
146
  # rails generate migration add_fieldname_to_tablename fieldname:string
121
147
  #
122
148
  # This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
@@ -191,9 +217,10 @@ module ActiveRecord
191
217
  #
192
218
  # == Using a model after changing its table
193
219
  #
194
- # Sometimes you'll want to add a column in a migration and populate it immediately after. In that case, you'll need
195
- # to make a call to Base#reset_column_information in order to ensure that the model has the latest column data from
196
- # after the new column was added. Example:
220
+ # Sometimes you'll want to add a column in a migration and populate it
221
+ # immediately after. In that case, you'll need to make a call to
222
+ # <tt>Base#reset_column_information</tt> in order to ensure that the model has the
223
+ # latest column data from after the new column was added. Example:
197
224
  #
198
225
  # class AddPeopleSalary < ActiveRecord::Migration
199
226
  # def self.up
@@ -257,7 +284,7 @@ module ActiveRecord
257
284
  #
258
285
  # config.active_record.timestamped_migrations = false
259
286
  #
260
- # In environment.rb.
287
+ # In application.rb.
261
288
  #
262
289
  class Migration
263
290
  @@verbose = true
@@ -4,11 +4,12 @@ require 'active_support/core_ext/kernel/singleton_class'
4
4
  require 'active_support/core_ext/object/blank'
5
5
 
6
6
  module ActiveRecord
7
+ # = Active Record Named \Scopes
7
8
  module NamedScope
8
9
  extend ActiveSupport::Concern
9
10
 
10
11
  module ClassMethods
11
- # Returns an anonymous scope.
12
+ # Returns an anonymous \scope.
12
13
  #
13
14
  # posts = Post.scoped
14
15
  # posts.size # Fires "select count(*) from posts" and returns the count
@@ -18,16 +19,17 @@ module ActiveRecord
18
19
  # fruits = fruits.where(:colour => 'red') if options[:red_only]
19
20
  # fruits = fruits.limit(10) if limited?
20
21
  #
21
- # Anonymous \scopes tend to be useful when procedurally generating complex queries, where passing
22
- # intermediate values (scopes) around as first-class objects is convenient.
22
+ # Anonymous \scopes tend to be useful when procedurally generating complex
23
+ # queries, where passing intermediate values (\scopes) around as first-class
24
+ # objects is convenient.
23
25
  #
24
- # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.
25
- def scoped(options = {}, &block)
26
- if options.present?
27
- relation = scoped.apply_finder_options(options)
28
- block_given? ? relation.extending(Module.new(&block)) : relation
26
+ # You can define a \scope that applies to all finders using
27
+ # ActiveRecord::Base.default_scope.
28
+ def scoped(options = nil)
29
+ if options
30
+ scoped.apply_finder_options(options)
29
31
  else
30
- current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone
32
+ current_scoped_methods ? relation.merge(current_scoped_methods) : relation.clone
31
33
  end
32
34
  end
33
35
 
@@ -35,7 +37,7 @@ module ActiveRecord
35
37
  read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
36
38
  end
37
39
 
38
- # Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
40
+ # Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query,
39
41
  # such as <tt>where(:color => :red).select('shirts.*').includes(:washing_instructions)</tt>.
40
42
  #
41
43
  # class Shirt < ActiveRecord::Base
@@ -85,28 +87,40 @@ module ActiveRecord
85
87
  # end
86
88
  def scope(name, scope_options = {}, &block)
87
89
  name = name.to_sym
90
+ valid_scope_name?(name)
88
91
 
89
- if !scopes[name] && respond_to?(name, true)
90
- logger.warn "Creating scope :#{name}. " \
91
- "Overwriting existing method #{self.name}.#{name}."
92
- end
92
+ extension = Module.new(&block) if block_given?
93
93
 
94
94
  scopes[name] = lambda do |*args|
95
95
  options = scope_options.is_a?(Proc) ? scope_options.call(*args) : scope_options
96
96
 
97
- relation = scoped
98
- relation = options.is_a?(Hash) ? relation.apply_finder_options(options) : scoped.merge(options) if options
99
- block_given? ? relation.extending(Module.new(&block)) : relation
97
+ relation = if options.is_a?(Hash)
98
+ scoped.apply_finder_options(options)
99
+ elsif options
100
+ scoped.merge(options)
101
+ else
102
+ scoped
103
+ end
104
+
105
+ extension ? relation.extending(extension) : relation
100
106
  end
101
107
 
102
- singleton_class.send :define_method, name, &scopes[name]
108
+ singleton_class.send(:redefine_method, name, &scopes[name])
103
109
  end
104
110
 
105
111
  def named_scope(*args, &block)
106
112
  ActiveSupport::Deprecation.warn("Base.named_scope has been deprecated, please use Base.scope instead", caller)
107
113
  scope(*args, &block)
108
114
  end
109
- end
110
115
 
116
+ protected
117
+
118
+ def valid_scope_name?(name)
119
+ if !scopes[name] && respond_to?(name, true)
120
+ logger.warn "Creating scope :#{name}. " \
121
+ "Overwriting existing method #{self.name}.#{name}."
122
+ end
123
+ end
124
+ end
111
125
  end
112
126
  end
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  self.nested_attributes_options = {}
16
16
  end
17
17
 
18
- # == Nested Attributes
18
+ # = Active Record Nested Attributes
19
19
  #
20
20
  # Nested attributes allow you to save attributes on associated records
21
21
  # through the parent. By default nested attribute updating is turned off,
@@ -25,6 +25,7 @@ module ActiveRecord
25
25
  #
26
26
  # The attribute writer is named after the association, which means that
27
27
  # in the following example, two new methods are added to your model:
28
+ #
28
29
  # <tt>author_attributes=(attributes)</tt> and
29
30
  # <tt>pages_attributes=(attributes)</tt>.
30
31
  #
@@ -132,7 +133,7 @@ module ActiveRecord
132
133
  # member.posts.first.title # => 'Kari, the awesome Ruby documentation browser!'
133
134
  # member.posts.second.title # => 'The egalitarian assumption of the modern citizen'
134
135
  #
135
- # Alternatively, :reject_if also accepts a symbol for using methods:
136
+ # Alternatively, :reject_if also accepts a symbol for using methods:
136
137
  #
137
138
  # class Member < ActiveRecord::Base
138
139
  # has_many :posts
@@ -144,7 +145,7 @@ module ActiveRecord
144
145
  # accepts_nested_attributes_for :posts, :reject_if => :reject_posts
145
146
  #
146
147
  # def reject_posts(attributed)
147
- # attributed['title].blank?
148
+ # attributed['title'].blank?
148
149
  # end
149
150
  # end
150
151
  #
@@ -212,7 +213,7 @@ module ActiveRecord
212
213
  # that will reject a record where all the attributes are blank.
213
214
  # [:limit]
214
215
  # Allows you to specify the maximum number of the associated records that
215
- # can be processes with the nested attributes. If the size of the
216
+ # can be processed with the nested attributes. If the size of the
216
217
  # nested attributes array exceeds the specified limit, NestedAttributes::TooManyRecords
217
218
  # exception is raised. If omitted, any number associations can be processed.
218
219
  # Note that the :limit option is only applicable to one-to-many associations.
@@ -278,7 +279,7 @@ module ActiveRecord
278
279
  # Assigns the given attributes to the association.
279
280
  #
280
281
  # If update_only is false and the given attributes include an <tt>:id</tt>
281
- # that matches the existing records id, then the existing record will be
282
+ # that matches the existing record's id, then the existing record will be
282
283
  # modified. If update_only is true, a new record is only created when no
283
284
  # object exists. Otherwise a new record will be built.
284
285
  #
@@ -295,7 +296,9 @@ module ActiveRecord
295
296
  assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy])
296
297
 
297
298
  elsif attributes['id']
298
- raise_nested_attributes_record_not_found(association_name, 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)
299
302
 
300
303
  elsif !reject_new_record?(association_name, attributes)
301
304
  method = "build_#{association_name}"
@@ -365,11 +368,16 @@ module ActiveRecord
365
368
  unless reject_new_record?(association_name, attributes)
366
369
  association.build(attributes.except(*UNASSIGNABLE_KEYS))
367
370
  end
371
+
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
+
368
377
  elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
369
378
  association.send(:add_record_to_target_with_callbacks, existing_record) unless association.loaded?
370
379
  assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
371
- else
372
- raise_nested_attributes_record_not_found(association_name, attributes['id'])
380
+
373
381
  end
374
382
  end
375
383
  end
@@ -389,7 +397,7 @@ module ActiveRecord
389
397
  ConnectionAdapters::Column.value_to_boolean(hash['_destroy'])
390
398
  end
391
399
 
392
- # Determines if a new record should be build by checking for
400
+ # Determines if a new record should be built by checking for
393
401
  # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
394
402
  # association and evaluates to +true+.
395
403
  def reject_new_record?(association_name, attributes)
@@ -405,9 +413,5 @@ module ActiveRecord
405
413
  end
406
414
  end
407
415
 
408
- def raise_nested_attributes_record_not_found(association_name, record_id)
409
- reflection = self.class.reflect_on_association(association_name)
410
- raise RecordNotFound, "Couldn't find #{reflection.klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
411
- end
412
416
  end
413
417
  end