undefined method `map' for #<ActionController::Parameters Rails 6.0

1.1k views Asked by At

Getting this error when trying to call the method validate_create_action. The error occurs on the line @element = klass.new(params[:element]).

NoMethodError in Analyses::MitigationMatricesController#validate_create_action

undefined method `map' for #<ActionController::Parameters {"0"=>{"phase"=>"Launch", "reason"=>"test"}} permitted: false>
Did you mean?  tap
Extracted source (around line #7):

#5       class ManyEmbeddedProxy < EmbeddedCollection
#6         def replace(values)
*7           @_values = (values || []).compact.map do |v|
#8             v.respond_to?(:attributes) ? v.attributes : v
#9           end
#10           reset

In the post request the element object below is sent to the backend. The issue seems to be the array critical_phases.

element[applicable_during][critical_phases][0][phase]   "Launch"
element[applicable_during][critical_phases][0][reason]  "test"

If I add the method to_unsafe_h to params[:element].to_unsafe_h I get a different error involving the critical_phases array saying "TypeError: no implicit conversion of String into Integer". Any ideas on how to solve this?

element_controller.rb

 def validate_create_action
    rejigger_dynamic_lists
    remove_associations_from_params
    @element                     = klass.new(params[:element])

 ...

 def klass
unless @klass
  klass_str     = params[:controller].split("/").last.singularize.camelize
  namespace_str = SITE_CONFIG['adaptation_name'].camelize
  @klass        = "#{namespace_str}::#{klass_str}".constantize
end
@klass
end

UPDATE:

I updated my code to the working code params[:element].permit!

    params2 = ActionController::Parameters.new(params[:element][:applicable_during][:critical_phases].to_h)
    params2.permit!
    params[:element][:applicable_during][:critical_phases] = params2.values.map do |h|
      ActionController::Parameters.new(h.permit!.to_h).permit(:phase, :reason) # use permit! instead if you want to live dangerously
      test_mapping = {}
    end
1

There are 1 answers

0
max On

The problem here is that it the parameters aren't actually an array. Its a hash with the keys "0", "1" etc. This is done to avoid the potential ambigiuty thats happens when Rack parses form data keys containing arrays of hashes:

irb(main):015:0> Rack::Utils.parse_nested_query("foo[][bar]=1&foo[][bar]=2")                                                                                                                                  
=> {"foo"=>[{"bar"=>"1"}, {"bar"=>"2"}]}   
# should this be one or two hashes? Discuss
irb(main):016:0> Rack::Utils.parse_nested_query("foo[][bar]=1&foo[][baz]=2")
=> {"foo"=>[{"bar"=>"1", "baz"=>"2"}]}     
irb(main):017:0> Rack::Utils.parse_nested_query("foo[0][bar]=1&foo[1][baz]=2")
=> {"foo"=>{"0"=>{"bar"=>"1"}, "1"=>{"baz"=>"2"}}}

Typically normalizing this is handled by accepts_nested_attributes_for. But what you're doing doesn't look the least bit ordinary.

If you want to jury rig a system that processes this and returns an array of whitelisted hashes you could do:

params = ActionController::Parameters.new("0"=>{"phase"=>"Launch", "reason"=>"test"}, "1"=>{"phase"=>"Foo", "reason"=>"bar"})

params.values.map do |h|
  ActionController::Parameters.new(h)
    .permit(:phase, :reason) # use permit! instead if you want to live dangerously
end

# [<ActionController::Parameters {"phase"=>"Launch", "reason"=>"test"} permitted: true>, <ActionController::Parameters {"phase"=>"Foo", "reason"=>"bar"} permitted: true>]