空パスでもトレーリングスラッシュ付きURLがクエリパラメータを受け取れるように

rails/rails

Trailing slash が有効な状態でパスが空文字列の場合、url_for がクエリパラメータやアンカーを付与しようとすると FrozenError が発生していました。この問題は path_for が凍結された文字列 "/" に対して変更を試みたことが原因です。修正により空パスでも安全にパラメータを付加できるようになりました。

背景

url_fortrailing_slash: truepath: "" を指定し、さらに paramsanchor を渡すと FrozenError が即座に発生していました。これは path_for"/" を文字列リテラルとして代入し、内部で凍結されたまま add_paramsadd_anchor が呼び出されたためです。結果として空パスの URL でクエリやフラグメントを利用できないという制約が残っていました。

技術的な変更

path_for の該当行を path = "/" から path = +"/" に変更しました。+ 演算子を付与すると新しい mutable 文字列オブジェクトが生成され、以降の add_paramsadd_anchor が安全に文字列を変更できます。変更前後のコードは以下の通りです。

変更前:

path = "/" if options[:trailing_slash] && path.blank?

変更後:

path = +"/" if options[:trailing_slash] && path.blank?

さらにテストが追加され、trailing_slash: true と空パスの組み合わせでクエリパラメータやアンカーが正しく付与されることを検証しています。テストはフル URL とパスオンリーの両ケースで期待結果を assert しています。これにより空パスでも url_for が正常にクエリやフラグメントを組み込め、既存の挙動に影響を与えるケースはありません。

test "url_for keeps query params and anchor with a trailing slash and a blank path" do
  assert_equal "http://www.example.com/?search=books", url_for(trailing_slash: true, params: { search: "books" })
  assert_equal "http://www.example.com/#signup", url_for(trailing_slash: true, anchor: "signup")
  assert_equal "/?a=1", url_for(only_path: true, trailing_slash: true, path: "", params: { a: 1 })
end

この修正により、空パスでも url_for が正常にクエリやフラグメントを組み込めるようになり、既存の挙動に影響を与えるケースはありません。

設計判断

今回の修正は 文字列リテラルに unary plus を付与して新しいインスタンスを生成 するという最小限の変更で実現されています。ロジック全体を大幅に書き換えることなく、凍結状態だけを回避できる点が選択された理由です。+"/" は可読性を損なわず、既存の path 生成ロジックと互換性を保ちつつバグを解消します。

まとめ

trailing_slash: true と空パスの組み合わせで発生していた FrozenError を、path = +"/" に置き換えることで解消し、クエリパラメータやアンカーの付与が正しく動作するようになりました。追加されたテストにより振る舞いが保証され、Rails の URL 生成機能の一貫性が向上しています。この改善により、開発者は空パスでも安全に URL を構築でき、既存コードへの影響を心配せずに利用できます。

記事メタデータ

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

この記事は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リンクも # を含むリンクテキストで記述されています。

対象読者への適合性 ✓ PASS

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

技術者向けの内容で、余計な基礎解説はなく、適切な専門レベルです。

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

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

各セクションは総論パラグラフ、技術詳細、結論パラグラフで構成され、各段落はトピックセンテンスで始まり、長さも適切です。空行で区切られています。

Diff内容との照合 ✓ PASS

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

記事中のコードブロックは Diff の変更内容と完全に一致しており、ファイル名・変更箇所も正しく反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

FrozenError、trailing_slash、add_params などの用語は PR と一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

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

技術的な説明は Diff と PR の記述に基づき、因果関係も正しく示されています。

事実の突合 ✓ PASS

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

すべての主張は PR のタイトル、説明、Diff に裏付けられており、根拠のない推測はありません。

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

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

PR 番号 #57938 やテストコード内の期待値は正確です。

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

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

記事タイトルは PR タイトルの日本語要約であり、内容と一致しています。

外部知識の正確性 ✓ PASS

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

外部のバージョンサポート情報等は記載されておらず、PR 以外の知識は含まれていません。

時間表現の正確性 ✓ PASS

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

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