activerecord 3.0.0 → 4.0.0

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 (181) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +35 -44
  5. data/examples/performance.rb +110 -100
  6. data/lib/active_record/aggregations.rb +59 -75
  7. data/lib/active_record/associations/alias_tracker.rb +76 -0
  8. data/lib/active_record/associations/association.rb +248 -0
  9. data/lib/active_record/associations/association_scope.rb +135 -0
  10. data/lib/active_record/associations/belongs_to_association.rb +60 -59
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +16 -59
  12. data/lib/active_record/associations/builder/association.rb +108 -0
  13. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  14. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  16. data/lib/active_record/associations/builder/has_many.rb +15 -0
  17. data/lib/active_record/associations/builder/has_one.rb +25 -0
  18. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  19. data/lib/active_record/associations/collection_association.rb +608 -0
  20. data/lib/active_record/associations/collection_proxy.rb +986 -0
  21. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +40 -112
  22. data/lib/active_record/associations/has_many_association.rb +83 -76
  23. data/lib/active_record/associations/has_many_through_association.rb +147 -66
  24. data/lib/active_record/associations/has_one_association.rb +67 -108
  25. data/lib/active_record/associations/has_one_through_association.rb +21 -25
  26. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  27. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  28. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  29. data/lib/active_record/associations/join_dependency.rb +235 -0
  30. data/lib/active_record/associations/join_helper.rb +45 -0
  31. data/lib/active_record/associations/preloader/association.rb +121 -0
  32. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  33. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  35. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  36. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  37. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  38. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  39. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  40. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  41. data/lib/active_record/associations/preloader.rb +178 -0
  42. data/lib/active_record/associations/singular_association.rb +64 -0
  43. data/lib/active_record/associations/through_association.rb +87 -0
  44. data/lib/active_record/associations.rb +512 -1224
  45. data/lib/active_record/attribute_assignment.rb +201 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +49 -12
  47. data/lib/active_record/attribute_methods/dirty.rb +51 -28
  48. data/lib/active_record/attribute_methods/primary_key.rb +94 -22
  49. data/lib/active_record/attribute_methods/query.rb +5 -4
  50. data/lib/active_record/attribute_methods/read.rb +63 -72
  51. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +39 -41
  53. data/lib/active_record/attribute_methods/write.rb +39 -13
  54. data/lib/active_record/attribute_methods.rb +362 -29
  55. data/lib/active_record/autosave_association.rb +132 -75
  56. data/lib/active_record/base.rb +83 -1627
  57. data/lib/active_record/callbacks.rb +69 -47
  58. data/lib/active_record/coders/yaml_column.rb +38 -0
  59. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +411 -138
  60. data/lib/active_record/connection_adapters/abstract/database_limits.rb +21 -11
  61. data/lib/active_record/connection_adapters/abstract/database_statements.rb +234 -173
  62. data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -22
  63. data/lib/active_record/connection_adapters/abstract/quoting.rb +82 -25
  64. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +176 -414
  65. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +562 -232
  67. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  68. data/lib/active_record/connection_adapters/abstract_adapter.rb +281 -53
  69. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  70. data/lib/active_record/connection_adapters/column.rb +318 -0
  71. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  73. data/lib/active_record/connection_adapters/mysql_adapter.rb +365 -450
  74. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  75. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  81. data/lib/active_record/connection_adapters/postgresql_adapter.rb +672 -752
  82. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  83. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +588 -17
  84. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  85. data/lib/active_record/connection_handling.rb +98 -0
  86. data/lib/active_record/core.rb +463 -0
  87. data/lib/active_record/counter_cache.rb +108 -101
  88. data/lib/active_record/dynamic_matchers.rb +131 -0
  89. data/lib/active_record/errors.rb +54 -13
  90. data/lib/active_record/explain.rb +38 -0
  91. data/lib/active_record/explain_registry.rb +30 -0
  92. data/lib/active_record/explain_subscriber.rb +29 -0
  93. data/lib/active_record/fixture_set/file.rb +55 -0
  94. data/lib/active_record/fixtures.rb +703 -785
  95. data/lib/active_record/inheritance.rb +200 -0
  96. data/lib/active_record/integration.rb +60 -0
  97. data/lib/active_record/locale/en.yml +8 -1
  98. data/lib/active_record/locking/optimistic.rb +69 -60
  99. data/lib/active_record/locking/pessimistic.rb +34 -12
  100. data/lib/active_record/log_subscriber.rb +40 -6
  101. data/lib/active_record/migration/command_recorder.rb +164 -0
  102. data/lib/active_record/migration/join_table.rb +15 -0
  103. data/lib/active_record/migration.rb +614 -216
  104. data/lib/active_record/model_schema.rb +345 -0
  105. data/lib/active_record/nested_attributes.rb +248 -119
  106. data/lib/active_record/null_relation.rb +65 -0
  107. data/lib/active_record/persistence.rb +275 -57
  108. data/lib/active_record/query_cache.rb +29 -9
  109. data/lib/active_record/querying.rb +62 -0
  110. data/lib/active_record/railtie.rb +135 -21
  111. data/lib/active_record/railties/console_sandbox.rb +5 -0
  112. data/lib/active_record/railties/controller_runtime.rb +17 -5
  113. data/lib/active_record/railties/databases.rake +249 -359
  114. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  115. data/lib/active_record/readonly_attributes.rb +30 -0
  116. data/lib/active_record/reflection.rb +283 -103
  117. data/lib/active_record/relation/batches.rb +38 -34
  118. data/lib/active_record/relation/calculations.rb +252 -139
  119. data/lib/active_record/relation/delegation.rb +125 -0
  120. data/lib/active_record/relation/finder_methods.rb +182 -188
  121. data/lib/active_record/relation/merger.rb +161 -0
  122. data/lib/active_record/relation/predicate_builder.rb +86 -21
  123. data/lib/active_record/relation/query_methods.rb +917 -134
  124. data/lib/active_record/relation/spawn_methods.rb +53 -92
  125. data/lib/active_record/relation.rb +405 -143
  126. data/lib/active_record/result.rb +67 -0
  127. data/lib/active_record/runtime_registry.rb +17 -0
  128. data/lib/active_record/sanitization.rb +168 -0
  129. data/lib/active_record/schema.rb +20 -14
  130. data/lib/active_record/schema_dumper.rb +55 -46
  131. data/lib/active_record/schema_migration.rb +39 -0
  132. data/lib/active_record/scoping/default.rb +146 -0
  133. data/lib/active_record/scoping/named.rb +175 -0
  134. data/lib/active_record/scoping.rb +82 -0
  135. data/lib/active_record/serialization.rb +8 -46
  136. data/lib/active_record/serializers/xml_serializer.rb +21 -68
  137. data/lib/active_record/statement_cache.rb +26 -0
  138. data/lib/active_record/store.rb +156 -0
  139. data/lib/active_record/tasks/database_tasks.rb +203 -0
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  141. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  142. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  143. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  144. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  145. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  146. data/lib/active_record/test_case.rb +57 -28
  147. data/lib/active_record/timestamp.rb +49 -18
  148. data/lib/active_record/transactions.rb +106 -63
  149. data/lib/active_record/translation.rb +22 -0
  150. data/lib/active_record/validations/associated.rb +25 -24
  151. data/lib/active_record/validations/presence.rb +65 -0
  152. data/lib/active_record/validations/uniqueness.rb +123 -83
  153. data/lib/active_record/validations.rb +29 -29
  154. data/lib/active_record/version.rb +7 -5
  155. data/lib/active_record.rb +83 -34
  156. data/lib/rails/generators/active_record/migration/migration_generator.rb +46 -9
  157. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  158. data/lib/rails/generators/active_record/migration/templates/migration.rb +30 -8
  159. data/lib/rails/generators/active_record/model/model_generator.rb +15 -5
  160. data/lib/rails/generators/active_record/model/templates/model.rb +7 -2
  161. data/lib/rails/generators/active_record/model/templates/module.rb +3 -1
  162. data/lib/rails/generators/active_record.rb +4 -8
  163. metadata +163 -121
  164. data/CHANGELOG +0 -6023
  165. data/examples/associations.png +0 -0
  166. data/lib/active_record/association_preload.rb +0 -403
  167. data/lib/active_record/associations/association_collection.rb +0 -562
  168. data/lib/active_record/associations/association_proxy.rb +0 -295
  169. data/lib/active_record/associations/through_association_scope.rb +0 -154
  170. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -113
  171. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -401
  172. data/lib/active_record/dynamic_finder_match.rb +0 -53
  173. data/lib/active_record/dynamic_scope_match.rb +0 -32
  174. data/lib/active_record/named_scope.rb +0 -138
  175. data/lib/active_record/observer.rb +0 -140
  176. data/lib/active_record/session_store.rb +0 -340
  177. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -16
  178. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  179. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -2
  180. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -24
  181. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -1,10 +1,8 @@
