Context: I am trying to put up a Decorator pattern in Ruby. As a Decorator should delegate all unknown methods to the underlying objects, I used the Delegator class.
I could have used SimpleDelegator but I wanted to fully understand what I was doing.
So the basic code I came out with was :
class Decorator < Delegator
def initialize(component)
super
@component = component
end
def __setobj__(o); @component = o end
def __getobj__; @component end
def send(s, *a); __send__(s, *a) end
end
Which is ~exactly the same as the implementation of SimpleDelegator. Seems good.
But the thing I did not want was for the code handling the Decorator to know it is manipulating a Decorator. I want full transparency.
At this moment Decorator.new(Object.new).class returned Decorator
So I tinkered a bit and came up with this :
class Decorator < Delegator
undef_method :==
undef_method :class
undef_method :instance_of?
# Stores the decorated object
def initialize(component)
super
@component = component
end
def __setobj__(o); @component = o end
def __getobj__; @component end
def send(s, *a); __send__(s, *a) end
end
This way, I can safely use class or instance_of? on my Decorated object, it will send the method to the underlying object via method_missing (which is implemented by Delegator).
The thing is : I don’t understand why I had to undef :class and :instance_of?. I can see that BasicObject defines :== so I had to undefine it but what about those two ?
I looked at the BasicObject documentation and a bit in the C code but did not find anything. I looked the same at the Delegator documentation and code, and did not find anything either.
It seems Delegator include the Kernel module, but Kernel#class or Kernel#instance_of? don’t exist.
Where those two method came from ? Why did I need to undefine them if they were not implemented at all ?
I guess I must be missing something about Ruby’s object model or something.
Thanks.
You can get a hint by inspecting the method:
The method’s owner is
Decoratorbut actually defined in#<Module:0x00000102137498>. So there is an anonymous module that defines it. Interesting… let’s look at:There’s that module again, between
DelegatorandBasicObject. SoDelegatordoesn’t directly derive fromBasicObject. If you look at the source code inlib/delegate.rbyou find:So a copy of the
Kernelmodule is made, which doesn’t haveto_s,inspect, etc… but still hasclassandinstance_of?. It’s included inDelegatorand that’s where they come from.Note that
Objectinherits the same methods by including theKernelmodule (but it includes the full module, of course):This is stated in the
Objectdoc: