activerecord 1.0.0 → 3.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 (178) hide show
  1. data/CHANGELOG +5518 -76
  2. data/README.rdoc +222 -0
  3. data/examples/performance.rb +162 -0
  4. data/examples/simple.rb +14 -0
  5. data/lib/active_record/aggregations.rb +192 -80
  6. data/lib/active_record/association_preload.rb +403 -0
  7. data/lib/active_record/associations/association_collection.rb +545 -53
  8. data/lib/active_record/associations/association_proxy.rb +295 -0
  9. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +127 -36
  12. data/lib/active_record/associations/has_many_association.rb +108 -84
  13. data/lib/active_record/associations/has_many_through_association.rb +116 -0
  14. data/lib/active_record/associations/has_one_association.rb +143 -0
  15. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  16. data/lib/active_record/associations/through_association_scope.rb +154 -0
  17. data/lib/active_record/associations.rb +2086 -368
  18. data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
  19. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  20. data/lib/active_record/attribute_methods/primary_key.rb +50 -0
  21. data/lib/active_record/attribute_methods/query.rb +39 -0
  22. data/lib/active_record/attribute_methods/read.rb +116 -0
  23. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
  24. data/lib/active_record/attribute_methods/write.rb +37 -0
  25. data/lib/active_record/attribute_methods.rb +60 -0
  26. data/lib/active_record/autosave_association.rb +369 -0
  27. data/lib/active_record/base.rb +1603 -721
  28. data/lib/active_record/callbacks.rb +176 -225
  29. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -0
  30. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  31. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  32. data/lib/active_record/connection_adapters/abstract/database_statements.rb +329 -0
  33. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  34. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -0
  35. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  36. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +543 -0
  37. data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -279
  38. data/lib/active_record/connection_adapters/mysql_adapter.rb +594 -82
  39. data/lib/active_record/connection_adapters/postgresql_adapter.rb +988 -135
  40. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -0
  41. data/lib/active_record/connection_adapters/sqlite_adapter.rb +365 -71
  42. data/lib/active_record/counter_cache.rb +115 -0
  43. data/lib/active_record/dynamic_finder_match.rb +53 -0
  44. data/lib/active_record/dynamic_scope_match.rb +32 -0
  45. data/lib/active_record/errors.rb +172 -0
  46. data/lib/active_record/fixtures.rb +941 -105
  47. data/lib/active_record/locale/en.yml +40 -0
  48. data/lib/active_record/locking/optimistic.rb +172 -0
  49. data/lib/active_record/locking/pessimistic.rb +55 -0
  50. data/lib/active_record/log_subscriber.rb +48 -0
  51. data/lib/active_record/migration.rb +617 -0
  52. data/lib/active_record/named_scope.rb +138 -0
  53. data/lib/active_record/nested_attributes.rb +417 -0
  54. data/lib/active_record/observer.rb +105 -36
  55. data/lib/active_record/persistence.rb +291 -0
  56. data/lib/active_record/query_cache.rb +36 -0
  57. data/lib/active_record/railtie.rb +91 -0
  58. data/lib/active_record/railties/controller_runtime.rb +38 -0
  59. data/lib/active_record/railties/databases.rake +512 -0
  60. data/lib/active_record/reflection.rb +364 -87
  61. data/lib/active_record/relation/batches.rb +89 -0
  62. data/lib/active_record/relation/calculations.rb +286 -0
  63. data/lib/active_record/relation/finder_methods.rb +355 -0
  64. data/lib/active_record/relation/predicate_builder.rb +41 -0
  65. data/lib/active_record/relation/query_methods.rb +261 -0
  66. data/lib/active_record/relation/spawn_methods.rb +112 -0
  67. data/lib/active_record/relation.rb +393 -0
  68. data/lib/active_record/schema.rb +59 -0
  69. data/lib/active_record/schema_dumper.rb +195 -0
  70. data/lib/active_record/serialization.rb +60 -0
  71. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  72. data/lib/active_record/session_store.rb +340 -0
  73. data/lib/active_record/test_case.rb +67 -0
  74. data/lib/active_record/timestamp.rb +88 -0
  75. data/lib/active_record/transactions.rb +329 -75
  76. data/lib/active_record/validations/associated.rb +48 -0
  77. data/lib/active_record/validations/uniqueness.rb +185 -0
  78. data/lib/active_record/validations.rb +58 -179
  79. data/lib/active_record/version.rb +9 -0
  80. data/lib/active_record.rb +100 -24
  81. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  82. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  83. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  84. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  85. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  86. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  87. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  88. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  89. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  90. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  91. data/lib/rails/generators/active_record.rb +27 -0
  92. metadata +216 -158
  93. data/README +0 -361
  94. data/RUNNING_UNIT_TESTS +0 -36
  95. data/dev-utils/eval_debugger.rb +0 -9
  96. data/examples/associations.rb +0 -87
  97. data/examples/shared_setup.rb +0 -15
  98. data/examples/validation.rb +0 -88
  99. data/install.rb +0 -60
  100. data/lib/active_record/deprecated_associations.rb +0 -70
  101. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  102. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  103. data/lib/active_record/support/clean_logger.rb +0 -10
  104. data/lib/active_record/support/inflector.rb +0 -70
  105. data/lib/active_record/vendor/mysql.rb +0 -1117
  106. data/lib/active_record/vendor/simple.rb +0 -702
  107. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  108. data/lib/active_record/wrappings.rb +0 -59
  109. data/rakefile +0 -122
  110. data/test/abstract_unit.rb +0 -16
  111. data/test/aggregations_test.rb +0 -34
  112. data/test/all.sh +0 -8
  113. data/test/associations_test.rb +0 -477
  114. data/test/base_test.rb +0 -513
  115. data/test/class_inheritable_attributes_test.rb +0 -33
  116. data/test/connections/native_mysql/connection.rb +0 -24
  117. data/test/connections/native_postgresql/connection.rb +0 -24
  118. data/test/connections/native_sqlite/connection.rb +0 -24
  119. data/test/deprecated_associations_test.rb +0 -336
  120. data/test/finder_test.rb +0 -67
  121. data/test/fixtures/accounts/signals37 +0 -3
  122. data/test/fixtures/accounts/unknown +0 -2
  123. data/test/fixtures/auto_id.rb +0 -4
  124. data/test/fixtures/column_name.rb +0 -3
  125. data/test/fixtures/companies/first_client +0 -6
  126. data/test/fixtures/companies/first_firm +0 -4
  127. data/test/fixtures/companies/second_client +0 -6
  128. data/test/fixtures/company.rb +0 -37
  129. data/test/fixtures/company_in_module.rb +0 -33
  130. data/test/fixtures/course.rb +0 -3
  131. data/test/fixtures/courses/java +0 -2
  132. data/test/fixtures/courses/ruby +0 -2
  133. data/test/fixtures/customer.rb +0 -30
  134. data/test/fixtures/customers/david +0 -6
  135. data/test/fixtures/db_definitions/mysql.sql +0 -96
  136. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  137. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  138. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  139. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  140. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  141. data/test/fixtures/default.rb +0 -2
  142. data/test/fixtures/developer.rb +0 -8
  143. data/test/fixtures/developers/david +0 -2
  144. data/test/fixtures/developers/jamis +0 -2
  145. data/test/fixtures/developers_projects/david_action_controller +0 -2
  146. data/test/fixtures/developers_projects/david_active_record +0 -2
  147. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  148. data/test/fixtures/entrant.rb +0 -3
  149. data/test/fixtures/entrants/first +0 -3
  150. data/test/fixtures/entrants/second +0 -3
  151. data/test/fixtures/entrants/third +0 -3
  152. data/test/fixtures/fixture_database.sqlite +0 -0
  153. data/test/fixtures/fixture_database_2.sqlite +0 -0
  154. data/test/fixtures/movie.rb +0 -5
  155. data/test/fixtures/movies/first +0 -2
  156. data/test/fixtures/movies/second +0 -2
  157. data/test/fixtures/project.rb +0 -3
  158. data/test/fixtures/projects/action_controller +0 -2
  159. data/test/fixtures/projects/active_record +0 -2
  160. data/test/fixtures/reply.rb +0 -21
  161. data/test/fixtures/subscriber.rb +0 -5
  162. data/test/fixtures/subscribers/first +0 -2
  163. data/test/fixtures/subscribers/second +0 -2
  164. data/test/fixtures/topic.rb +0 -20
  165. data/test/fixtures/topics/first +0 -9
  166. data/test/fixtures/topics/second +0 -8
  167. data/test/fixtures_test.rb +0 -20
  168. data/test/inflector_test.rb +0 -104
  169. data/test/inheritance_test.rb +0 -125
  170. data/test/lifecycle_test.rb +0 -110
  171. data/test/modules_test.rb +0 -21
  172. data/test/multiple_db_test.rb +0 -46
  173. data/test/pk_test.rb +0 -57
  174. data/test/reflection_test.rb +0 -78
  175. data/test/thread_safety_test.rb +0 -33
  176. data/test/transactions_test.rb +0 -83
  177. data/test/unconnected_test.rb +0 -24
  178. data/test/validations_test.rb +0 -126
