debug icon indicating copy to clipboard operation
debug copied to clipboard

Loading `debug` gem breaks `blankslate` gem

Open jvilk-stripe opened this issue 3 years ago • 5 comments

Your environment

  • ruby -v: ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux]
  • rdbg -v: rdbg 1.6.2

Describe the bug

The debug gem undefines and redefines singleton_method_added.

The blankslate gem provides a "abstract base class with no predefined methods" except for some core Ruby builtins. "BlankSlate is useful as a base class when writing classes that depend upon method_missing (e.g. dynamic proxies)", so it only works if singleton_method_added is available. However, it hides singleton_method_added because it's redefined by the debug gem.

This problem does not occur when the debug gem is not required.

To Reproduce

require 'debug'
require 'blankslate'

BlankSlate.new.singleton_method_added

Buggy output -- the method isn't defined:

Traceback (most recent call last):
repro.rb:4:in `<main>': undefined method `singleton_method_added' for #<BlankSlate:0x000055a654b12fb0> (NoMethodError)

Expected behavior

The method is defined, but private:

Traceback (most recent call last):
repro.rb:3:in `<main>': private method `singleton_method_added' called for #<BlankSlate:0x000055d758dcd540> (NoMethodError)

Workaround

We can work around the bug by explicilty unhiding this method, or by requiring blankslate before debug:

BlankSlate.reveal(:singleton_method_added)

Additional context

The problem is caused by this code in the debug gem:

https://github.com/ruby/debug/blob/19b4dde3308f532943e4234d1588d4fa26c52345/lib/debug/session.rb#L1686-L1689

Why does the debug gem redefine this method?

jvilk-stripe avatar Sep 21 '22 18:09 jvilk-stripe

This problem breaks one of Stripe's services in development because we start a debug session when it starts up, before it requires the blankslate gem. We are working around the issue, but it would be great if it could be fixed!

jvilk-stripe avatar Sep 21 '22 18:09 jvilk-stripe

Thank you for the report. I didn't know the blankslate but could you make a smaller repro-code without the blankslate gem? Otherwise I'll try to make it.

ko1 avatar Oct 04 '22 07:10 ko1

minitest also breaks, see https://github.com/minitest/minitest/issues/929

dentarg avatar Apr 04 '23 07:04 dentarg

Here is blankslate torn all the way down to a minimal repro:

#!/usr/bin/env ruby -vw

class BlankSlate
  def self.hide(name)
    return unless instance_methods.include? name
    undef_method name
  end

  hide :singleton_method_added
end

begin
  BlankSlate.new.singleton_method_added
rescue NoMethodError => e
  p e.message.include?("private method") ? :GOOD : :BAD
else
  p :BAD
end

__END__
% ruby -rdebug debug_blankslate.rb
:BAD
% ruby debug_blankslate.rb
:GOOD

run w/o ruby -rdebug to output GOOD and run with ruby -rdebug to output BAD...

It essentially boils down to "is singleton_method_added defined?"

for minitest/mock, not too dissimilar:

#!/usr/bin/env ruby -vw

class Mock
  overridden_methods = %w[
    ===
    class
    inspect
    instance_eval
    instance_variables
    object_id
    public_send
    respond_to_missing?
    send
    to_s
  ]

  instance_methods.each do |m|
    undef_method m unless overridden_methods.include?(m.to_s) || m =~ /^__/
  end

  def method_missing sym, *args, **kwargs, &block # :nodoc:
    raise NoMethodError, "unmocked method %p" % [sym]
  end
end

foo = Mock.new
def foo.bar = 'very awesome foo'

p :GOOD

zenspider avatar Nov 10 '23 21:11 zenspider

I think in my case I'm going to add singleton_method_added to overridden_methods if DEBUGGER__ is defined.

zenspider avatar Nov 10 '23 22:11 zenspider