💭
[Feature #21219] Object#inspect に含まれるインスタンス変数を制御できるようにする提案
[Feature #21219] Object#inspect accept a list of instance variables to display
-
Object#inspectはオブジェクトのインスタンス変数も含まれるような形になっている-
#inspectはp等で内部から呼ばれるメソッド
-
class User
def initialize(name)
@name = name
end
end
user = User.new("homu")
p user.inspect
# => #<User:0x0000743db53f7bd8 @name="homu">
- これはデバッグ等で便利なんですがログ出力などで使用する場合に機密情報まで出力されてしまう弊害があります
require 'logger'
logger = Logger.new(STDOUT)
class DatabaseConfig
def initialize(host, user, password)
@host = host
@user = user
@password = password
end
end
env = {db_config: DatabaseConfig.new("localhost", "root", "hunter2")}
# 以下のようなログ出力をするときに内部で #inspect が呼ばれる
# パスワードのような秘匿したい情報まで出力されてしまう
logger.info("something happened, env: #{env}")
# => I, [2025-06-04T05:49:03.289106 #2350376] INFO -- : something happened, env: {db_config: #<DatabaseConfig:0x00007851931555d8 @host="localhost", @user="root", @password="hunter2">}
- このようなケースで
#inspectで参照するインスタンス変数を制御できるようにしたいという内容のチケット - チケットでは以下のように
#inspectに出力するインスタンス変数名を指定できるような提案がされている
require 'logger'
logger = Logger.new(STDOUT)
class DatabaseConfig
def initialize(host, user, password)
@host = host
@user = user
@password = password
end
# @host と @user のみ inspect に含める
def inspect = super(instance_variables: [:@host, :@user])
end
env = {db_config: DatabaseConfig.new("localhost", "root", "hunter2")}
logger.info("something happened, env: #{env}")
# => INFO -- : something happened, env: {db_config: #<DatabaseConfig:0x00000001002b3a08 @host="localhost", @user="root">}
- これなんですが pretty_print だと既に
#pretty_print_instance_variablesで含まれるインスタンス変数を制御できるような機能があるみたいですね- pretty_print は
ppのときに利用される機能 - https://bugs.ruby-lang.org/issues/21219#note-1
- Object#pretty_print_instance_variables (Ruby 3.4 リファレンスマニュアル)
- pretty_print は
class Foo
def initialize
@pub_1 = :A
@pub_2 = :B
@priv_1 = :secret
@priv_2 = :secret
end
def pretty_print_instance_variables
super - [:@priv_1, :@priv_2]
end
end
pp Foo.new
# => #<Foo:0x0000711c6c83cb70 @pub_1=:A, @pub_2=:B>
-
#pretty_print_instance_variablesは知らなかった - 別の案だと対象のインスタンス変数が
#inspectに含まれるかどうかをチェックするメソッドで判定する案もコメントされていますね
class Foo
def initialize
@pub_1 = :A
@pub_2 = :B
@priv_1 = :secret
@priv_2 = :secret
end
# false を返すと #inspect には含まれなくなる
def inspect_include_variable?(ivar)
ivar != :@priv_1 && ivar != :@priv2
end
end
pp Foo.new
# => #<Foo:0x0000711c6c83cb70 @pub_1=:A, @pub_2=:B>
- この場合だとメソッドが定義されていない場合は常に
trueを返す、みたな最適化ができるみたいですね - devise gem でも機密情報が出力されないように
#inspectを書き換えたりしているので実際の利用ケースはありそうですねー - これなんですが最終的には『
#instance_variables_to_inspectが返したインスタンス変数名のみ#inspectに含める』というような挙動になりました - これは既に開発版の Ruby 3.5-dev で実装済みです
class DatabaseConfig
def initialize(host, user, password)
@host = host
@user = user
@password = password
end
# @host と @user のみ inspect に含める
def instance_variables_to_inspect = [:@host, :@user]
end
p DatabaseConfig.new("localhost", "root", "hunter2")
# Ruby 3.4 => #<DatabaseConfig:0x0000736643e793f8 @host="localhost", @user="root", @password="hunter2">
# Ruby 3.5 => #<DatabaseConfig:0x00007668a74a8fe0 @host="localhost", @user="root">
Discussion