🐥

Rails 7.2 から ActiveRecord::Core#inspect が id しか出力されなくて困っている

2024/09/04に公開

例えば以下のような schema のモデルのオブジェクトを ppp などで出力する場合に Rails 7.2 以前では全カラムの値が出力されていました。

ActiveRecord::Schema.define do
  create_table :users, force: true do |t|
    t.string :name
    t.string :email
    t.boolean :active
    t.timestamps
  end
end

class User < ActiveRecord::Base
end

homu = User.create(name: "homu", email: "homu@@example.com", active: true)

# 各カラムの値が出力される
pp homu
# => #<User:0x00007def9a6bde58
#     id: 1,
#     name: "homu",
#     email: "homu@@example.com",
#     active: true,
#     created_at: 2024-09-08 02:37:14.044523 UTC,
#     updated_at: 2024-09-08 02:37:14.044523 UTC>

しかし Rails 7.2 では以下のように id カラムの値のみが出力されるようになりました。

homu = User.create(name: "homu", email: "homu@@example.com", active: true)

# id のみが出力される
pp homu
# => #<User:0x000075967c049dd0 id: 1>

これは以下の PR で『 #inspect #pretty_print で出力するカラムを制御できるようにした』対応による影響になります。

ActiveRecord::Core.attributes_for_inspect

Rails 7.2 では新しく ActiveRecord::Core.attributes_for_inspect というメソッドが追加されました。
これは #inspect#pretty_print に含まれるカラムを制御するための機能になります。
#inspect#pretty_printppp で出力するときに内部で呼ばれるメソッドになります。
例えば次のようにして #inspect#pretty_printid name email のみ含まれるように設定することができます。

class User < ActiveRecord::Base
  # #inspect で出力したいカラム名を配列で設定する
  self.attributes_for_inspect = %w(id name email)
end

homu = User.create(name: "homu", email: "homu@@example.com", active: true)

pp homu
# => #<User:0x000074d8f0c58850 id: 1, name: "homu", email: "homu@@example.com">

また次のように :all を設定することで『すべてのカラムの値』が #inspect に含まれます。

class User < ActiveRecord::Base
  self.attributes_for_inspect = :all
end

homu = User.create(name: "homu", email: "homu@@example.com", active: true)

pp homu
# => #<User:0x000073170d9113a0
#     id: 1,
#     name: "homu",
#     email: "homu@@example.com",
#     active: true,
#     created_at: "2024-09-08 11:07:28.004564000 +0000",
#     updated_at: "2024-09-08 11:07:28.004564000 +0000">

Rails 7.2 ではこれがデフォルトで attributes_for_inspect = [:id] で設定されています。
なのでデフォルトでは #inspectid のみが含まれていることになります。
ただし development test 環境では :all が設定されているのですべてのカラムが出力されます。

デフォルトの attributes_for_inspect を設定する

デフォルトの attributes_for_inspect の設定は configure で設定することができます。

Rails.application.configure do
  active_record.attributes_for_inspect = :all
end

もしくは ActiveRecord だけの場合であれば次のようにしても設定できます。

ActiveRecord::Base.attributes_for_inspect = :all

development test 環境以外でも全部のカラムの値を出力したい場合はこれで対応できます。

所感

正直なところ ActiveRecord::Core.attributes_for_inspect 自体はよい機能だと思うんですが(秘匿したい値をカジュアルに出力されないようにする、みたいニーズはある)デフォルトで id しか出力されないのはやり過ぎだと思いますね。
#inspect で出力したいケースってデバッグ目的でデータを参照したり保存したりすることが多いと思うんですが id だけ出力されたところで全く意味をなさないですよね。
一応 development test 環境だとデフォルトで :all が設定されているので挙動が変わらないらしいんですが、逆にそれ以外の環境で差異がでてしまうことによって CI で挙動が担保できなかったりとか、そもそも #inspect がログ出力とかに依存している場合は全部壊れてしまいます。
なので結局全部 :all にするのであればデフォルトを [:id] にする必要性がないと思います(そもそもパフォーマンス的に問題があるのであれば #inspect をどうにかするのではなくてその処理自体で対応してほしい……。

最初にも書いたんですが #inspect の出力がやや冗長にあることとかはままあるので、それを制御するために ActiveRecord::Core#attributes_for_inspect を利用したりできるのはめっちゃいいと思うのでデフォルトを id だけにするのはやめてほしいなあ…。

おまけ:特定のカラムのみ除外する

attributes_for_inspect は『特定のカラムのみ指定する』なんですが次のようにすれば『特定のカラムを除外する』こともできます。

class User < ActiveRecord::Base
  # attribute_names は全カラム名を返すのでそこから不要なカラムを引く
  self.attributes_for_inspect = attribute_names - %w(created_at updated_at)
end

homu = User.create(name: "homu", email: "homu@@example.com", active: true)

pp homu
# => #<User:0x000072c2db9f5f68
#     id: 1,
#     name: "homu",
#     email: "homu@@example.com",
#     active: true>
GitHubで編集を提案

Discussion