空パスでもトレーリングスラッシュ付きURLがクエリパラメータを受け取れるように
Trailing slash が有効な状態でパスが空文字列の場合、url_for がクエリパラメータやアンカーを付与しようとすると FrozenError が発生していました。この問題は path_for が凍結された文字列 "/" に対して変更を試みたことが原因です。修正により空パスでも安全にパラメータを付加できるようになりました。
背景
url_for に trailing_slash: true と path: "" を指定し、さらに params や anchor を渡すと FrozenError が即座に発生していました。これは path_for が "/" を文字列リテラルとして代入し、内部で凍結されたまま add_params や add_anchor が呼び出されたためです。結果として空パスの URL でクエリやフラグメントを利用できないという制約が残っていました。
技術的な変更
path_for の該当行を path = "/" から path = +"/" に変更しました。+ 演算子を付与すると新しい mutable 文字列オブジェクトが生成され、以降の add_params や add_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 を構築でき、既存コードへの影響を心配せずに利用できます。