@@ -0,0 +1,369 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
3
+ module ActiveRecord
4
+ # = Active Record Autosave Association
5
+ #
6
+ # +AutosaveAssociation+ is a module that takes care of automatically saving
7
+ # associacted records when their parent is saved. In addition to saving, it
8
+ # also destroys any associated records that were marked for destruction.
9
+ # (See +mark_for_destruction+ and <tt>marked_for_destruction?</tt>).
10
+ #
11
+ # Saving of the parent, its associations, and the destruction of marked
12
+ # associations, all happen inside a transaction. This should never leave the
13
+ # database in an inconsistent state.
14
+ #
15
+ # If validations for any of the associations fail, their error messages will
16
+ # be applied to the parent.
17
+ #
18
+ # Note that it also means that associations marked for destruction won't
19
+ # be destroyed directly. They will however still be marked for destruction.
20
+ #
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.
23
+ #
24
+ # === One-to-one Example
25
+ #
26
+ # class Post
27
+ # has_one :author, :autosave => true
28
+ # end
29
+ #
30
+ # Saving changes to the parent and its associated model can now be performed
31
+ # automatically _and_ atomically:
32
+ #
33
+ # post = Post.find(1)
34
+ # post.title # => "The current global position of migrating ducks"
35
+ # post.author.name # => "alloy"
36
+ #
37
+ # post.title = "On the migration of ducks"
38
+ # post.author.name = "Eloy Duran"
39
+ #
40
+ # post.save
41
+ # post.reload
42
+ # post.title # => "On the migration of ducks"
43
+ # post.author.name # => "Eloy Duran"
44
+ #
45
+ # Destroying an associated model, as part of the parent's save action, is as
46
+ # simple as marking it for destruction:
47
+ #
48
+ # post.author.mark_for_destruction
49
+ # post.author.marked_for_destruction? # => true
50
+ #
51
+ # Note that the model is _not_ yet removed from the database:
52
+ #
53
+ # id = post.author.id
54
+ # Author.find_by_id(id).nil? # => false
55
+ #
56
+ # post.save
57
+ # post.reload.author # => nil
58
+ #
59
+ # Now it _is_ removed from the database:
60
+ #
61
+ # Author.find_by_id(id).nil? # => true
62
+ #
63
+ # === One-to-many Example
64
+ #
65
+ # When <tt>:autosave</tt> is not declared new children are saved when their parent is saved:
66
+ #
67
+ # class Post
68
+ # has_many :comments # :autosave option is no declared
69
+ # end
70
+ #
71
+ # post = Post.new(:title => 'ruby rocks')
72
+ # post.comments.build(:body => 'hello world')
73
+ # post.save # => saves both post and comment
74
+ #
75
+ # post = Post.create(:title => 'ruby rocks')
76
+ # post.comments.build(:body => 'hello world')
77
+ # post.save # => saves both post and comment
78
+ #
79
+ # post = Post.create(:title => 'ruby rocks')
80
+ # post.comments.create(:body => 'hello world')
81
+ # post.save # => saves both post and comment
82
+ #
83
+ # When <tt>:autosave</tt> is true all children is saved, no matter whether they are new records:
84
+ #
85
+ # class Post
86
+ # has_many :comments, :autosave => true
87
+ # end
88
+ #
89
+ # post = Post.create(:title => 'ruby rocks')
90
+ # post.comments.create(:body => 'hello world')
91
+ # post.comments[0].body = 'hi everyone'
92
+ # post.save # => saves both post and comment, with 'hi everyone' as title
93
+ #
94
+ # Destroying one of the associated models as part of the parent's save action
95
+ # is as simple as marking it for destruction:
96
+ #
97
+ # post.comments.last.mark_for_destruction
98
+ # post.comments.last.marked_for_destruction? # => true
99
+ # post.comments.length # => 2
100
+ #
101
+ # Note that the model is _not_ yet removed from the database:
102
+ #
103
+ # id = post.comments.last.id
104
+ # Comment.find_by_id(id).nil? # => false
105
+ #
106
+ # post.save
107
+ # post.reload.comments.length # => 1
108
+ #
109
+ # Now it _is_ removed from the database:
110
+ #
111
+ # Comment.find_by_id(id).nil? # => true
112
+ #
113
+ # === Validation
114
+ #
115
+ # Children records are validated unless <tt>:validate</tt> is +false+.
116
+ module AutosaveAssociation
117
+ extend ActiveSupport::Concern
118
+
119
+ ASSOCIATION_TYPES = %w{ has_one belongs_to has_many has_and_belongs_to_many }
120
+
121
+ included do
122
+ ASSOCIATION_TYPES.each do |type|
123
+ send("valid_keys_for_#{type}_association") << :autosave
124
+ end
125
+ end
126
+
127
+ module ClassMethods
128
+ private
129
+
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))
139
+ end
140
+ CODE
141
+ end
142
+
143
+ # Adds validation and save callbacks for the association as specified by
144
+ # the +reflection+.
145
+ #
146
+ # For performance reasons, we don't check whether to validate at runtime.
147
+ # However the validation and callback methods are lazy and those methods
148
+ # get created when they are invoked for the very first time. However,
149
+ # this can change, for instance, when using nested attributes, which is
150
+ # called _after_ the association has been defined. Since we don't want
151
+ # the callbacks to get defined multiple times, there are guards that
152
+ # check if the save or validation methods have already been defined
153
+ # before actually defining them.
154
+ def add_autosave_association_callbacks(reflection)
155
+ save_method = :"autosave_associated_records_for_#{reflection.name}"
156
+ validation_method = :"validate_associated_records_for_#{reflection.name}"
157
+ collection = reflection.collection?
158
+
159
+ unless method_defined?(save_method)
160
+ if collection
161
+ before_save :before_save_collection_association
162
+
163
+ define_method(save_method) { save_collection_association(reflection) }
164
+ # Doesn't use after_save as that would save associations added in after_create/after_update twice
165
+ after_create save_method
166
+ after_update save_method
167
+ 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
175
+ end
176
+ end
177
+
178
+ if reflection.validate? && !method_defined?(validation_method)
179
+ method = (collection ? :validate_collection_association : :validate_single_association)
180
+ define_method(validation_method) { send(method, reflection) }
181
+ validate validation_method
182
+ end
183
+ end
184
+ end
185
+
186
+ # Reloads the attributes of the object as usual and clears <tt>marked_for_destruction</tt> flag.
187
+ def reload(options = nil)
188
+ @marked_for_destruction = false
189
+ super
190
+ end
191
+
192
+ # Marks this record to be destroyed as part of the parents save transaction.
193
+ # This does _not_ actually destroy the record instantly, rather child record will be destroyed
194
+ # when <tt>parent.save</tt> is called.
195
+ #
196
+ # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
197
+ def mark_for_destruction
198
+ @marked_for_destruction = true
199
+ end
200
+
201
+ # Returns whether or not this record will be destroyed as part of the parents save transaction.
202
+ #
203
+ # Only useful if the <tt>:autosave</tt> option on the parent is enabled for this associated model.
204
+ def marked_for_destruction?
205
+ @marked_for_destruction
206
+ end
207
+
208
+ # Returns whether or not this record has been changed in any way (including whether
209
+ # any of its nested autosave associations are likewise changed)
210
+ def changed_for_autosave?
211
+ new_record? || changed? || marked_for_destruction? || nested_records_changed_for_autosave?
212
+ end
213
+
214
+ private
215
+
216
+ # Returns the record for an association collection that should be validated
217
+ # or saved. If +autosave+ is +false+ only new records will be returned,
218
+ # unless the parent is/was a new record itself.
219
+ def associated_records_to_validate_or_save(association, new_record, autosave)
220
+ if new_record
221
+ association
222
+ elsif autosave
223
+ association.target.find_all { |record| record.changed_for_autosave? }
224
+ else
225
+ association.target.find_all { |record| record.new_record? }
226
+ end
227
+ end
228
+
229
+ # go through nested autosave associations that are loaded in memory (without loading
230
+ # any new ones), and return true if is changed for autosave
231
+ def nested_records_changed_for_autosave?
232
+ self.class.reflect_on_all_autosave_associations.any? do |reflection|
233
+ association = association_instance_get(reflection.name)
234
+ association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? }
235
+ end
236
+ end
237
+
238
+ # Validate the association if <tt>:validate</tt> or <tt>:autosave</tt> is
239
+ # turned on for the association.
240
+ def validate_single_association(reflection)
241
+ if (association = association_instance_get(reflection.name)) && !association.target.nil?
242
+ association_valid?(reflection, association)
243
+ end
244
+ end
245
+
246
+ # Validate the associated records if <tt>:validate</tt> or
247
+ # <tt>:autosave</tt> is turned on for the association specified by
248
+ # +reflection+.
249
+ def validate_collection_association(reflection)
250
+ if association = association_instance_get(reflection.name)
251
+ if records = associated_records_to_validate_or_save(association, new_record?, reflection.options[:autosave])
252
+ records.each { |record| association_valid?(reflection, record) }
253
+ end
254
+ end
255
+ end
256
+
257
+ # Returns whether or not the association is valid and applies any errors to
258
+ # the parent, <tt>self</tt>, if it wasn't. Skips any <tt>:autosave</tt>
259
+ # 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?
262
+
263
+ unless valid = association.valid?
264
+ if reflection.options[:autosave]
265
+ association.errors.each do |attribute, message|
266
+ attribute = "#{reflection.name}.#{attribute}"
267
+ errors[attribute] << message
268
+ errors[attribute].uniq!
269
+ end
270
+ else
271
+ errors.add(reflection.name)
272
+ end
273
+ end
274
+ valid
275
+ end
276
+
277
+ # Is used as a before_save callback to check while saving a collection
278
+ # association whether or not the parent was a new record before saving.
279
+ def before_save_collection_association
280
+ @new_record_before_save = new_record?
281
+ true
282
+ end
283
+
284
+ # Saves any new associated records, or all loaded autosave associations if
285
+ # <tt>:autosave</tt> is enabled on the association.
286
+ #
287
+ # In addition, it destroys all children that were marked for destruction
288
+ # with mark_for_destruction.
289
+ #
290
+ # This all happens inside a transaction, _if_ the Transactions module is included into
291
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
292
+ def save_collection_association(reflection)
293
+ if association = association_instance_get(reflection.name)
294
+ autosave = reflection.options[:autosave]
295
+
296
+ if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
297
+ records.each do |record|
298
+ next if record.destroyed?
299
+
300
+ if autosave && record.marked_for_destruction?
301
+ association.destroy(record)
302
+ elsif autosave != false && (@new_record_before_save || record.new_record?)
303
+ if autosave
304
+ saved = association.send(:insert_record, record, false, false)
305
+ else
306
+ association.send(:insert_record, record)
307
+ end
308
+ elsif autosave
309
+ saved = record.save(:validate => false)
310
+ end
311
+
312
+ raise ActiveRecord::Rollback if saved == false
313
+ end
314
+ end
315
+
316
+ # reconstruct the SQL queries now that we know the owner's id
317
+ association.send(:construct_sql) if association.respond_to?(:construct_sql)
318
+ end
319
+ end
320
+
321
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled
322
+ # on the association.
323
+ #
324
+ # In addition, it will destroy the association if it was marked for
325
+ # destruction with mark_for_destruction.
326
+ #
327
+ # This all happens inside a transaction, _if_ the Transactions module is included into
328
+ # ActiveRecord::Base after the AutosaveAssociation module, which it does by default.
329
+ def save_has_one_association(reflection)
330
+ if (association = association_instance_get(reflection.name)) && !association.target.nil? && !association.destroyed?
331
+ autosave = reflection.options[:autosave]
332
+
333
+ if autosave && association.marked_for_destruction?
334
+ association.destroy
335
+ else
336
+ 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)
340
+ raise ActiveRecord::Rollback if !saved && autosave
341
+ saved
342
+ end
343
+ end
344
+ end
345
+ end
346
+
347
+ # Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
348
+ #
349
+ # In addition, it will destroy the association if it was marked for destruction.
350
+ def save_belongs_to_association(reflection)
351
+ if (association = association_instance_get(reflection.name)) && !association.destroyed?
352
+ autosave = reflection.options[:autosave]
353
+
354
+ if autosave && association.marked_for_destruction?
355
+ association.destroy
356
+ elsif autosave != false
357
+ saved = association.save(:validate => !autosave) if association.new_record? || autosave
358
+
359
+ if association.updated?
360
+ association_id = association.send(reflection.options[:primary_key] || :id)
361
+ self[reflection.primary_key_name] = association_id
362
+ end
363
+
364
+ saved if autosave
365
+ end
366
+ end
367
+ end
368
+ end
369
+ end