Routingのdeprecated positional-hashでfalseが無視されていた問題を修正

rails/rails

Rails 8.1のキーワード移行期間中、ルーティングDSLのdeprecated positional-hash形式keyword形式は同等であることが期待されます。しかし、format: falseanchor: false といったブール値オプションが positional‑hash で無視され、ルートが変化し、しかもデプリケーション警告が出ないため、マイグレーション時に予期せぬ挙動が発生していました。

背景

このセクションでは、問題が発生した経緯とその影響を概観します。

問題の根本は、assign_deprecated_option が真偽判定で false を除外していたことです。 具体例として、get "/*path", to: "pages#show", format: false はフォーマットセグメントを除去しますが、同等と見なされる positional‑hash 形式 get "/*path", { to: "pages#show", format: false } では format: false が捨てられ、結果は /*path(.:format) となります。これにより、キーワード形式へ移行した際にルート動作が変わり、デプリケーション警告も発生しないため、ユーザーは違いに気付けません。

この不整合は、マイグレーションガイドが前提とする「同等動作」を壊すため、実運用上のリグレッションリスクを増大させます。 影響は、フォーマットやアンカーを無効化したいルート全般に及び、テストスイートでも失敗が報告されました。

技術的な変更

このセクションでは、実装上の修正内容とテスト追加を詳述します。

assign_deprecated_optionfalse を正しく扱うようロジックを変更しました。 変更前は if (deprecated_value = deprecated_options.delete(key)) という真偽判定で false が除外されていましたが、修正後は if deprecated_options.key?(key) でキーの有無を確認し、続いて deprecated_options.delete(key) で値を返すようになりました。これにより false も保持され、デプリケーション警告が出ます。

@@
-            if (deprecated_value = deprecated_options.delete(key))
+            if deprecated_options.key?(key)
               ActionDispatch.deprecator.warn(<<~MSG.squish)
                 #{method_name} received a hash argument #{key}. Please use a keyword instead. Support to hash argument will be removed in Rails 8.2.
               MSG
-              deprecated_value
+              deprecated_options.delete(key)
             end

テストが追加され、format: false が positional‑hash でも正しく処理されることを検証しました。 新しいテスト test_deprecated_hash_honors_format_false は、デプリケーション警告が出ることと、生成されたルート文字列が /*path になることをアサートします。

@@
-      def test_deprecated_hash_honors_format_false
-        fakeset = FakeSet.new
-        mapper = Mapper.new fakeset
-
-        assert_deprecated(ActionDispatch.deprecator) do
-          mapper.get "/*path", { to: "pages#show", format: false }
-        end
-
-        assert_equal "/*path", fakeset.asts.first.to_s
-      end
+      def test_deprecated_hash_honors_format_false
+        fakeset = FakeSet.new
+        mapper = Mapper.new fakeset
+
+        assert_deprecated(ActionDispatch.deprecator) do
+          mapper.get "/*path", { to: "pages#show", format: false }
+        end
+
+        assert_equal "/*path", fakeset.asts.first.to_s
+      end

修正は assign_deprecated_option のロジックとテスト追加だけに留まり、既存の呼び出し側コードは変更不要です。 これにより、既存アプリケーションへの影響は最小限に抑えられます。

設計判断

このセクションでは、採られた設計方針とその妥当性を考察します。

最小侵入的な修正で、既存APIと互換性を保ちつつバグを解消しました。 assign_deprecated_option の内部だけを変更し、外部インターフェースや呼び出し側ロジックはそのままです。これにより、format: falseanchor: false が正しく扱われ、同時にデプリケーション警告も発生するため、将来的な削除に備えた移行が容易になります。

デプリケーション警告のロジックは保持し、キーが存在すれば必ず警告を出す方針です。 以前は false が除外されて警告が出なかった点を修正し、ユーザーが意図的に false を渡したことを明示的に通知します。これにより、ドキュメント通りの挙動が保証され、移行ガイドの前提が崩れません。

まとめ

assign_deprecated_option の真偽判定を key? へ置き換え、false も正しく処理できるようにしたことで、deprecated positional‑hash と keyword 形式のルーティングが等価になりました。影響範囲はメソッド内部に留まり、既存コードへの破壊的変更はありません。これにより、Rails 8.1 のキーワード移行が安全に進められ、将来的な hash 形式の廃止に向けた一貫したデプリケーション体験が提供されます。

記事メタデータ

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

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文がタイトル直下にあり、背景、技術的変更、設計判断、まとめの各セクションが順序立てて配置されている。まとめは単なる繰り返しではなく、変更の意義と影響を述べている。

カスタムMarkdown構文 ✓ PASS

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

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

対象読者への適合性 ✓ PASS

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

対象はRailsエンジニアであり、専門用語中心の記述で初心者向けの過度な説明はない。

パラグラフ・ライティング ✓ PASS

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

各セクションは総論→各論→結論の構成になり、段落はトピックセンテンスで始まり1段落1トピック、5文以下に収め、空行で区切られている。

Diff内容との照合 ✓ PASS

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

記事中のコードブロックは提供されたDiffと完全に一致しており、変更箇所・追加テストが正確に反映されている。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`assign_deprecated_option`、`positional-hash`、`keyword form`、`false` などの用語はPRと一致し、誤用はない。

説明の技術的正確性 ✓ PASS

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

技術的説明はPRの原因・修正内容と合致しており、因果関係も正しく記述されている。

事実の突合 ✓ PASS

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

全ての主張はPRのタイトル・説明・Diffで裏付けられており、外部知識や根拠のない数値は含まれていない。

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

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

PR番号 #57748、Railsバージョン 8.1・8.2 などの数値は正確である。

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

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

記事タイトルはPRの内容(positional‑hash の falsy 値を正しく扱う修正)を正確に日本語で要約している。

外部知識の正確性 ✓ PASS

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

LTS やリリース日程等の PR に無関係な外部情報は一切含まれていない。

時間表現の正確性 ✓ PASS

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

時間表現は PR の記述と一致し、歪曲はない。