クラス単位で GlobalID のアプリ名を設定可能に

rails/globalid

GlobalID::Identification がクラスごとに独自の app を指定できるようになり、to_gid / to_sgid がオーバーライドを正しく受け継ぐようになりました。これにより、外部サービス向けモデルや gem が提供するモデルでも、名前空間を分離した Global ID を簡潔に扱えるようになります。

背景

GlobalID::Identification は従来、
GlobalID.create
のデフォルト app(Rails.application.class.module_parent_name 由来)を全モデルで共有していました。そのため、Gem が提供するモデルや外部サービスのエンティティに対して固有の app を付与したい場合、個別に to_global_idto_signed_global_id を上書きしなければならず、コードが冗長になるケースがありました。

このような背景から、モデル単位で app を設定できる仕組みが求められ、PR #208per‑class app 設定 が導入されました。変更は既存コードへの影響を最小限に抑えつつ、カスタムロケータ実装をシンプルにします。

クラスごとに app を持てることは、名前空間分離と可読性向上に直結し、今後の拡張性を高めます。

技術的な変更

このセクションでは、実装上の主要変更点とその効果を示します。

クラス属性の導入

lib/global_id/identification.rbActiveSupport::Concernclass_attribute が組み込まれ、モジュールがクラスレベルで状態を保持できるようになりました。included ブロックで _global_id_app が定義され、インスタンスからは直接参照できません。

included do
  class_attribute :_global_id_app, instance_accessor: false
end

この属性は継承チェーンで自動的にコピーされるため、サブクラスが明示的に設定しない限り親クラスの app が継承されます。

クラスメソッドの追加

クラスメソッド global_id_appglobal_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_optionsapp: が未指定の場合にクラス属性を参照するようになりました。

def to_global_id(options = {})
  GlobalID.create(self, global_id_options(options))
end

この変更だけで、クラス固有の app が自動的に適用されます。

ショートカットメソッドの委譲

従来は alias to_gid to_global_id などの静的エイリアスが使用されていましたが、to_gidto_sgid が可変長引数を受け取り、対応する長形式メソッドへ委譲する形に改められました。これにより、to_global_idto_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 が提供するモデルでも、名前空間を明示的に管理でき、カスタムロケータの実装がシンプルになります。既存コードへの影響は最小限で、テストにより後方互換性が保証されています。

記事メタデータ

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

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文・背景・技術的な変更・設計判断・まとめの5セクションが揃っており、総論→各論→結論の流れが明確です。

カスタムMarkdown構文 ✓ PASS

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

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

対象読者への適合性 ✓ PASS

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

内容はRailsエンジニア向けで、専門用語の説明が中心。初心者向けの過剰な説明はありません。

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

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

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

Diff内容との照合 ✓ PASS

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

記事中のコードスニペットはDiffに含まれる変更と一致しており、抜けや改変はありません。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

GlobalID、class_attribute、ActiveSupport::Concern、URI::GID.validate_app などの用語が正しく使用されています。

説明の技術的正確性 ✓ PASS

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

技術的な説明はDiffおよびPRの記述と合致し、誤った主張や不整合は見られません。

事実の突合 ✓ PASS

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

全ての主張がPR情報やテストコードに裏付けられており、推測や外部知識の付与はありません。

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

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

PR番号やファイル名、コード中の設定値は正確です。

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

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

記事タイトルはPRの内容「per‑class GlobalID app configuration」を正確に日本語化しています。

外部知識の正確性 ✓ PASS

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

LTS、リリース日程等の外部情報は含まれていません。

時間表現の正確性 ✓ PASS

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

時間表現の誤りはなく、PRの記述と一致しています。