Skip to content

Commit

Permalink
Require explicit attribute list for nested attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
antulik committed Sep 23, 2024
1 parent 88a4ded commit 7aa1bee
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 4 deletions.
50 changes: 48 additions & 2 deletions lib/active_interaction/extras/nested_attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,25 @@ def accepts_nested_attributes_for(*attributes)
options = attributes.extract_options!
options.reverse_merge!(allow_destroy: false, update_only: false)

if options.key?(:permit)
if options[:permit].is_a?(Array)
options[:permit] |= [:id, :_destroy]
end
else
raise ArgumentError, "missing :permit option"
end

attributes.each do |attribute|
nested_attribute_options[attribute] = options

case filters[attribute]
when ActiveInteraction::ArrayFilter
define_association_setter_for_many attribute, options
else
raise "Nested attributes are not supported for single object"
define_association_setter_for_single(attribute, options)
define_method "#{attribute}_attributes=" do |*args, **kwargs|
send("#{attribute}=", *args, **kwargs)
end
end
end
end
Expand All @@ -50,9 +61,20 @@ def define_association_setter_for_many(association, options)
end
end

def define_association_setter_for_single(association, options)
return unless options&.dig(:allow_destroy)

define_method "#{association}=" do |attributes|
if ActiveRecord::Type::Boolean.new.cast(attributes.stringify_keys.dig("_destroy"))
super(nil)
else
super(attributes)
end
end
end

def process_nested_collection(attributes, options = nil)
attributes = attributes.values if attributes.is_a?(Hash)

if options&.dig(:allow_destroy)
attributes.reject! do |attribute|
ActiveRecord::Type::Boolean.new.cast(attribute.stringify_keys.dig("_destroy"))
Expand All @@ -75,4 +97,28 @@ def call_reject_if(attributes, callback)
end
end
end

def assign_collection_association(model, association_name, attributes_collection, destroy_missing: false, **options)

if attributes_collection.is_a?(ActiveRecord::Associations::CollectionProxy)
return attributes_collection
end

model.instance_exec do
begin
old_options = nested_attributes_options[association_name]
nested_attributes_options[association_name] = options

list = assign_nested_attributes_for_collection_association(association_name, attributes_collection)

if destroy_missing
association(association_name).scope.excluding(list).each(&:destroy!)
end

list
ensure
nested_attributes_options[association_name] = old_options
end
end
end
end
5 changes: 3 additions & 2 deletions lib/active_interaction/extras/strong_params.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ def permitted_params
end.flatten(1).compact

if respond_to?(:nested_attribute_options)
nested_attribute_options.each do |attribute, _|
permissions << {"#{attribute}_attributes": {}}
nested_attribute_options.each do |attribute, opts|
permitted_list = opts.fetch(:permit)
permissions << {"#{attribute}_attributes": permitted_list}
end
end

Expand Down

0 comments on commit 7aa1bee

Please sign in to comment.