Pattern MatchingでParametersの残余キーが取得できるように deconstruct_keys を改良

rails/rails

Pattern matching で ActionController::Parameters{ key:, **rest } の形で分解できなかった問題を、deconstruct_keys がキー指定なしの場合に全パラメータを返すよう修正し、Hash#deconstruct_keys と同等の振る舞いを実現しました。

背景

パターンマッチングが rest バインディングで失敗していた ことが、本変更の出発点です。ActionController::Parameters に対して { name:, **rest } のようなパターンを使用すると、deconstruct_keys が空ハッシュを返すためマッチしませんでした。実例として、params = ActionController::Parameters.new(name: "Bob", age: 22).permit!case 文で分解しようとすると、name が取得できず rest も空になるという挙動が報告されていました。これにより、Rails のパラメータオブジェクトと Ruby の Hash の挙動に不整合が生じ、開発者が期待通りにパターンマッチングを利用できない状況が生まれていました。

技術的な変更

deconstruct_keys の実装がキー指定なしで全体を返すよう変更されました。従来は slice(*keys).each... のみで、keysnil または空の場合は空ハッシュが生成されていました。新しい実装は次の通りです。

def deconstruct_keys(keys)
  params = keys ? slice(*keys) : self
  params.each.with_object({}) { |(key, value), hash| hash.merge!(key.to_sym => value) }
end

テストが追加され、期待動作が検証されましたequality_test.rb に新たに deconstruct_keys matches a pattern with a rest binding テストが導入され、{ person: { age:, **rest } } のようなパターンで rest が正しく残りのキーを保持することが確認されています。

test "deconstruct_keys matches a pattern with a rest binding" do
  @params => { person: { age:, **rest } }

  assert_equal "32", age
  assert_equal %i[name addresses].sort, rest.keys.map(&:to_sym).sort
end

設計判断

Hash#deconstruct_keys との挙動を合わせる方針が採用されましたActionController::Parameters は内部的に HashWithIndifferentAccess を継承しているため、Hash の deconstruction と同様の振る舞いを提供すれば、開発者は追加のラッピングや変換なしにパターンマッチングを利用できます。キーが明示されている場合は従来通り slice で限定し、指定が無い場合は自インスタンス全体を走査するシンプルな分岐を追加しただけで、既存コードへの破壊的変更は回避されています。

まとめ

この変更により、ActionController::Parameters でも `rest` バインディングが正しく機能**し、パラメータの部分抽出が Ruby の Hash と同等に扱えるようになりました。実装は最小限の条件分岐に留められ、既存の挙動を壊さずに API の一貫性が向上したことが最大の意義です。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
8a205491

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

リード文、背景、技術的変更、設計判断、まとめの5セクションが揃っており、総論→各論→結論の流れが明確です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

コードブロックは `ruby:ファイルパス` 形式で正しく記述され、PRリンクも `[PR #57942](URL)` で適切にリンク化されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

Rails エンジニア向けの専門的な内容で、初心者向けの過度な説明はありません。

パラグラフ・ライティング ⚠ WARNING

トピックセンテンス・1段落1トピック・段落長

各セクションは総論パラグラフと各論パラグラフがありトピックセンテンスも適切ですが、セクションごとの結論パラグラフが欠如しています。段落の長さや空行は問題ありません。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事中のコードブロックは Diff の変更内容と完全に一致しており、ファイル名・行内容とも合致しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`deconstruct_keys`、`rest binding`、`Hash#deconstruct_keys` などの用語は PR と一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

変更理由と実装内容が PR の記述と合致しており、因果関係も論理的です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事の主張はすべて PR の内容と Diff に裏付けられており、根拠のない推測はありません。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR 番号 #57942、テストで使用されている数値 "32" など、全て正確です。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事タイトルは PR の趣旨(キー未指定時に全パラメータを返す)を適切に日本語化して表現しています。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

Rails の内部実装に関する記述は事実に基づくもので、PR に記載のないバージョン情報等の外部知識は含まれていません。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

時間表現は使用されておらず、PR と食い違う表現もありません。