[Feature #21219] Selective inspect of instance variables
Make Kernel#inspect ask which instance variables should be dumped by the result of `#instance_variables_to_inspect`. Co-Authored-By: Jean Boussier <byroot@ruby-lang.org>
This commit is contained in:
parent
e8094943a4
commit
f4135feafc
Notes:
git
2025-06-09 07:26:08 +00:00
20
NEWS.md
20
NEWS.md
@ -14,6 +14,26 @@ Note that each entry is kept to a minimum, see links for details.
|
|||||||
|
|
||||||
Note: We're only listing outstanding class updates.
|
Note: We're only listing outstanding class updates.
|
||||||
|
|
||||||
|
* Kernel
|
||||||
|
|
||||||
|
* `Kernel#inspect` now check for the existence of a `#instance_variables_to_inspect` method
|
||||||
|
allowing to control which instance variables are displayed in the `#inspect` string:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
class DatabaseConfig
|
||||||
|
def initialize(host, user, password)
|
||||||
|
@host = host
|
||||||
|
@user = user
|
||||||
|
@password = password
|
||||||
|
end
|
||||||
|
|
||||||
|
private def instance_variables_to_inspect = [:@host, :@user]
|
||||||
|
end
|
||||||
|
|
||||||
|
conf = DatabaseConfig.new("localhost", "root", "hunter2")
|
||||||
|
conf.inspect #=> #<DatabaseConfig:0x0000000104def350 @host="localhost", @user="root">
|
||||||
|
```
|
||||||
|
|
||||||
* Binding
|
* Binding
|
||||||
|
|
||||||
* `Binding#local_variables` does no longer include numbered parameters.
|
* `Binding#local_variables` does no longer include numbered parameters.
|
||||||
|
56
object.c
56
object.c
@ -83,6 +83,7 @@ static VALUE rb_cFalseClass_to_s;
|
|||||||
#define id_init_dup idInitialize_dup
|
#define id_init_dup idInitialize_dup
|
||||||
#define id_const_missing idConst_missing
|
#define id_const_missing idConst_missing
|
||||||
#define id_to_f idTo_f
|
#define id_to_f idTo_f
|
||||||
|
static ID id_instance_variables_to_inspect;
|
||||||
|
|
||||||
#define CLASS_OR_MODULE_P(obj) \
|
#define CLASS_OR_MODULE_P(obj) \
|
||||||
(!SPECIAL_CONST_P(obj) && \
|
(!SPECIAL_CONST_P(obj) && \
|
||||||
@ -733,11 +734,17 @@ rb_inspect(VALUE obj)
|
|||||||
static int
|
static int
|
||||||
inspect_i(ID id, VALUE value, st_data_t a)
|
inspect_i(ID id, VALUE value, st_data_t a)
|
||||||
{
|
{
|
||||||
VALUE str = (VALUE)a;
|
VALUE *args = (VALUE *)a, str = args[0], ivars = args[1];
|
||||||
|
|
||||||
/* need not to show internal data */
|
/* need not to show internal data */
|
||||||
if (CLASS_OF(value) == 0) return ST_CONTINUE;
|
if (CLASS_OF(value) == 0) return ST_CONTINUE;
|
||||||
if (!rb_is_instance_id(id)) return ST_CONTINUE;
|
if (!rb_is_instance_id(id)) return ST_CONTINUE;
|
||||||
|
if (!NIL_P(ivars)) {
|
||||||
|
VALUE name = ID2SYM(id);
|
||||||
|
for (long i = 0; RARRAY_AREF(ivars, i) != name; ) {
|
||||||
|
if (++i >= RARRAY_LEN(ivars)) return ST_CONTINUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (RSTRING_PTR(str)[0] == '-') { /* first element */
|
if (RSTRING_PTR(str)[0] == '-') { /* first element */
|
||||||
RSTRING_PTR(str)[0] = '#';
|
RSTRING_PTR(str)[0] = '#';
|
||||||
rb_str_cat2(str, " ");
|
rb_str_cat2(str, " ");
|
||||||
@ -752,13 +759,15 @@ inspect_i(ID id, VALUE value, st_data_t a)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
inspect_obj(VALUE obj, VALUE str, int recur)
|
inspect_obj(VALUE obj, VALUE a, int recur)
|
||||||
{
|
{
|
||||||
|
VALUE *args = (VALUE *)a, str = args[0];
|
||||||
|
|
||||||
if (recur) {
|
if (recur) {
|
||||||
rb_str_cat2(str, " ...");
|
rb_str_cat2(str, " ...");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
rb_ivar_foreach(obj, inspect_i, str);
|
rb_ivar_foreach(obj, inspect_i, a);
|
||||||
}
|
}
|
||||||
rb_str_cat2(str, ">");
|
rb_str_cat2(str, ">");
|
||||||
RSTRING_PTR(str)[0] = '#';
|
RSTRING_PTR(str)[0] = '#';
|
||||||
@ -791,17 +800,47 @@ inspect_obj(VALUE obj, VALUE str, int recur)
|
|||||||
* end
|
* end
|
||||||
* end
|
* end
|
||||||
* Bar.new.inspect #=> "#<Bar:0x0300c868 @bar=1>"
|
* Bar.new.inspect #=> "#<Bar:0x0300c868 @bar=1>"
|
||||||
|
*
|
||||||
|
* If _obj_ responds to +instance_variables_to_inspect+, then only
|
||||||
|
* the instance variables listed in the returned array will be included
|
||||||
|
* in the inspect string.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* class DatabaseConfig
|
||||||
|
* def initialize(host, user, password)
|
||||||
|
* @host = host
|
||||||
|
* @user = user
|
||||||
|
* @password = password
|
||||||
|
* end
|
||||||
|
*
|
||||||
|
* private
|
||||||
|
* def instance_variables_to_inspect = [:@host, :@user]
|
||||||
|
* end
|
||||||
|
*
|
||||||
|
* conf = DatabaseConfig.new("localhost", "root", "hunter2")
|
||||||
|
* conf.inspect #=> #<DatabaseConfig:0x0000000104def350 @host="localhost", @user="root">
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_obj_inspect(VALUE obj)
|
rb_obj_inspect(VALUE obj)
|
||||||
{
|
{
|
||||||
if (rb_ivar_count(obj) > 0) {
|
VALUE ivars = rb_check_funcall(obj, id_instance_variables_to_inspect, 0, 0);
|
||||||
VALUE str;
|
st_index_t n = 0;
|
||||||
|
if (UNDEF_P(ivars)) {
|
||||||
|
n = rb_ivar_count(obj);
|
||||||
|
ivars = Qnil;
|
||||||
|
}
|
||||||
|
else if (!NIL_P(ivars)) {
|
||||||
|
Check_Type(ivars, T_ARRAY);
|
||||||
|
n = RARRAY_LEN(ivars);
|
||||||
|
}
|
||||||
|
if (n > 0) {
|
||||||
VALUE c = rb_class_name(CLASS_OF(obj));
|
VALUE c = rb_class_name(CLASS_OF(obj));
|
||||||
|
VALUE args[2] = {
|
||||||
str = rb_sprintf("-<%"PRIsVALUE":%p", c, (void*)obj);
|
rb_sprintf("-<%"PRIsVALUE":%p", c, (void*)obj),
|
||||||
return rb_exec_recursive(inspect_obj, obj, str);
|
ivars
|
||||||
|
};
|
||||||
|
return rb_exec_recursive(inspect_obj, obj, (VALUE)args);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return rb_any_to_s(obj);
|
return rb_any_to_s(obj);
|
||||||
@ -4600,6 +4639,7 @@ void
|
|||||||
Init_Object(void)
|
Init_Object(void)
|
||||||
{
|
{
|
||||||
id_dig = rb_intern_const("dig");
|
id_dig = rb_intern_const("dig");
|
||||||
|
id_instance_variables_to_inspect = rb_intern_const("instance_variables_to_inspect");
|
||||||
InitVM(Object);
|
InitVM(Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,4 +28,34 @@ describe "Kernel#inspect" do
|
|||||||
end
|
end
|
||||||
obj.inspect.should be_kind_of(String)
|
obj.inspect.should be_kind_of(String)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ruby_version_is "3.5" do
|
||||||
|
it "calls #instance_variables_to_inspect private method to know which variables to display" do
|
||||||
|
obj = Object.new
|
||||||
|
obj.instance_eval do
|
||||||
|
@host = "localhost"
|
||||||
|
@user = "root"
|
||||||
|
@password = "hunter2"
|
||||||
|
end
|
||||||
|
obj.singleton_class.class_eval do
|
||||||
|
private def instance_variables_to_inspect = %i[@host @user @does_not_exist]
|
||||||
|
end
|
||||||
|
|
||||||
|
inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
|
||||||
|
inspected.should == '#<Object:0x00 @host="localhost", @user="root">'
|
||||||
|
|
||||||
|
obj = Object.new
|
||||||
|
obj.instance_eval do
|
||||||
|
@host = "localhost"
|
||||||
|
@user = "root"
|
||||||
|
@password = "hunter2"
|
||||||
|
end
|
||||||
|
obj.singleton_class.class_eval do
|
||||||
|
private def instance_variables_to_inspect = []
|
||||||
|
end
|
||||||
|
|
||||||
|
inspected = obj.inspect.sub(/^#<Object:0x[0-9a-f]+/, '#<Object:0x00')
|
||||||
|
inspected.should == "#<Object:0x00>"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -950,6 +950,19 @@ class TestObject < Test::Unit::TestCase
|
|||||||
assert_match(/\bInspect\u{3042}:.* @\u{3044}=42\b/, x.inspect)
|
assert_match(/\bInspect\u{3042}:.* @\u{3044}=42\b/, x.inspect)
|
||||||
x.instance_variable_set("@\u{3046}".encode(Encoding::EUC_JP), 6)
|
x.instance_variable_set("@\u{3046}".encode(Encoding::EUC_JP), 6)
|
||||||
assert_match(/@\u{3046}=6\b/, x.inspect)
|
assert_match(/@\u{3046}=6\b/, x.inspect)
|
||||||
|
|
||||||
|
x = Object.new
|
||||||
|
x.singleton_class.class_eval do
|
||||||
|
private def instance_variables_to_inspect = [:@host, :@user]
|
||||||
|
end
|
||||||
|
|
||||||
|
x.instance_variable_set(:@host, "localhost")
|
||||||
|
x.instance_variable_set(:@user, "root")
|
||||||
|
x.instance_variable_set(:@password, "hunter2")
|
||||||
|
s = x.inspect
|
||||||
|
assert_include(s, "@host=\"localhost\"")
|
||||||
|
assert_include(s, "@user=\"root\"")
|
||||||
|
assert_not_include(s, "@password=")
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_singleton_methods
|
def test_singleton_methods
|
||||||
|
Loading…
x
Reference in New Issue
Block a user