diff --git a/lib/pp.rb b/lib/pp.rb index 798a4f4..32f4c9f 100644 --- a/lib/pp.rb +++ b/lib/pp.rb @@ -200,18 +200,38 @@ def pop_inspect_key(id) def pp(obj) # If obj is a Delegator then use the object being delegated to for cycle # detection - obj = obj.__getobj__ if defined?(::Delegator) and ::Delegator === obj + delegated = defined?(::Delegator) && ::Delegator === obj + key = delegated ? obj.__getobj__ : obj - if check_inspect_key(obj) + if check_inspect_key(key) + obj = key if delegated and !obj.respond_to?(:pretty_print_cycle) group {obj.pretty_print_cycle self} return end - guard_inspect(obj) do + guard_inspect(key) do + custom_inspect = false + if delegated + delegator_class = obj.class + unless delegator_class.public_method_defined?(:pretty_print) + singleton_methods = obj.singleton_methods(false) + custom_inspect = + !singleton_methods.include?(:pretty_print) && + (delegator_class.public_method_defined?(:inspect) || + singleton_methods.include?(:inspect)) + end + end + obj = key if delegated && !custom_inspect && !obj.respond_to?(:pretty_print) group do - obj.pretty_print self - rescue NoMethodError - text Kernel.instance_method(:inspect).bind_call(obj) + if custom_inspect + text obj.inspect + else + begin + obj.pretty_print self + rescue NoMethodError + text Kernel.instance_method(:inspect).bind_call(obj) + end + end end end end diff --git a/test/test_pp.rb b/test/test_pp.rb index 922ed37..56c32a1 100644 --- a/test/test_pp.rb +++ b/test/test_pp.rb @@ -312,6 +312,24 @@ def test_direct_pp class PPDelegateTest < Test::Unit::TestCase class A < DelegateClass(Array); end + class Delegated < SimpleDelegator + def pretty_print(q) + q.text "" + end + end + + class Inspected < SimpleDelegator + def inspect + "" + end + end + + class RaisingInspect < SimpleDelegator + def inspect + self.missing_from_inspect + end + end + def test_delegate assert_equal("[]\n", A.new([]).pretty_inspect, "[ruby-core:25804]") end @@ -327,6 +345,35 @@ def test_delegate_cycle assert_equal(cycle_pretty_inspect, delegator_cycle_pretty_inspect) end + + def test_delegate_pretty_print + delegated = Delegated.new(HasPrettyPrint.new(nil)) + assert_equal("\n", PP.pp(delegated, ''.dup)) + end + + def test_delegate_inspect + delegated = Inspected.new(HasInspect.new(:target)) + assert_equal("\n", PP.pp(delegated, ''.dup)) + end + + def test_delegate_singleton_inspect + delegated = SimpleDelegator.new(HasInspect.new(:target)) + def delegated.inspect() "" end + + assert_equal("\n", PP.pp(delegated, ''.dup)) + end + + def test_delegate_inspect_error + error = assert_raise(NoMethodError) do + PP.pp(RaisingInspect.new(HasInspect.new(:target)), ''.dup) + end + assert_equal(:missing_from_inspect, error.name) + end + + def test_delegate_basic_object + delegated = SimpleDelegator.new(BasicObject.new) + assert_match(/\A#\n\z/, PP.pp(delegated, ''.dup)) + end end class PPFileStatTest < Test::Unit::TestCase