Don't try to constantize GID's class too soon

rails/globalid

GlobalID::Locator の公開 API locate メソッドは、渡された GID のクラスがオプションで指定された only: リストに含まれるかを事前にチェックします。従来はこのチェックの前に常にモデルクラスを constantize していたため、only: が指定されていないケースでも不要な定数化が走っていました。この PR は、その無駄な constantize を回避し、カスタムデフォルトロケータがレガシーな GID を扱えるように修正します。

背景

問題点は、locatefetchonly: オプションの有無に関わらず gid.model_class を即座に定数化していたことです。この挙動により、アプリケーション側でデフォルトロケータを上書きしてレガシーなクラス名を解決しようとしても、事前の constantize が失敗し、ロケータが呼び出される前に例外が発生していました。特に、コードリファクタリングやクラス名変更がライブ環境で行われる際に、古い GID が残るケースで障害の原因となっていました。

カスタムデフォルトロケータは locate の最初の許可チェックを通過した後に実行される設計 であるため、事前の定数化が不要であることは設計上自然です。したがって、only: が指定されていないときはモデルクラスの定数化をスキップすべきという要件が PR の背景にあります。

技術的な変更

locatefetchfind_allowed? に渡す引数を gid.model_class から gid 全体に変更 しました。これにより、only: が提供されていない限り model_class の定数化が行われません。

@@ -34,7 +34,7 @@ class << self
       def locate(gid, options = {})
         gid = GlobalID.parse(gid)
-
-        return unless gid && find_allowed?(gid.model_class, options[:only])
+        return unless gid && find_allowed?(gid, options[:only])
@@ -70,7 +70,7 @@ def locate(gid, options = {})
       def fetch(gid, options = {})
         gid = GlobalID.parse(gid)
-
-        return unless gid && find_allowed?(gid.model_class, options[:only])
+        return unless gid && find_allowed?(gid, options[:only])

find_allowed? のシグネチャと実装を model_class から gid に変更 し、only: が指定されたときだけ gid.model_class を評価するようにしました。

@@ -180,8 +180,8 @@ def locator_for(gid)
           @locators.fetch(normalize_app(gid.app)) { default_locator }
         end
-
-        def find_allowed?(model_class, only = nil)
-          only ? Array(only).any? { |c| model_class <= c } : true
+        def find_allowed?(gid, only = nil)
+          only ? Array(only).any? { |c| gid.model_class <= c } : true

parse_allowed も同様に find_allowed? への引数を統一 し、内部ロジックは変更ありません。

@@ -191,7 +191,7 @@ def fetch_record(gid, options)
         end

         def parse_allowed(gids, only = nil)
-          gids.collect { |gid| GlobalID.parse(gid) }.compact.select { |gid| find_allowed?(gid.model_class, only) }
+          gids.collect { |gid| GlobalID.parse(gid) }.compact.select { |gid| find_allowed?(gid, only) }
         end

これらの変更は API のシグネチャや戻り値を変えずに内部ロジックだけを調整 しているため、既存コードへの破壊的影響はありません。

設計判断

定数化のタイミングを find_allowed? に集約した設計 が選択されました。only: が無い場合は true を返すだけに留め、モデルクラスの読み込みコストを回避します。この変更により、locatefetch のエントリーポイントはシンプルに保たれ、ロジックの責務が find_allowed? に集中します。

後方互換性を最優先 した点も重要です。only: オプションが提供されている既存呼び出しは振る舞いを変えず、オプションが無いケースだけが改善されます。そのため、アプリケーション側で追加のマイグレーションは不要です。

まとめ

この PR は 不要な定数化を回避し、レガシー GID を扱うカスタムロケータの実装を可能にした 変更です。find_allowed? に委譲したことでロジックがモジュール化され、パフォーマンスと拡張性が向上しました。既存 API はそのまま利用できるため、導入コストも最小限です。

記事メタデータ

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

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

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

カスタムMarkdown構文 ⚠ WARNING

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

PRリンクが `[PR #204](URL)` の形式で記載されており、要求される `[#204](URL)` 形式になっていません。その他のMarkdown構文は正しく使用されています。

対象読者への適合性 ✓ PASS

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

エンジニア向けの技術的記述で、初心者向けの過度な説明はありません。

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

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

各セクションは総論パラグラフ、各論パラグラフ、結論パラグラフで構成され、段落はトピックセンテンスで始まり、1段落1トピック・適切な長さが保たれています。

Diff内容との照合 ✓ PASS

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

記事中のdiffブロックはPRで提供されたdiffと完全に一致しており、コード変更が正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用されている技術用語はPRで示されたものと一致しており、誤用はありません。

説明の技術的正確性 ✓ PASS

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

変更の目的・影響についての説明はPRの記述と整合しており、技術的に正確です。

事実の突合 ✓ PASS

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

記事の主張はPRのタイトル・説明・diffに裏付けられており、推測や捏造はありません。

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

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

PR番号 #204 などの数値は正確です。

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

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

記事タイトルはPRタイトルと一致しています。

外部知識の正確性 ✓ PASS

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

PRに記載されていない外部知識(バージョンサポート等)は含まれていません。

時間表現の正確性 ✓ PASS

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

時間表現のズレはありません。