Skip to content

Commit badf915

Browse files
authored
Merge pull request rails#42501 from HParker/allow-specifying-numeric-strong_params
Allow permitting numeric params
2 parents 20db084 + 5c7a785 commit badf915

File tree

2 files changed

+113
-3
lines changed

2 files changed

+113
-3
lines changed

actionpack/lib/action_controller/metal/strong_parameters.rb

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,41 @@ def require(key)
577577
#
578578
# params.require(:person).permit(contact: [ :email, :phone ])
579579
# # => <ActionController::Parameters {"contact"=><ActionController::Parameters {"email"=>"[email protected]", "phone"=>"555-1234"} permitted: true>} permitted: true>
580+
#
581+
# If your parameters specify multiple parameters indexed by a number,
582+
# you can permit each set of parameters under the numeric key to be the same using the same syntax as permitting a single item.
583+
#
584+
# params = ActionController::Parameters.new({
585+
# person: {
586+
# '0': {
587+
# email: "[email protected]",
588+
# phone: "555-1234"
589+
# },
590+
# '1': {
591+
# email: "[email protected]",
592+
# phone: "555-6789"
593+
# },
594+
# }
595+
# })
596+
# params.permit(person: [:email]).to_h
597+
# # => {"person"=>{"0"=>{"email"=>"[email protected]"}, "1"=>{"email"=>"[email protected]"}}}
598+
#
599+
# If you want to specify what keys you want from each numeric key, you can instead specify each one individually
600+
#
601+
# params = ActionController::Parameters.new({
602+
# person: {
603+
# '0': {
604+
# email: "[email protected]",
605+
# phone: "555-1234"
606+
# },
607+
# '1': {
608+
# email: "[email protected]",
609+
# phone: "555-6789"
610+
# },
611+
# }
612+
# })
613+
# params.permit(person: { '0': [:email], '1': [:phone]}).to_h
614+
# # => {"person"=>{"0"=>{"email"=>"[email protected]"}, "1"=>{"phone"=>"555-6789"}}}
580615
def permit(*filters)
581616
params = self.class.new
582617

@@ -937,12 +972,18 @@ def convert_value_to_parameters(value)
937972
end
938973
end
939974

940-
def each_element(object, &block)
975+
def specify_numeric_keys?(filter)
976+
if filter.respond_to?(:keys)
977+
filter.keys.any? { |key| /\A-?\d+\z/.match?(key) }
978+
end
979+
end
980+
981+
def each_element(object, filter, &block)
941982
case object
942983
when Array
943984
object.grep(Parameters).filter_map(&block)
944985
when Parameters
945-
if object.nested_attributes?
986+
if object.nested_attributes? && !specify_numeric_keys?(filter)
946987
object.each_nested_attribute(&block)
947988
else
948989
yield object
@@ -1055,7 +1096,7 @@ def hash_filter(params, filter)
10551096
end
10561097
elsif non_scalar?(value)
10571098
# Declaration { user: :name } or { user: [:name, :age, { address: ... }] }.
1058-
params[key] = each_element(value) do |element|
1099+
params[key] = each_element(value, filter[key]) do |element|
10591100
element.permit(*Array.wrap(filter[key]))
10601101
end
10611102
end

actionpack/test/controller/parameters/nested_parameters_permit_test.rb

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,75 @@ def assert_filtered_out(params, key)
194194
assert_filtered_out permitted[:book][:authors_attributes]["-1"], :age_of_death
195195
end
196196

197+
test "nested params with numeric keys addressing individual numeric keys" do
198+
params = ActionController::Parameters.new(
199+
book: {
200+
authors_attributes: {
201+
'0': { name: "William Shakespeare", age_of_death: "52" },
202+
'1': { name: "Unattributed Assistant" },
203+
'2': { name: %w(injected names) }
204+
}
205+
})
206+
permitted = params.permit book: { authors_attributes: { '1': [ :name ], '0': [ :name, :age_of_death ] } }
207+
208+
assert_equal(
209+
{ "book" => { "authors_attributes" => { "0" => { "name" => "William Shakespeare", "age_of_death" => "52" }, "1" => { "name" => "Unattributed Assistant" } } } },
210+
permitted.to_h
211+
)
212+
end
213+
214+
test "nested params with numeric keys addressing individual numeric keys using require first" do
215+
params = ActionController::Parameters.new(
216+
book: {
217+
authors_attributes: {
218+
'0': { name: "William Shakespeare", age_of_death: "52" },
219+
'1': { name: "Unattributed Assistant" },
220+
'2': { name: %w(injected names) }
221+
}
222+
})
223+
224+
permitted = params.require(:book).permit(authors_attributes: { '1': [:name] })
225+
226+
assert_equal(
227+
{ "authors_attributes" => { "1" => { "name" => "Unattributed Assistant" } } },
228+
permitted.to_h
229+
)
230+
end
231+
232+
test "nested params with numeric keys addressing individual numeric keys to arrays" do
233+
params = ActionController::Parameters.new(
234+
book: {
235+
authors_attributes: {
236+
'0': ["draft 1", "draft 2", "draft 3"],
237+
'1': ["final draft"],
238+
'2': { name: %w(injected names) }
239+
}
240+
})
241+
permitted = params.permit book: { authors_attributes: { '2': [ :name ], '0': [] } }
242+
243+
assert_equal(
244+
{ "book" => { "authors_attributes" => { "2" => {}, "0" => ["draft 1", "draft 2", "draft 3"] } } },
245+
permitted.to_h
246+
)
247+
end
248+
249+
test "nested params with numeric keys addressing individual numeric keys to more nested params" do
250+
params = ActionController::Parameters.new(
251+
book: {
252+
authors_attributes: {
253+
'0': ["draft 1", "draft 2", "draft 3"],
254+
'1': ["final draft"],
255+
'2': { name: { "projects" => [ "hamlet", "Othello" ] } }
256+
}
257+
})
258+
permitted = params.permit book: { authors_attributes: { '2': { name: { projects: [] } }, '0': [] } }
259+
260+
assert_equal(
261+
{ "book" => { "authors_attributes" => { "2" => { "name" => { "projects" => ["hamlet", "Othello"] } }, "0" => ["draft 1", "draft 2", "draft 3"] } } },
262+
permitted.to_h
263+
)
264+
end
265+
197266
test "nested number as key" do
198267
params = ActionController::Parameters.new(
199268
product: {

0 commit comments

Comments
 (0)