Kernel#singleton_method bug in Ruby.
Few days ago when I was browsing through StackOverflow I came across this question about weird behaviour of
Kernel#singleton_method such as if you call
Kernel#singleton_methods method on
ActiveSupport::Deprecation class you would get you list of singleton methods (no surprise here) but when you try to get that singleton method with
Kernel#singleton_method Ruby would throw
At first, I thought it’s
ActiveSupport doing something fishy. And while I was looking through
AS codebase this answer appeared. @michaelj discovered that it's possible to reproduce this bug without
ActiveSupport at all. All you needed to do is to prepend any module to a singleton class:
So it was something wrong with
I started looking for some bugs with
Module#prepend on Ruby's bugtracker. All I could find related was this bug with
Module#prepend. So what I needed to do is to check how they fixed it and do something similar with
DISCLAIMER: I’m not that good with Ruby internals so next part of the post might have some incorrect statements.
Main thing I learned from
Object#methods fix was
RCLASS_ORIGIN macro. This macro is used to get origin class of the passed class/module. And as I discovered
Module#prepend makes a copy of a target class internally so if you need to access original one you can use that macro. Honestly, I don't really understand why you can't access singleton method but I got the idea.
rb_obj_singleton_method looked before the fix:
as you can see the
klass was retrieved by
rb_singleton_class_get function which returns class's copy if it was already prepended with some module. That means all I had to do is to apply
RCLASS_ORIGIN on that class. And it did the trick. You can find the whole patch in this issue.
And may Ruby be with you.