Skip to content

Instantly share code, notes, and snippets.

@koenpunt
Last active August 26, 2021 06:46
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save koenpunt/ac279e05cfeb0954ca763344fc0240b4 to your computer and use it in GitHub Desktop.
Save koenpunt/ac279e05cfeb0954ca763344fc0240b4 to your computer and use it in GitHub Desktop.
ActionController::Parameters -> permit_recursive_params
{"tree"=>
{"name"=>"Australia",
"value"=>39904,
"id"=>6,
"description"=>"",
"string_array"=>["one", "two"],
"array_with_hashes"=>[{"a"=>"1"}, {"a"=>"2"}],
"type"=>{"name"=>"Other", "icon"=>"<i class=\"fa fa-pagelines\"></i>"},
"children"=>
[{"name"=>"Boxes",
"value"=>451609,
"id"=>7,
"description"=>"Used to store things.",
"type"=>{"name"=>"Storage", "icon"=>"<i class=\"fa fa-archive\"></i>"},
"children"=>
[{"name"=>"Boxes",
"value"=>451609,
"id"=>7,
"description"=>"Used to store things.",
"type"=>
{"name"=>"Storage", "icon"=>"<i class=\"fa fa-archive\"></i>"},
"children"=>
[{"name"=>"Boxes",
"value"=>451609,
"id"=>7,
"description"=>"Used to store things.",
"type"=>
{"name"=>"Storage",
"icon"=>"<i class=\"fa fa-archive\"></i>"}}]}]},
{"name"=>"Boxes",
"value"=>451609,
"id"=>7,
"description"=>"Used to store things.",
"type"=>{"name"=>"Storage", "icon"=>"<i class=\"fa fa-archive\"></i>"}},
{"name"=>"Boxes",
"value"=>451609,
"id"=>7,
"description"=>"Used to store things.",
"type"=>
{"name"=>"Storage", "icon"=>"<i class=\"fa fa-archive\"></i>"}}]}}
require 'pp'
require 'active_support/core_ext/object/try'
require 'action_controller'
def permit_recursive_params(params)
(params.try(:to_unsafe_h) || params).map do |key, value|
if value.is_a?(Array)
if value.first.respond_to?(:map)
{ key => [ permit_recursive_params(value.first) ] }
else
{ key => [] }
end
elsif value.is_a?(Hash)
{ key => permit_recursive_params(value) }
else
key
end
end
end
params = ActionController::Parameters.new(
budget: {
tree: {
name: "Australia",
value: 39904,
id: 6,
description: "",
string_array: ["one", "two"],
array_with_hashes: [{
a: "1"
}, {
a: "2"
}],
type: {
name: "Other",
icon: "<i class=\"fa fa-pagelines\"></i>"
},
children: [
{
name: "Boxes",
value: 451609,
id: 7,
description: "Used to store things.",
type: {
name: "Storage",
icon: "<i class=\"fa fa-archive\"></i>"
},
children: [
{
name: "Boxes",
value: 451609,
id: 7,
description: "Used to store things.",
type: {
name: "Storage",
icon: "<i class=\"fa fa-archive\"></i>"
},
children: [
{
name: "Boxes",
value: 451609,
id: 7,
description: "Used to store things.",
type: {
name: "Storage",
icon: "<i class=\"fa fa-archive\"></i>"
}
}
]
}
]
},
{
name: "Boxes",
value: 451609,
id: 7,
description: "Used to store things.",
type: {
name: "Storage",
icon: "<i class=\"fa fa-archive\"></i>"
}
},
{
name: "Boxes",
value: 451609,
id: 7,
description: "Used to store things.",
type: {
name: "Storage",
icon: "<i class=\"fa fa-archive\"></i>"
}
}
]
}
}
)
pp params.require(:budget).permit(tree: permit_recursive_params(params[:budget][:tree])).to_h
@serg-kovalev
Copy link

serg-kovalev commented Jan 18, 2017

What do you think about adding rescue in method to handle nil values correctly.
https://gist.github.com/koenpunt/ac279e05cfeb0954ca763344fc0240b4#file-permit_recursive_params-rb-L18

def permit_recursive_params(params)
   # ...
rescue StandardError => e
  Rails.logger.warn e
  nil
end

@JeremiahChurch
Copy link

JeremiahChurch commented Feb 3, 2017

@serg-kovalev - I went with a guard clause at the beginning instead - return if params.nil?. it's a bit safer than swallowing standarderror

@paniko0
Copy link

paniko0 commented Oct 30, 2017

I don't think this solution is working on every scenario:

require 'pp'
require 'active_support/core_ext/object/try'
require 'action_controller'

def permit_recursive_params(params)
  (params.try(:to_unsafe_h) || params).map do |key, value|
    if value.is_a?(Array)
      if value.first.respond_to?(:map)
        { key => [ permit_recursive_params(value.first) ] }
      else
        { key => [] }
      end
    elsif value.is_a?(Hash)
      { key => permit_recursive_params(value) }
    else
      key
    end
  end
end

params = ActionController::Parameters.new(
document: { id: 1212, token_map: {"1": [{"page": 1, "type": "signature", "coords": {"x": 75, "y": 285}, "fontSize": 16}, {"page": 1, "type": "text", "field": "fullname", "coords": {"x": 335, "y": 316}, "fontSize": 16}, {"page": 1, "type": "text", "field": "date", "coords": {"x": 75, "y": 380}, "fontSize": 16}]} }
)


pp params.require(:document).permit(token_map: permit_recursive_params(params[:document][:token_map])).to_h

For some reason, it does not whitelist the field node. I believe it's because you are whitelisting only the first node. In any case, I'm trying to fix it and I'll post it here when/if I finish it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment