Created
March 28, 2012 04:13
-
-
Save Zequez/2223565 to your computer and use it in GitHub Desktop.
Allow existing associations in accepts_nested_attributes_for
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module ActiveRecord::NestedAttributes | |
module ClassMethods | |
def accepts_nested_attributes_for(*attr_names) | |
options = { :allow_destroy => false, :update_only => false } | |
options.update(attr_names.extract_options!) | |
# Modified stuff | |
#options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only) | |
options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only, :allow_existing) | |
# /Modified stuff | |
options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank | |
attr_names.each do |association_name| | |
if reflection = reflect_on_association(association_name) | |
reflection.options[:autosave] = true | |
add_autosave_association_callbacks(reflection) | |
nested_attributes_options = self.nested_attributes_options.dup | |
nested_attributes_options[association_name.to_sym] = options | |
self.nested_attributes_options = nested_attributes_options | |
type = (reflection.collection? ? :collection : :one_to_one) | |
# def pirate_attributes=(attributes) | |
# assign_nested_attributes_for_one_to_one_association(:pirate, attributes, mass_assignment_options) | |
# end | |
class_eval " if method_defined?(:#{association_name}_attributes=) | |
remove_method(:#{association_name}_attributes=) | |
end | |
def #{association_name}_attributes=(attributes) | |
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes, mass_assignment_options) | |
end | |
", __FILE__, __LINE__ + 1 | |
else | |
raise ArgumentError, "No association found for name `#{association_name}'. Has it been defined yet?" | |
end | |
end | |
end | |
end | |
def assign_nested_attributes_for_collection_association(association_name, attributes_collection, assignment_opts = {}) | |
options = self.nested_attributes_options[association_name] | |
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array) | |
raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})" | |
end | |
if options[:limit] && attributes_collection.size > options[:limit] | |
raise TooManyRecords, "Maximum #{options[:limit]} records are allowed. Got #{attributes_collection.size} records instead." | |
end | |
if attributes_collection.is_a? Hash | |
keys = attributes_collection.keys | |
attributes_collection = if keys.include?('id') || keys.include?(:id) | |
Array.wrap(attributes_collection) | |
else | |
attributes_collection.values | |
end | |
end | |
association = association(association_name) | |
# Modified stuff | |
#existing_records = if association.loaded? | |
# association.target | |
#else | |
# attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact | |
# attribute_ids.empty? ? [] : association.scoped.where(association.klass.primary_key => attribute_ids) | |
#end | |
existing_records = if options[:allow_existing] or not association.loaded? | |
attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact | |
scope = options[:allow_existing] ? association.target_scope : association.scoped | |
scope.where(association.klass.primary_key => attribute_ids) | |
else | |
association.target | |
end | |
# / Modified stuff | |
attributes_collection.each do |attributes| | |
attributes = attributes.with_indifferent_access | |
if attributes['id'].blank? | |
unless reject_new_record?(association_name, attributes) | |
association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts) | |
end | |
elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s } | |
unless association.loaded? || call_reject_if(association_name, attributes) | |
# Make sure we are operating on the actual object which is in the association's | |
# proxy_target array (either by finding it, or adding it if not found) | |
target_record = association.target.detect { |record| record == existing_record } | |
if target_record | |
existing_record = target_record | |
else | |
association.add_to_target(existing_record) | |
end | |
end | |
if !call_reject_if(association_name, attributes) | |
assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy], assignment_opts) | |
end | |
elsif assignment_opts[:without_protection] | |
association.build(attributes.except(*unassignable_keys(assignment_opts)), assignment_opts) | |
else | |
raise_nested_attributes_record_not_found(association_name, attributes['id']) | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment