テーブル名設定時の Predicate Builder リセットを条件付きに変更
ActiveRecord の table_name= セッターが、テーブル名が既に設定されている場合にのみ @predicate_builder をリセットするよう改修され、遅延テーブル名計算で発生していた TypeError: can't quote Regexp を防ぎます。
背景
ActiveRecord でテーブル名の算出を遅延化した PR #57642 により、table_name= が最初に呼び出されたときでも内部状態がリセットされるケースが生じました。結果として、カスタム型ハンドラ(例: Regexp)を利用した結合条件の登録時に @predicate_builder が初期化され、ActiveRecord::PredicateBuilderTest#test_registering_new_handlers_for_joins が失敗し TypeError が投げられました。Issue #57824 で報告されたこの不具合は、テーブル名が未設定の初回計算時にリセットが走ることが根本原因です。
技術的な変更
変更前の実装
def table_name=(value)
if defined?(@table_name)
return if value == @table_name
reset_column_information if connected?
end
@table_name = value
@arel_table = Arel::Table.new(klass: self)
@sequence_name = nil unless @explicit_sequence_name
@predicate_builder = nil
end
変更後の実装
def table_name=(value)
if defined?(@table_name)
return if value == @table_name
reset_column_information if connected?
@predicate_builder = nil
end
@table_name = value
@arel_table = Arel::Table.new(klass: self)
@sequence_name = nil unless @explicit_sequence_name
end
主な差分は、@predicate_builder = nil を if ブロック内へ移動し、テーブル名が既に定義されている場合にだけリセットするようにした点です。元の実装では @predicate_builder が常に末尾でリセットされ、テーブル名が未設定でもクリアされていました。今回の変更により、初回の遅延算出時には @predicate_builder が保持され、カスタム型ハンドラの登録が安全に行えるようになりました。
設計判断
@predicate_builder のリセットは テーブル名が変更されたとき にのみ必要と判断し、最小限の条件分岐(defined?(@table_name))で実装しました。既存の API 挙動や後方互換性への影響はなく、既にテーブル名を持つモデルに対しては従来通りリセットされます。これにより、内部状態管理を正確に保ちつつ、不要なリセットによるエッジケースを排除しています。
まとめ
この PR は、遅延テーブル名計算に伴う @predicate_builder の不適切なリセットを防ぎ、Issue #57824 で報告されたテスト失敗を解消します。変更は局所的で後方互換性に影響せず、ActiveRecord の内部状態管理をより堅牢にする設計判断と言えます。