Skip to content

Commit dbc4134

Browse files
committed
Refactored nested associations so they are more readable. Added comments
which make it clearer about what's going on.
1 parent a2d57fd commit dbc4134

File tree

1 file changed

+54
-26
lines changed

1 file changed

+54
-26
lines changed

activerecord/lib/active_record/nested_attributes.rb

Lines changed: 54 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -269,23 +269,36 @@ def accepts_nested_attributes_for(*attr_names)
269269
self.nested_attributes_options = nested_attributes_options
270270

271271
type = (reflection.collection? ? :collection : :one_to_one)
272-
273-
# def pirate_attributes=(attributes)
274-
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options)
275-
# end
276-
generated_feature_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
277-
if method_defined?(:#{association_name}_attributes=)
278-
remove_method(:#{association_name}_attributes=)
279-
end
280-
def #{association_name}_attributes=(attributes)
281-
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
282-
end
283-
eoruby
272+
generate_association_writer(association_name, type)
284273
else
285274
raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?"
286275
end
287276
end
288277
end
278+
279+
private
280+
281+
# Generates a writer method for this association. Serves as a point for
282+
# accessing the objects in the association. For example, this method
283+
# could generate the following:
284+
#
285+
# def pirate_attributes=(attributes)
286+
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes)
287+
# end
288+
#
289+
# This redirects the attempts to write objects in an association through
290+
# the helper methods defined below. Makes it seem like the nested
291+
# associations are just regular associations.
292+
def generate_association_writer(association_name, type)
293+
generated_feature_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
294+
if method_defined?(:#{association_name}_attributes=)
295+
remove_method(:#{association_name}_attributes=)
296+
end
297+
def #{association_name}_attributes=(attributes)
298+
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
299+
end
300+
eoruby
301+
end
289302
end
290303

291304
# Returns ActiveRecord::AutosaveAssociation::marked_for_destruction? It's
@@ -371,20 +384,7 @@ def assign_nested_attributes_for_collection_association(association_name, attrib
371384
raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
372385
end
373386

374-
if limit = options[:limit]
375-
limit = case limit
376-
when Symbol
377-
send(limit)
378-
when Proc
379-
limit.call
380-
else
381-
limit
382-
end
383-
384-
if limit && attributes_collection.size > limit
385-
raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead."
386-
end
387-
end
387+
check_record_limit!(options[:limit], attributes_collection)
388388

389389
if attributes_collection.is_a? Hash
390390
keys = attributes_collection.keys
@@ -433,6 +433,29 @@ def assign_nested_attributes_for_collection_association(association_name, attrib
433433
end
434434
end
435435

436+
# Takes in a limit and checks if the attributes_collection has too many
437+
# records. The method will take limits in the form of symbols, procs, and
438+
# number-like objects (anything that can be compared with an integer).
439+
#
440+
# Will raise an TooManyRecords error if the attributes_collection is
441+
# larger than the limit.
442+
def check_record_limit!(limit, attributes_collection)
443+
if limit
444+
limit = case limit
445+
when Symbol
446+
send(limit)
447+
when Proc
448+
limit.call
449+
else
450+
limit
451+
end
452+
453+
if limit && attributes_collection.size > limit
454+
raise TooManyRecords, "Maximum #{limit} records are allowed. Got #{attributes_collection.size} records instead."
455+
end
456+
end
457+
end
458+
436459
# Updates a record with the +attributes+ or marks it for destruction if
437460
# +allow_destroy+ is +true+ and has_destroy_flag? returns +true+.
438461
def assign_to_or_mark_for_destruction(record, attributes, allow_destroy)
@@ -452,6 +475,11 @@ def reject_new_record?(association_name, attributes)
452475
has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
453476
end
454477

478+
# Determines if a record with the particular +attributes+ should be
479+
# rejected by calling the reject_if Symbol or Proc (if defined).
480+
# The reject_if option is defined by +accepts_nested_attributes_for+.
481+
#
482+
# Returns false if there is a +destroy_flag+ on the attributes.
455483
def call_reject_if(association_name, attributes)
456484
return false if has_destroy_flag?(attributes)
457485
case callback = self.nested_attributes_options[association_name][:reject_if]

0 commit comments

Comments
 (0)