Some background: I have an external library that uses explicit type checking instead of duck-typing in one of its methods. Something like:
def a_method(value)
case value
when Array then 'an Array'
when Hash then 'a Hash'
when Foo then 'a Foo'
end
end
Foo is defined in the library. I would like to pass another object to this method that should be treated like a Foo. Therefore, I'm subclassing Foo:
class Bar < Foo
end
which works just fine:
bar = Bar.new
a_method(bar)
#=> 'a Foo'
Unfortunately, Foo implements several methods that will break the way Bar is supposed to work, including method_missing and respond_to?. For example:
class Foo
def respond_to?(method_name)
false
end
end
Because Foo is Bar's superclass, Foo#respond_to? is invoked when calling Bar#respond_to?:
class Bar < Foo
def hello
end
end
bar = Bar.new
bar.respond_to?(:hello) #=> false
bar.method(:respond_to?) #=> #<Method: Bar(Foo)#respond_to?>
I would like to remove or bypass Foo's method in this case (i.e. from within Bar) so that:
bar.respond_to?(:hello) #=> true
bar.method(:respond_to?) #=> #<Method: Bar(Kernel)#respond_to?>
just as if Foo#respond_to? did not exist.
Any suggestions?
Ruby's method lookup seems to be "hard-coded". I couldn't find a way to alter it from within Ruby.
Based on Sergio Tulentsev's suggestion to re-implement the methods, however, I came up with a helper method to replace / overwrite every superclass (instance) method with its "super" method (or undefine it if there is none):
def self.revert_superclass_methods
superclass.instance_methods(false).each do |method|
super_method = instance_method(method).super_method
if super_method
puts "reverting #{self}##{method} to #{super_method}"
define_method(method, super_method)
else
puts "undefining #{self}##{method}"
undef_method(method)
end
end
end
This has basically the same effect for my purposes. Example usage:
module M
def foo ; 'M#foo' ; end
end
class A
include M
def to_s ; 'A#to_s' ; end
def foo ; 'A#foo' ; end
def bar ; 'A#bar' ; end
end
class B < A
def self.revert_superclass_methods
# ...
end
end
b = B.new
b.to_s #=> "A#to_s"
b.foo #=> "A#foo"
b.bar #=> "A#bar"
B.revert_superclass_methods
# reverting B#to_s to #<UnboundMethod: Object(Kernel)#to_s>
# reverting B#foo to #<UnboundMethod: Object(M)#foo>
# undefining B#bar
b.to_s #=> "#<B:0x007fb389987490>"
b.foo #=> "M#foo"
b.bar #=> undefined method `bar' for #<B:0x007fb389987490> (NoMethodError)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With