Don't try to constantize GID's class too soon
GlobalID::Locator の公開 API locate メソッドは、渡された GID のクラスがオプションで指定された only: リストに含まれるかを事前にチェックします。従来はこのチェックの前に常にモデルクラスを constantize していたため、only: が指定されていないケースでも不要な定数化が走っていました。この PR は、その無駄な constantize を回避し、カスタムデフォルトロケータがレガシーな GID を扱えるように修正します。
背景
問題点は、locate と fetch が only: オプションの有無に関わらず gid.model_class を即座に定数化していたことです。この挙動により、アプリケーション側でデフォルトロケータを上書きしてレガシーなクラス名を解決しようとしても、事前の constantize が失敗し、ロケータが呼び出される前に例外が発生していました。特に、コードリファクタリングやクラス名変更がライブ環境で行われる際に、古い GID が残るケースで障害の原因となっていました。
カスタムデフォルトロケータは locate の最初の許可チェックを通過した後に実行される設計 であるため、事前の定数化が不要であることは設計上自然です。したがって、only: が指定されていないときはモデルクラスの定数化をスキップすべきという要件が PR の背景にあります。
技術的な変更
locate と fetch が find_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 を返すだけに留め、モデルクラスの読み込みコストを回避します。この変更により、locate と fetch のエントリーポイントはシンプルに保たれ、ロジックの責務が find_allowed? に集中します。
後方互換性を最優先 した点も重要です。only: オプションが提供されている既存呼び出しは振る舞いを変えず、オプションが無いケースだけが改善されます。そのため、アプリケーション側で追加のマイグレーションは不要です。
まとめ
この PR は 不要な定数化を回避し、レガシー GID を扱うカスタムロケータの実装を可能にした 変更です。find_allowed? に委譲したことでロジックがモジュール化され、パフォーマンスと拡張性が向上しました。既存 API はそのまま利用できるため、導入コストも最小限です。