1
- require 'active_support/core_ext/array/wrap'
2
-
3
1
  module ActiveRecord
4
2
  # = Active Record Autosave Association
5
3
  #
6
4
  # +AutosaveAssociation+ is a module that takes care of automatically saving
7
- # associacted records when their parent is saved. In addition to saving, it
5
+ # associated records when their parent is saved. In addition to saving, it
8
6
  # also destroys any associated records that were marked for destruction.
9
7
  # (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
10
8
  #
@@ -18,13 +16,27 @@ module ActiveRecord
18
16
  # Note that it also means that associations marked for destruction won't
19
17
  # be destroyed directly. They will however still be marked for destruction.
20
18
  #
21
- # Note that <tt>:autosave => false</tt> is not same as not declaring <tt>:autosave</tt>.
22
- # When the <tt>:autosave</tt> option is not present new associations are saved.
19
+ # Note that <tt>autosave: false</tt> is not same as not declaring <tt>:autosave</tt>.
20
+ # When the <tt>:autosave</tt> option is not present then new association records are
21
+ # saved but the updated association records are not saved.
22
+ #
23
+ # == Validation
24
+ #
25
+ # Children records are validated unless <tt>:validate</tt> is +false+.
26
+ #
27
+ # == Callbacks
28
+ #
29
+ # Association with autosave option defines several callbacks on your
30
+ # model (before_save, after_create, after_update). Please note that
31
+ # callbacks are executed in the order they were defined in
32
+ # model. You should avoid modifying the association content, before
33
+ # autosave callbacks are executed. Placing your callbacks after
34
+ # associations is usually a good practice.
23
35
  #
