クラス単位で GlobalID のアプリ名を設定可能に
GlobalID::Identification がクラスごとに独自の app を指定できるようになり、to_gid / to_sgid がオーバーライドを正しく受け継ぐようになりました。これにより、外部サービス向けモデルや gem が提供するモデルでも、名前空間を分離した Global ID を簡潔に扱えるようになります。
背景
GlobalID::Identification は従来、 のデフォルト app(
GlobalID.createRails.application.class.module_parent_name 由来)を全モデルで共有していました。そのため、Gem が提供するモデルや外部サービスのエンティティに対して固有の app を付与したい場合、個別に to_global_id や to_signed_global_id を上書きしなければならず、コードが冗長になるケースがありました。
このような背景から、モデル単位で app を設定できる仕組みが求められ、PR #208 で per‑class app 設定 が導入されました。変更は既存コードへの影響を最小限に抑えつつ、カスタムロケータ実装をシンプルにします。
クラスごとに app を持てることは、名前空間分離と可読性向上に直結し、今後の拡張性を高めます。
技術的な変更
このセクションでは、実装上の主要変更点とその効果を示します。
クラス属性の導入
lib/global_id/identification.rb に ActiveSupport::Concern と class_attribute が組み込まれ、モジュールがクラスレベルで状態を保持できるようになりました。included ブロックで _global_id_app が定義され、インスタンスからは直接参照できません。
included do
class_attribute :_global_id_app, instance_accessor: false
end
この属性は継承チェーンで自動的にコピーされるため、サブクラスが明示的に設定しない限り親クラスの app が継承されます。
クラスメソッドの追加
クラスメソッド global_id_app と global_id_app= が提供され、設定時には URI::GID.validate_app による検証が行われます。
class_methods do
def global_id_app
_global_id_app
end
def global_id_app=(app)
self._global_id_app = URI::GID.validate_app(app)
end
end
これにより、self.global_id_app = "stripe" のように一行でアプリ名を指定でき、global_id_app が nil の場合はデフォルト app が使用されます。
to_global_id のオプション注入
to_global_id の実装が GlobalID.create(self, options) から GlobalID.create(self, global_id_options(options)) へ変更され、global_id_options が app: が未指定の場合にクラス属性を参照するようになりました。
def to_global_id(options = {})
GlobalID.create(self, global_id_options(options))
end
この変更だけで、クラス固有の app が自動的に適用されます。
ショートカットメソッドの委譲
従来は alias to_gid to_global_id などの静的エイリアスが使用されていましたが、to_gid と to_sgid が可変長引数を受け取り、対応する長形式メソッドへ委譲する形に改められました。これにより、to_global_id や to_signed_global_id をオーバーライドした際に、ショートカット側でも同様の振る舞いが保証されます。
# 変更前
alias to_gid to_global_id
# 変更後
def to_gid(...)
to_global_id(...)
end
テストでも to_gid がオーバーライドされた to_global_id の結果をそのまま返すことが確認されています。
これらの変更は、既存の to_gid / to_sgid 呼び出しを壊さず、クラス単位の設定機構だけを新たに提供します。
設計判断
クラス属性による拡張 が選択された理由は、class_attribute が継承を自動的に扱う点にあります。サブクラスが独自に global_id_app を設定しなければ、親クラスの設定が継承されるため、設定漏れによる予期せぬ app 変更を防げます。また、URI::GID.validate_app によるバリデーションを setter に組み込むことで、無効な文字列が流入するリスクを排除しています。
メソッド委譲への変更 は、オーバーライド可能性を高めるためのトレードオフです。エイリアスはコンパイル時に固定されるため、上書きされたメソッドが呼ばれません。可変長引数を受け取り to_global_id / to_signed_global_id へ委譲することで、ユーザーがカスタムロジックを追加した場合でも一貫した挙動が保たれます。呼び出しコストは元のエイリアスと同程度であり、パフォーマンスへの影響は PR で測定されていないものの、実装上の負荷は極めて低いと評価できます。
後方互換性の確保 も重要視され、global_id_app が未設定の場合は従来通りデフォルト app が使用されます。既存コードは変更不要で動作し続けます。
まとめ
本 PR により、モデル単位で任意の app を設定でき、to_gid / to_sgid がオーバーライドを正しく伝搬するようになりました。これにより、外部サービスオブジェクトや gem が提供するモデルでも、名前空間を明示的に管理でき、カスタムロケータの実装がシンプルになります。既存コードへの影響は最小限で、テストにより後方互換性が保証されています。