I have a class Klass
, and its constructor accepts an argument. We should be able to call methods on this object that are not defined in Klass
.
We can chain multiple methods, but in the end, we have to use Klass#result
to get the result like:
Klass.new(5).pred.pred.result
and the output here should be 3
. I tried using method_missing
in Klass
and using send
on the object's class, but that would have worked without the result
method that I have to use. Can someone explain how this can be done with delegation?
You could do something like this:
class Klass
def initialize(number)
@number = number
end
def result
@number
end
def method_missing(method_name, *arguments, &block)
if @number.respond_to?(method_name)
@number = @number.method(method_name).call(*arguments, &block)
return self
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
# be sure to implement this...
end
end
puts Klass.new(5).pred.pred.result # => 3
But it's problematic. In this particular example, since #pred returns a new object (it doesn't modify the object it was called on), we have to reassign the instance variable to the result. It works for pred and other methods that return new Integers, but some methods on Integer don't return an Integer (e.g. Integer#even). In this case you'd get this sort of behavior:
puts Klass.new(4).even?.result # => true
Depending on your particular situation, that might be what you're after. Or, it might be that in your situation all methods the object being delegated to mutate that object, rather than return new instances of the object, in which case the reassignment isn't needed.
I don't think you can use Ruby's existing Delegator and SimpleDelegator constructs, because the only way you can chain the final #result call onto the end is if every delegated call returns the instance of Klass. Using those existing constructs would cause delegated calls to return their normal return values, and the chaining would then be on whatever objects those return values return. For example, using the above code, you'd see this behavior:
puts Klass.new(5).pred.pred.class # => "Klass"
Using SimpleDelegator, you'd see this behavior
require 'delegate'
class Klass2 < SimpleDelegator
# Klass2 methods...
end
puts Klass2.new(5).pred.pred.class # => "Fixnum"
Hope that helps.