lib/para/attribute_field/nested_many.rb in para-0.6.9 vs lib/para/attribute_field/nested_many.rb in para-0.7.0

- old
+ new

@@ -1,29 +1,50 @@ module Para module AttributeField class NestedManyField < AttributeField::HasManyField + include Para::Helpers::AttributesMappings + include Para::AttributeField::NestedField + register :nested_many, self - def parse_input(params) + def parse_input(params, resource) if (nested_attributes = params[nested_attributes_key]) nested_attributes.each do |index, attributes| - nested_model_mappings.fields.each do |field| - field.parse_input(attributes) + mappings = nested_model_mappings(attributes) + nested_resource = fetch_or_build_nested_resource_for(resource, index, attributes) + + mappings.fields.each do |field| + field.parse_input(attributes, nested_resource) end params[nested_attributes_key][index] = attributes end else - super(params) + super end end - def nested_model_mappings - @nested_model_mappings ||= AttributeFieldMappings.new(reflection.klass) - end + private - def nested_attributes_key - @nested_attributes_key ||= :"#{ name }_attributes" + # Force loading association and look for a resource matching the provided + # attributes. If no resource is found, one is created and a fake `id` is + # assigned to it to hack Rails' nested resources lookup from new + # attributes params. + # + # This is necessary to be able to provide a resource to the #parse_input + # method when called on the nested resources hash + # + def fetch_or_build_nested_resource_for(parent, index, attributes) + nested_resources = parent.association(name).load_target + + if (id = attributes['id'].presence) + nested_resources.find { |res| res.id == id.to_i } + else + parent.association(name).build(attributes.slice('type')).tap do |resource| + attributes['id'] = "__#{ index }" + temporarily_extend_new_resource(resource, attributes) + end + end end end end end