24
36
  # === One-to-one Example
25
37
  #
26
38
  # class Post
27
- # has_one :author, :autosave => true
39
+ # has_one :author, autosave: true
28
40
  # end
29
41
  #
30
42
  # Saving changes to the parent and its associated model can now be performed
@@ -51,45 +63,46 @@ module ActiveRecord
51
63
  # Note that the model is _not_ yet removed from the database:
52
64
  #
53
65
  # id = post.author.id
54
- # Author.find_by_id(id).nil? # => false
66
+ # Author.find_by(id: id).nil? # => false
55
67
  #
56
68
  # post.save
57
69
  # post.reload.author # => nil
58
70
  #
59
71
  # Now it _is_ removed from the database:
60
72
  #
61
- # Author.find_by_id(id).nil? # => true
73
+ # Author.find_by(id: id).nil? # => true
62
74
  #
63
75
  # === One-to-many Example
64
76
  #
65
77
  # When <tt>:autosave</tt> is not declared new children are saved when their parent is saved:
66
78
  #
67
79
  # class Post
68
- # has_many :comments # :autosave option is no declared
80
+ # has_many :comments # :autosave option is not declared
69
81
  # end
70
82
  #
71
- # post = Post.new(:title => 'ruby rocks')
72
- # post.comments.build(:body => 'hello world')
83
+ # post = Post.new(title: 'ruby rocks')
84
+ # post.comments.build(body: 'hello world')
73
85
  # post.save # => saves both post and comment
74
86
  #
75
- # post = Post.create(:title => 'ruby rocks')
76
- # post.comments.build(:body => 'hello world')
87
+ # post = Post.create(title: 'ruby rocks')
88
+ # post.comments.build(body: 'hello world')
77
89
  # post.save # => saves both post and comment
78
90
  #
