ActiveRecord の primary_key 定義で class_attribute の定義場所を修正

rails/rails

ActiveRecord の primary_key 定義に利用される _primary_key_definition を、誤って inherited フックで定義していた箇所を included フックへ移動し、子クラスや孫クラスでの不要な再定義を防止した変更です。

背景

_primary_key_definitioninherited で定義されると、サブクラスが生成されるたびに同属性が再定義される という問題が指摘されました。これは、サブクラスごとに class_attribute の内部状態が再初期化され、期待した継承関係が壊れる原因となります。関連 PR #57775 で primary_keyclass attribute にしたことに伴い、正しい定義タイミングが求められるようになりました。

この問題は、抽象クラスや STI(シングルテーブル継承)を利用する際に、親クラスで設定した primary_key が子クラスへ正しく継承されないケースとして顕在化していました。したがって、定義場所の見直しは ActiveRecord の継承モデル全体の一貫性を保つ上で不可欠です。

技術的な変更

class_attribute :_primary_key_definition の定義を included ブロックに移動し、inherited フックからは削除 しました。これにより、コンテナがモジュールに混入された時点で属性が一度だけ定義され、以降のサブクラスは自動的に同属性を継承します。

変更前(抜粋):

def inherited(base)
  super
  base.class_eval do
    class_attribute :_primary_key_definition, instance_accessor: false
    @attributes_builder = nil
  end
end

変更後(抜粋):

included do
  class_attribute :_primary_key_definition, instance_accessor: false
end

テストコードも同様に更新され、primary_key を設定した親クラスと子クラスの両方で _primary_key_definition.name が一致 することを明示的に検証しています。

k = Class.new(ActiveRecord::Base)
subclass = Class.new(k)

k.primary_key = "foo"
assert_equal "foo", k._primary_key_definition&.name
assert_equal "foo", subclass._primary_key_definition&.name

この修正により、class_attribute の再定義が排除され、属性の継承が期待通りに機能するようになりました。

設計判断

モジュールがインクルードされた時点で class_attribute を定義する方針 が採用されました。class_attribute はその性質上、モジュールが混入されたクラスの階層全体で共有されるべきであり、inherited フックでの定義は不必要かつ副作用を招く実装です。

この判断は、後方互換性を保ちつつ最小限のコード変更で問題を根本的に解決 することを目的としています。included ブロックは一度だけ実行されるため、既存の primary_key 設定ロジックに影響を与えることなく、安全に属性定義を統一できます。

まとめ

本 PR は、_primary_key_definitionclass_attribute 定義箇所を inherited から included へ移すことで、サブクラス間での不要な再定義を防止し、ActiveRecord の継承モデルにおける属性共有を正しく機能させました。テストの更新により、設定が正しく継承されることが自動的に保証されています。

記事メタデータ

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

この記事は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 #57775 がリンク化されていません。#付きでリンク化する必要があります。

対象読者への適合性 ✓ PASS

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

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

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

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

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

Diff内容との照合 ✓ PASS

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

記事中のコードスニペットは提供されたDiffと一致しており、変更点が正確に反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

class_attribute、inherited、included、primary_key などの用語は正しく使用されています。

説明の技術的正確性 ✓ PASS

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

技術的な説明はDiffとPRの記述に基づき、論理的かつ正確です。

事実の突合 ✓ PASS

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

全ての主張がPRタイトル・説明・Diffで裏付けられており、推測や捏造はありません。

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

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

PR番号 #57810 と #57775 の記載は正しいです。数値や固有名詞の誤りはありません。

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

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

記事タイトルはPRの内容を正確に日本語で表現しており、一致しています。

外部知識の正確性 ✓ PASS

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

バージョンやリリース日程などPRに無い外部情報は含まれていません。

時間表現の正確性 ✓ PASS

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

時間表現の歪曲はなく、PRの記載と整合しています。