Rails 7.2 から ActiveRecord::Core#inspect が id しか出力されなくて困っている
例えば以下のような schema のモデルのオブジェクトを p や pp などで出力する場合に 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_print は p や pp で出力するときに内部で呼ばれるメソッドになります。
例えば次のようにして #inspect や #pretty_print に id 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] で設定されています。
なのでデフォルトでは #inspect で id のみが含まれていることになります。
ただし 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>
Discussion