`insert_all` / `upsert_all` の匿名クラスにおけるログメッセージを修正
insert_all および upsert_all 実行時のログメッセージが、匿名クラスに対して #<Class:0x0000ffff7e683480> Upsert のような不読なオブジェクト参照を出力していた問題が修正されました。これにより、匿名クラスでも AnonymousBook Bulk Insert のような読みやすいログエントリが記録されるようになります。
背景
匿名クラス(Class.new で生成されたクラス)を使って insert_all や upsert_all を実行すると、ログに出力されるモデル名がオブジェクトの内部表現(例: #<Class:0x0000ffff7e683480>)になっていました。通常のクラスであれば Book Bulk Upsert と出力されるところが、匿名クラスでは判読不能な文字列になるという問題です。
この問題は、ログメッセージを構築する際に model オブジェクトそのものを文字列補間していたことが原因です。Rubyでは、クラスオブジェクトを文字列補間すると to_s が呼ばれますが、匿名クラスの to_s はオブジェクトIDを含む内部表現を返します。一方、name メソッドは定義されていれば任意の文字列を返せるため、匿名クラスに self.name を定義することで読みやすい名前を提供できます。
技術的な変更
activerecord/lib/active_record/insert_all.rb の execute メソッド内で、ログメッセージを構築する1行が変更されました。
変更前:
message = +"#{model} "
変更後:
message = +"#{model.name} "
変更は model から model.name への切り替えのみです。model.name を呼ぶことで、クラスオブジェクトの to_s ではなく name メソッドの戻り値が使われます。通常の名前付きクラスでは name は定数名(例: "Book")を返すため、既存の動作は変わりません。
テストでは、Class.new(Book) で匿名クラスを生成し、self.name を "AnonymousBook" と定義した上で insert を呼び出し、ログに AnonymousBook Insert が含まれることを検証するケースが追加されています。
def test_insert_logs_message_including_model_name_from_anonymous_class
skip unless supports_insert_conflict_target?
anonymous_book_klass = Class.new(Book) do
def self.name
"AnonymousBook"
end
end
capture_log_output do |output|
anonymous_book_klass.insert({ name: "Rework", author_id: 1 })
assert_match "AnonymousBook Insert", output.string
end
end
設計判断
model.to_s ではなく model.name を使うという選択がなされています。
Rubyの Module#name は、クラスが定数に代入されていれば定数名の文字列を、匿名クラスであれば nil を返します。一方 Module#to_s は匿名クラスでも #<Class:0x...> 形式の文字列を返します。Active Recordの既存コードでは model.name はモデル名解決のための標準的な手段であり、ApplicationRecord を継承するクラスでは name が常に意味のある文字列を返すことが期待されています。今回のケースでは self.name を上書き定義することで匿名クラスでも可読な名前を提供できるというRubyの設計を活かした修正といえます。
なお、name が nil を返す完全な匿名クラス(self.name を定義しない場合)については、文字列補間で "" に変換されるため、ログが " Bulk Insert" のように先頭が空文字になる可能性は残りますが、今回の修正スコープは self.name を定義した匿名クラスに限定されています。
まとめ
本PRは1行の変更で、insert_all / upsert_all のログ可読性を匿名クラスに対しても保証します。model.name への変更は既存の名前付きクラスの動作に影響せず、匿名クラスでも self.name を定義することでデバッグ・運用上の可視性を損なわないパターンを確立しています。