I have some code based on this SO answer: https://stackoverflow.com/a/2136117/2158544. Essentially it looks like this (note: in the actual code, I do not control module A):
module A
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def singleton_test
end
end
end
module B
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def self.extended(base)
puts base.method(:singleton_test).owner
define_method(:singleton_test) do |*args, &blk|
super(*args, &blk)
end
end
end
end
First inclusion:
class C
include A
include B
end
A::ClassMethods # <- output
Second inclusion:
class D
include A
include B
end
B::ClassMethods # <- output
Although the call to super still gets correctly routed to module A, I'm confused why singleton_test is already "wrapped" when it gets included into class D (owner is B::ClassMethods). My theory is that it's because when module B redefines singleton_test, it's redefining it on the included module level (module A) and thus every time module A gets included subsequently, the method has already been "wrapped".
When you call a method ruby walks up the ancestral chain looking for the method, if it reaches the top (BasicObject) and cannot find a matching method it will throw an error (unless
method_missingis defined)How this works in your example
First Inclusion
When you lookup
singleton_testit checks the ancestral chainsingleton_testNow in the
B::ClassMethods::extendedhook you are definingsingleton_testusingdefine_method. Since you called this method without a receiver the implicit receiver isselfandselfin the context of this method isB::ClassMethodsso in essence you are callingYou can see this more clearly as
Output:
Second Inclusion
I think you can see where this is going
When you lookup
singleton_testit checks the ancestral chainsingleton_testSo it is not that the method is "already wrapped" or that the method is being redefined on
Ait is that you have defined a new method inB::ClassMethodsand sinceBis included afterAit's definition takes priority (overrides that ofAin this context).