GlobalID の URI パーサーを RFC3986 に切り替えてアンダースコアを許容

rails/globalid

GlobalID が内部で使用していたハードコードの RFC2396_Parser を Ruby 標準の RFC3986_Parser に置き換えることで、ホスト名にアンダースコアを含む URI が有効になります。これにより、過去に配布された QR コード等に埋め込まれた ID が破壊的に失敗する問題が解消されます。

背景

GlobalID 1.3.0 からは URI::RFC2396_Parser が固定化され、gid://my_app/User/1 のようなアンダースコアを含むホストは URI::InvalidComponentError を投げていました。#201 で報告された回帰は、Ruby 本体が 2024 年 11 月にデフォルトパーサーを RFC3986_Parser に変更したことと、GlobalID が追随しなかったことが原因です。

この不一致は、QR コードに埋め込まれた ID が物理的に置き換えられない環境で致命的な障害となります。そこで、GlobalID 側でも標準パーサーに合わせる修正が求められました。

技術的な変更

URI パーサー定数の差し替え\nlib/global_id/uri/gid.rbURI_PARSER 定数を RFC2396_Parser から RFC3986_Parser に変更しました。

-    URI_PARSER = URI::RFC2396_Parser.new # :nodoc:
+    URI_PARSER = URI::RFC3986_Parser.new # :nodoc:

この変更により、以降の解析は RFC3986 に準拠したロジックが適用されます。

バリデーションコメントとエラーメッセージの更新\nvalidate_app のコメントは、許容文字を「ハイフンとダッシュ」に変更し、アンダースコア例を追記しました。また、エラーメッセージはアンダースコアも許容対象である旨に更新しました。

-      # Validates +app+'s as URI hostnames containing only alphanumeric characters
-      # and hyphens. An ArgumentError is raised if +app+ is invalid.
+      # Validates +app+'s as URI hostnames containing only alphanumeric characters,
+      # hyphens and dashes. An ArgumentError is raised if +app+ is invalid.
+      #   URI::GID.validate_app('foo_bar') # => 'foo_bar'
@@
-          'App names must be valid URI hostnames: alphanumeric and hyphen characters only.'
+          'App names must be valid URI hostnames: alphanumeric, hyphen and underscore characters only.'

parse メソッドのコメント修正\nparse が使用する URI_PARSER が RFC3986 であることをコメントに明示しました。

-        generic_components = URI.split(uri) << URI_PARSER << true # RFC2396 parser, true arg_check
+        generic_components = URI.split(uri) << URI_PARSER << true # RFC3986 parser, true arg_check

テストの調整\ntest/cases/uri_gid_test.rb ではアンダースコアを含むホスト名の期待結果を追加し、CompositePrimaryKeyModel の ID 表記もアンダースコア対応に修正しました。また、test/cases/global_id_test.rb では GlobalID.app = 'blog_app' が許容されることを確認し、GlobalID.app = 'blog:app' が引き続きエラーになることを検証しています。test/cases/global_locator_test.rb からは「ロケータ名にアンダースコアは不可」というテストが削除され、ロケータ名のアンダースコア許容が暗黙的に保証されました。

設計判断

本変更は 最小限の差分で標準パーサーに合わせる という方針に従い、定数置換とコメント・メッセージの微修正だけで実装されています。新たな設定項目を導入せず、Ruby 本体が提供する URI.parser の切り替え機構に委譲することで、将来的なパーサー変更にもコード修正を回避できる拡張性が確保されています。

まとめ

GlobalID が使用する URI パーサーを RFC3986_Parser に切り替えることで、ホスト名にアンダースコアを含む URI が正式にサポートされ、過去に配布された QR コード等の ID がそのまま利用可能になりました。変更は定数差し替えと軽微なバリデーションの更新のみで済んでおり、既存ユーザーへの影響は最小限です。Ruby 標準のパーサーとの整合性を取ることで、今後のメンテナンス性も向上します。

記事メタデータ

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

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

品質レビュー結果

Review Status:
リトライ後承認
Review Count:
3回 (改善を経て承認)
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文、背景、技術的詳細、設計判断(任意)、まとめが明確に配置されており、総論→各論→結論の構成が守られています。

カスタムMarkdown構文 ✓ PASS

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

コードブロックは ````ruby:filepath`` の形式で正しく記述され、PRリンクは [#202](URL) の形式で正しくリンク化されています。

対象読者への適合性 ✓ PASS

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

専門的なエンジニア向けの内容で、余計な初心者向け説明はなく適切です。

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

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

各セクションが総論・各論・結論で構成され、段落はトピックセンテンスで始まり 1 段落 1 トピック、長さも適切に分割されています。

Diff内容との照合 ✓ PASS

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

記事内のコードブロックは提供された Diff と一致し、変更点(パーサー置換、バリデーションコメント、parse メソッドコメント)を正確に反映しています。テスト変更の記述も Diff 内容と整合しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

URI パーサー関連の用語やクラス名が正しく使用されており、誤用はありません。

説明の技術的正確性 ✓ PASS

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

変更理由、影響範囲、実装内容が PR の説明と一致し、技術的に正確です。

事実の突合 ✓ PASS

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

すべての主張が PR タイトル・説明・Diff に裏付けられており、推測や外部知識の付加はありません。

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

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

PR 番号 #202、関連 Issue #201、日付 2024 年 11 月 など、数値・固有名詞は正確です。

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

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

記事タイトルは PR の「RFC2396 から RFC3986 へパーサーを変更」ことを正確に表現しています。

外部知識の正確性 ✓ PASS

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

バージョンサポート状況やリリーススケジュール等の外部情報は一切含んでおらず、PR に基づく内容のみです。

時間表現の正確性 ✓ PASS

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

「2024 年 11 月に Ruby 本体が変更」など、時間表現は PR の記載と一致しています。