79
- # post = Post.create(:title => 'ruby rocks')
80
- # post.comments.create(:body => 'hello world')
91
+ # post = Post.create(title: 'ruby rocks')
92
+ # post.comments.create(body: 'hello world')
81
93
  # post.save # => saves both post and comment
82
94
  #
83
- # When <tt>:autosave</tt> is true all children is saved, no matter whether they are new records:
95
+ # When <tt>:autosave</tt> is true all children are saved, no matter whether they
96
+ # are new records or not:
84
97
  #
85
98
  # class Post
86
- # has_many :comments, :autosave => true
99
+ # has_many :comments, autosave: true
87
100
  # end
88
101
  #
89
- # post = Post.create(:title => 'ruby rocks')
90
- # post.comments.create(:body => 'hello world')
102
+ # post = Post.create(title: 'ruby rocks')
103
+ # post.comments.create(body: 'hello world')
91
104
  # post.comments[0].body = 'hi everyone'
92
- # post.save # => saves both post and comment, with 'hi everyone' as title
105
+ # post.save # => saves both post and comment, with 'hi everyone' as body
93
106
  #
94
107
  # Destroying one of the associated models as part of the parent's save action
95
108
  # is as simple as marking it for destruction:
@@ -101,43 +114,50 @@ module ActiveRecord
101
114
  # Note that the model is _not_ yet removed from the database:
102
115
  #
103
116
  # id = post.comments.last.id
104
- # Comment.find_by_id(id).nil? # => false
117
+ # Comment.find_by(id: id).nil? # => false
105
118
  #
106
119
  # post.save
107
120
  # post.reload.comments.length # => 1
108
121
  #
109
122
  # Now it _is_ removed from the database:
110
123
  #
111
- # Comment.find_by_id(id).nil? # => true
112
- #
113
- # === Validation
114
- #
115
- # Children records are validated unless <tt>:validate</tt> is +false+.
124
+ # Comment.find_by(id: id).nil? # => true
125
+
116
126
  module AutosaveAssociation
117
127
  extend ActiveSupport::Concern
118
128
 
119
- ASSOCIATION_TYPES = %w{ has_one belongs_to has_many has_and_belongs_to_many }
129
+ module AssociationBuilderExtension #:nodoc:
130
+ def build
131
+ model.send(:add_autosave_association_callbacks, reflection)
132
+ super
133
+ end
134
+ end
120
135
 
121
136
  included do
122
- ASSOCIATION_TYPES.each do |type|
123
- send("valid_keys_for_#{type}_association") << :autosave
137
+ Associations::Builder::Association.class_eval do
138
+ self.valid_options << :autosave
139
+ include AssociationBuilderExtension
124
140
  end
125
141
  end
126
142
 
127
143
  module ClassMethods
128
144
  private
129
145
 
130
- # def belongs_to(name, options = {})
131
- # super
132
- # add_autosave_association_callbacks(reflect_on_association(name))
133
- # end
134
- ASSOCIATION_TYPES.each do |type|
135
- module_eval <<-CODE, __FILE__, __LINE__ + 1
136
- def #{type}(name, options = {})
137
- super
138
- add_autosave_association_callbacks(reflect_on_association(name))
146
+ def define_non_cyclic_method(name, reflection, &block)
147
+ define_method(name) do |*args|
148
+ result = true; @_already_called ||= {}
149
+ # Loop prevention for validation of associations
150
+ unless @_already_called[[name, reflection.name]]
151
+ begin
152
+ @_already_called[[name, reflection.name]]=true
153
+ result = instance_eval(&block)
154
+ ensure
155
+ @_already_called[[name, reflection.name]]=false
156
+ end
139
157
  end
140
- CODE
158
+
159
+ result
160
+ end
141
161
  end
142
162
 
143
163
  # Adds validation and save callbacks for the association as specified by
@@ -145,7 +165,7 @@ module ActiveRecord
145
165
  #
146
166
  # For performance reasons, we don't check whether to validate at runtime.
147
167
  # However the validation and callback methods are lazy and those methods
148
- # get created when they are invoked for the very first time. However,
168
+ # get created when they are invoked for the very first time. However,
149
169
  # this can change, for instance, when using nested attributes, which is
150
170
  # called _after_ the association has been defined. Since we don't want
151
171
  # the callbacks to get defined multiple times, there are guards that
