Is there a way of specifying arbitrarily deep strong parameters for tree structures in Rails 4? For example, how would I specify something as follows:
{
"node": {
"name": "parent",
"nodes": [
{ "name": "child1", "nodes": []},
{ "name": "child2", "nodes": [
{"name": "grandchild", "nodes": []}
]}
]
}
}
To allow each node to have a name attribute, and a nodes attribute?
There may be a cleaner solution for solving this but this is my current work around. The general idea is to count how deep my nesting goes and then auto generate the correct nested hash based on that number. So to follow your example:
def count_levels(node_params)
if !node_params[:nodes].nil?
max = 0
node_params[:node_parameters].each do |child_params|
count = count_levels(child_params[1])
max = count if count > max
end
return max + 1
else
return 0
end
end
def node_params
node_attributes_base = [:id, :name]
nodes = []
(1..count_levels(params[:node])).each do |val|
nodes = node_attributes_base + [node_attributes: nodes]
end
params.require(:node).permit(:id, :name, node_attributes: nodes)
end
(The above example can be cleaned up more since it's based on my code where the top level did not have the same parameters. I left it as I had it since it worked on my system.)
You can solve by depending on the fact that number of allowed level can be more than the levels you actually need, so you can count the occurrence of the recursive key nodes
key in your hash and use this count as number of levels.
Note that this count will be more than the levels you actually need, but it's simpler than recursively count number of levels in the hash
So in your controller you can the following:
# your_controller.rb
# include RecursiveParametersBuilder
recursive_nodes_attr = build_recursive_params(
recursive_key: 'nodes',
parameters: params,
permitted_attributes: [:name]
)
params.require(:model_name).permit(:name, nodes: recursive_nodes_attr)
And have the actual strong parameters building code can be like the following
# helper module
module RecursiveParametersBuilder
# recursive_path = [:post]
# recursive_key = :comment_attributes
# recursive_node_permitted_params = [:id, :_destroy, :parameterized_type, :parameterized_id, :name, :value, :is_encrypted, :base_param_id, :parent_param_id]
#
def build_recursive_params(recursive_key:, parameters:, permitted_attributes:)
template = { recursive_key => permitted_attributes }
nested_permit_list = template.deep_dup
current_node = nested_permit_list[recursive_key]
nested_count = parameters.to_s.scan(/#{recursive_key}/).count
(1..nested_count).each do |i|
new_element = template.deep_dup
current_node << new_element
current_node = new_element[recursive_key]
end
nested_permit_list
end
end
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With