From 697cce37cbc613c78a9c021186adacf90e2fa7bf Mon Sep 17 00:00:00 2001 From: Alexander Momchilov Date: Fri, 28 Jul 2023 09:34:12 -0400 Subject: [PATCH] Omit "#class" for simple values --- lib/debug/variable_inspector.rb | 14 +++++++++- test/debug/variable_inspector_test.rb | 38 +++++++++++++++++++-------- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/lib/debug/variable_inspector.rb b/lib/debug/variable_inspector.rb index f74e170db..b75c7fd05 100644 --- a/lib/debug/variable_inspector.rb +++ b/lib/debug/variable_inspector.rb @@ -42,14 +42,25 @@ def named_members_of(obj) Variable.new(name: iv, value: M_INSTANCE_VARIABLE_GET.bind_call(obj, iv)) end - members.unshift Variable.internal(name: '#class', value: M_CLASS.bind_call(obj)) members.concat(ivars_members) + unless simple_value?(obj) + members.unshift Variable.internal(name: '#class', value: M_CLASS.bind_call(obj)) + end + members end private + SIMPLE_VALUE_TYPES = [NilClass, FalseClass, TrueClass, Symbol, String, Integer, Float, Class, Module, Array, Hash] + + # A "simple" value is one that does not need its `#class` to be shown. + def simple_value?(o) + # Check `#instance_of?` instead of `#is_a?` so objects of subclasses show their `#class` for added clarity. + SIMPLE_VALUE_TYPES.any? { |t| M_INSTANCE_OF.bind_call(o, t) } + end + def value_inspect(obj, short: true) self.class.value_inspect(obj, short: short) end @@ -69,6 +80,7 @@ def self.value_inspect(obj, short: true) # TODO: Replace with Reflection helpers once they are merged # https://github.com/ruby/debug/pull/1002 + M_INSTANCE_OF = method(:instance_of?).unbind M_INSTANCE_VARIABLES = method(:instance_variables).unbind M_INSTANCE_VARIABLE_GET = method(:instance_variable_get).unbind M_CLASS = method(:class).unbind diff --git a/test/debug/variable_inspector_test.rb b/test/debug/variable_inspector_test.rb index 33c833685..f3cf6c8eb 100644 --- a/test/debug/variable_inspector_test.rb +++ b/test/debug/variable_inspector_test.rb @@ -47,7 +47,6 @@ def test_named_members_of_hash ) expected = [ - Variable.internal(name: '#class', value: Hash), Variable.new(name: ':sym', value: "has Symbol key"), Variable.new(name: '"str"', value: "has String key"), Variable.new(name: '1', value: "has Integer key"), @@ -73,7 +72,6 @@ def test_named_members_of_struct def test_named_members_of_string expected = [ - Variable.internal(name: '#class', value: String), Variable.internal(name: '#length', value: 5), Variable.internal(name: '#encoding', value: Encoding::UTF_8), # skip #dump member for short strings @@ -85,7 +83,6 @@ def test_named_members_of_string long_string = "A long string " + ('*' * 1000) expected = [ - Variable.internal(name: '#class', value: String), Variable.internal(name: '#length', value: long_string.length), Variable.internal(name: '#encoding', value: Encoding::UTF_8), Variable.internal(name: '#dump', value: VariableInspector::NaiveString.new(long_string)), @@ -95,10 +92,7 @@ def test_named_members_of_string end def test_named_members_of_class - expected = [ - Variable.internal(name: '#class', value: Class), - Variable.internal(name: '%ancestors', value: PointStruct.ancestors.drop(1)), - ] + expected = [Variable.internal(name: '%ancestors', value: PointStruct.ancestors.drop(1)),] assert_equal expected, @inspector.named_members_of(PointStruct) end @@ -109,10 +103,7 @@ def test_named_members_of_module include *ancestors end - expected = [ - Variable.internal(name: '#class', value: Module), - Variable.internal(name: '%ancestors', value: ancestors), - ] + expected = [Variable.internal(name: '%ancestors', value: ancestors)] assert_equal expected, @inspector.named_members_of(mod) end @@ -196,6 +187,31 @@ def test_named_members_of_other_objects assert_equal expected, @inspector.named_members_of(point) end + def test_hide_class_of_simple_values + assert_not_includes @inspector.named_members_of(nil).map(&:name), "#class" + assert_not_includes @inspector.named_members_of(true), "#class" + assert_not_includes @inspector.named_members_of(false), "#class" + assert_not_includes @inspector.named_members_of(:symbol), "#class" + assert_not_includes @inspector.named_members_of("string"), "#class" + assert_not_includes @inspector.named_members_of(123), "#class" + assert_not_includes @inspector.named_members_of(123.4), "#class" + assert_not_includes @inspector.named_members_of(Class.new), "#class" + assert_not_includes @inspector.named_members_of(Module.new), "#class" + assert_not_includes @inspector.named_members_of(Array.new), "#class" + assert_not_includes @inspector.named_members_of(Hash.new), "#class" + end + + def test_show_class_of_subclasses_of_simple_types + # Skipping these classes, which can't be subclassed meaningfully (or at all): + # NilClass, TrueClass, FalseClass, Symbol, Integer, Float, Class, Module + string_subclass = Class.new(String) + array_subclass = Class.new(Array) + hash_subclass = Class.new(Hash) # E.g. HashWithIndifferentAccess would behave this way + assert_includes @inspector.named_members_of(string_subclass.new), Variable.internal(name: "#class", value: string_subclass) + assert_includes @inspector.named_members_of(array_subclass.new), Variable.internal(name: "#class", value: array_subclass) + assert_includes @inspector.named_members_of(hash_subclass.new), Variable.internal(name: "#class", value: hash_subclass) + end + private class PointStruct < Struct.new(:x, :y, keyword_init: true)