Module: ActiveRecord::NestedAttributes

Defined in:
lib/composite_primary_keys/nested_attributes.rb

Instance Method Summary collapse

Instance Method Details

#assign_nested_attributes_for_collection_association(association_name, attributes_collection) ⇒ Object



3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/composite_primary_keys/nested_attributes.rb', line 3

def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
  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

  check_record_limit!(options[:limit], attributes_collection)

  if attributes_collection.is_a? Hash
    keys = attributes_collection.keys
    attributes_collection = if keys.include?('id') || keys.include?(:id)
                              [attributes_collection]
                            else
                              attributes_collection.values
                            end
  end

  association = association(association_name)

  existing_records = if association.loaded?
                       association.target
                     # CPK
                     elsif association.reflection.klass.composite?
                       attributes_collection.map do |attribute_collection|
                         attribute_ids = attribute_collection['id'] || attribute_collection[:id]
                         if attribute_ids
                           ids = CompositePrimaryKeys::CompositeKeys.parse(attribute_ids)
                           eq_predicates = association.klass.primary_key.zip(ids).map do |primary_key, value|
                             association.klass.arel_table[primary_key].eq(value)
                           end
                           association.scope.where(*eq_predicates).to_a
                         else
                           []
                         end
                       end.flatten.compact
                     else
                       attribute_ids = attributes_collection.map {|a| a['id'] || a[:id] }.compact
                       attribute_ids.empty? ? [] : association.scope.where(association.klass.primary_key => attribute_ids)
                     end

  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))
      end
    elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
      unless 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)
        # Take into account that the proxy_target may have changed due to callbacks
        target_record = association.target.detect { |record| record.id.to_s == attributes['id'].to_s }
        if target_record
          existing_record = target_record
        else
          association.add_to_target(existing_record, :skip_callbacks)
        end

        assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
      end
    else
      raise_nested_attributes_record_not_found!(association_name, attributes['id'])
    end
  end
end