@@ -160,24 +180,31 @@ module ActiveRecord
160
180
  if collection
161
181
  before_save :before_save_collection_association
162
182
 
163
- define_method(save_method) { save_collection_association(reflection) }
183
+ define_non_cyclic_method(save_method, reflection) { save_collection_association(reflection) }
164
184
  # Doesn't use after_save as that would save associations added in after_create/after_update twice
165
185
  after_create save_method
166
186
  after_update save_method
187
+ elsif reflection.macro == :has_one
188
+ define_method(save_method) { save_has_one_association(reflection) }
189
+ # Configures two callbacks instead of a single after_save so that
190
+ # the model may rely on their execution order relative to its
191
+ # own callbacks.
192
+ #
193
+ # For example, given that after_creates run before after_saves, if
194
+ # we configured instead an after_save there would be no way to fire
195
+ # a custom after_create callback after the child association gets
196
+ # created.
197
+ after_create save_method
198
+ after_update save_method
167
199
  else
168
- if reflection.macro == :has_one
169
- define_method(save_method) { save_has_one_association(reflection) }
170
- after_save save_method
171
- else
172
- define_method(save_method) { save_belongs_to_association(reflection) }
173
- before_save save_method
174
- end
200
+ define_non_cyclic_method(save_method, reflection) { save_belongs_to_association(reflection) }
201
+ before_save save_method
175
202
  end
176
203
  end
177
204
 
178
205
  if reflection.validate? && !method_defined?(validation_method)
179
206
  method = (collection ? :validate_collection_association : :validate_single_association)
180
- define_method(validation_method) { send(method, reflection) }
207
+ define_non_cyclic_method(validation_method, reflection) { send(method, reflection) }
181
208
  validate validation_method
182
209
  end
183
210
  end
@@ -186,6 +213,7 @@ module ActiveRecord
186
213
  # Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
187
214
  def reload(options = nil)
188
215
  @marked_for_destruction = false
216
+ @destroyed_by_association = nil
189
217
  super
190
218
  end
191
219
 
@@ -205,6 +233,19 @@ module ActiveRecord
205
233
  @marked_for_destruction
206
234
  end
207
235
 
236
+ # Records the association that is being destroyed and destroying this
237
+ # record in the process.
238
+ def destroyed_by_association=(reflection)
239
+ @destroyed_by_association = reflection
240
+ end
241
+
242
+ # Returns the association for the parent being destroyed.
243
+ #
244
+ # Used to avoid updating the counter cache unnecessarily.
245
+ def destroyed_by_association
246
+ @destroyed_by_association
247
+ end
248
+
208
249
  # Returns whether or not this record has been changed in any way (including whether
209
250
  # any of its nested autosave associations are likewise changed)
210
251
  def changed_for_autosave?
@@ -218,7 +259,7 @@ module ActiveRecord
218
259
  # unless the parent is/was a new record itself.
219
260
  def associated_records_to_validate_or_save(association, new_record, autosave)
220
261
  if new_record
221
- association
262
+ association && association.target
222
263
  elsif autosave
223
264
  association.target.find_all { |record| record.changed_for_autosave? }
224
265
  else
@@ -238,9 +279,9 @@ module ActiveRecord
238
279
  # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
239
280
  # turned on for the association.
240
281
  def validate_single_association(reflection)
241
- if (association = association_instance_get(reflection.name)) && !association.target.nil?
242
- association_valid?(reflection, association)
243
- end
282
+ association = association_instance_get(reflection.name)
283
+ record = association && association.reader
284
+ association_valid?(reflection, record) if record
244
285
  end
245
286
 
246
287
  # Validate the associated records if <tt>:validate</tt> or
@@ -257,12 +298,12 @@ module ActiveRecord
257
298
  # Returns whether or not the association is valid and applies any errors to
258
299
  # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
259
300
  # enabled records if they're marked_for_destruction? or destroyed.
260
- def association_valid?(reflection, association)
261
- return true if association.destroyed? || association.marked_for_destruction?
301
+ def association_valid?(reflection, record)
302
+ return true if record.destroyed? || record.marked_for_destruction?
262
303
 
263
- unless valid = association.valid?
304
+ unless valid = record.valid?
264
305
  if reflection.options[:autosave]
265
- association.errors.each do |attribute, message|
306
+ record.errors.each do |attribute, message|
266
307
  attribute = "#{reflection.name}.#{attribute}"
267
308
  errors[attribute] << message
268
309
  errors[attribute].uniq!
@@ -294,27 +335,34 @@ module ActiveRecord
294
335
  autosave = reflection.options[:autosave]
295
336
 
296
337
  if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
338
+ records_to_destroy = []
297
339
  records.each do |record|
298
340
  next if record.destroyed?
299
341
 
342
+ saved = true
343
+
300
344
  if autosave && record.marked_for_destruction?
301
- association.destroy(record)
345
+ records_to_destroy << record
302
346
  elsif autosave != false && (@new_record_before_save || record.new_record?)
303
347
  if autosave
304
- saved = association.send(:insert_record, record, false, false)
348
+ saved = association.insert_record(record, false)
305
349
  else
306
- association.send(:insert_record, record)
350
+ association.insert_record(record) unless reflection.nested?
307
351
  end
308
352
  elsif autosave
309
353
  saved = record.save(:validate => false)
310
354
  end
311
355
 
312
- raise ActiveRecord::Rollback if saved == false
356
+ raise ActiveRecord::Rollback unless saved
357
+ end
358
+
359
+ records_to_destroy.each do |record|
360
+ association.destroy(record)
313
361
  end
314
362
  end
315
363
 
316
- # reconstruct the SQL queries now that we know the owner's id
317
- association.send(:construct_sql) if association.respond_to?(:construct_sql)
364
+ # reconstruct the scope now that we know the owner's id
365
+ association.reset_scope if association.respond_to?(:reset_scope)
318
366
  end
319
367
  end
320
368
 
@@ -327,16 +375,21 @@ module ActiveRecord
327
375
  # This all happens inside a transaction, _if_ the Transactions module is included into
328
376
  # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
329
377
  def save_has_one_association(reflection)
330
- if (association = association_instance_get(reflection.name)) && !association.target.nil? && !association.destroyed?
378
+ association = association_instance_get(reflection.name)
379
+ record = association && association.load_target
380
+ if record && !record.destroyed?
331
381
  autosave = reflection.options[:autosave]
332
382
 
333
- if autosave && association.marked_for_destruction?
334
- association.destroy
383
+ if autosave && record.marked_for_destruction?
384
+ record.destroy
335
385
  else
336
386
  key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
337
- if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave)
338
- association[reflection.primary_key_name] = key
339
- saved = association.save(:validate => !autosave)
387
+ if autosave != false && (new_record? || record.new_record? || record[reflection.foreign_key] != key || autosave)
388
+ unless reflection.through_reflection
389
+ record[reflection.foreign_key] = key
390
+ end
391
+
392
+ saved = record.save(:validate => !autosave)
340
393
  raise ActiveRecord::Rollback if !saved && autosave
341
394
  saved
342
395
  end
@@ -348,17 +401,21 @@ module ActiveRecord
348
401
  #
349
402
  # In addition, it will destroy the association if it was marked for destruction.
350
403
  def save_belongs_to_association(reflection)
351
- if (association = association_instance_get(reflection.name)) && !association.destroyed?
404
+ association = association_instance_get(reflection.name)
405
+ record = association && association.load_target
406
+ if record && !record.destroyed?
352
407
  autosave = reflection.options[:autosave]
353
408
 
354
- if autosave && association.marked_for_destruction?
355
- association.destroy
409
+ if autosave && record.marked_for_destruction?
410
+ self[reflection.foreign_key] = nil
411
+ record.destroy
356
412
  elsif autosave != false
357
- saved = association.save(:validate => !autosave) if association.new_record? || autosave
413
+ saved = record.save(:validate => !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
358
414
 
359
415
  if association.updated?
360
- association_id = association.send(reflection.options[:primary_key] || :id)
361
- self[reflection.primary_key_name] = association_id
416
+ association_id = record.send(reflection.options[:primary_key] || :id)
417
+ self[reflection.foreign_key] = association_id
418
+ association.loaded!
362
419
  end
363
420
 
364
421
  saved if autosave