`ActionController::Parameters#merge` が複数引数に対応し `Hash#merge` と完全に一致
ActionController::Parameters#merge および #merge! が複数のハッシュを同時に受け取れるようになりました。これにより、Ruby の Hash#merge との挙動の一貫性が完全に整います。
背景
ActionController::Parameters の #merge / #merge! は、Ruby の Hash#merge と一貫したAPIを提供することを目指してきましたが、段階的な改善の途上にありました。#56410 では #merge へのブロックサポートが追加され、#merge! との非対称性が解消されました。しかし、Ruby 3.2 以降の Hash#merge が複数引数を受け付けるのに対し、Parameters#merge / #merge! はいずれも引数を1つしか受け取れないという制約が残っていました。
本 PR #56575 はその最後のギャップを埋め、Parameters の merge 系メソッドを Hash#merge と完全に揃えます。
技術的な変更
actionpack/lib/action_controller/metal/strong_parameters.rb の #merge と #merge! の引数シグネチャが other_hash から *other_hashes に変更され、可変長引数を受け付けるようになりました。
変更前:
def merge(other_hash, &block)
new_instance_with_inherited_permitted_status(
@parameters.merge(other_hash.to_h, &block)
)
end
def merge!(other_hash, &block)
@parameters.merge!(other_hash.to_h, &block)
self
end
変更後:
def merge(*other_hashes, &block)
new_instance_with_inherited_permitted_status(
@parameters.merge(*other_hashes.map!(&:to_h), &block)
)
end
def merge!(*other_hashes, &block)
@parameters.merge!(*other_hashes.map!(&:to_h), &block)
self
end
変更の核心は other_hashes.map!(&:to_h) です。可変長引数で受け取った各ハッシュに対して to_h を呼び出すことで、ActionController::Parameters インスタンスも通常の Hash も一様に内部表現へ変換し、そのままスプラット展開して @parameters.merge! / @parameters.merge に委譲しています。
以下のように複数のパラメータや通常のHashを一度にマージできます:
params1 = ActionController::Parameters.new(a: 1)
params2 = ActionController::Parameters.new(b: 2)
params1.merge(params2, { c: 3 })
# => #<ActionController::Parameters {"a"=>1, "b"=>2, "c"=>3} permitted: false>
引数を1つだけ渡す従来の使い方はそのまま動作するため、既存のコードへの影響はありません。
設計判断
map!(&:to_h) を使った最小限の実装 が選ばれました。
各引数に対して個別に to_h を呼び出す処理を map! で一括化し、スプラット展開で内部の Hash#merge / Hash#merge! へそのまま渡す構造は、追加のループやデータ構造を必要とせず簡潔です。ブロック引数の扱いも含め、変更箇所は引数の受け取り方と to_h の適用方法のみであり、既存の new_instance_with_inherited_permitted_status による許可状態の継承ロジックには一切手を加えていません。
この設計は、Parameters が Hash のラッパーとして振る舞うという既存のアーキテクチャを踏襲しており、複数引数への対応を Hash 側に委譲することで実装の複雑さを最小化しています。
まとめ
本 PR (#56575) により、ActionController::Parameters#merge / #merge! は Ruby の Hash#merge と引数の互換性が完全に一致しました。#56410 から続く段階的な整合化の取り組みが完結し、Parameters を Hash と同様に扱うコードがより自然に書けるようになります。