Bug #19079
closed
Modules included in a DelegateClass cannot override delegate methods
Added by jonathanhefner (Jonathan Hefner) almost 2 years ago.
Updated over 1 year ago.
Description
Because DelegateClass
defines delegate methods on the class itself, those delegate methods come first in the method lookup chain. This prevents included modules from overriding delegate methods:
Base = Class.new do
def foo
"base"
end
end
Helper = Module.new do
def foo
"helper"
end
end
WithHelper = DelegateClass(Base) { include Helper }
WithHelper.new(Base.new).foo
# => "base"
One possible solution would be to define the delegate methods in a separate module. That way, other modules could come before it in the method lookup chain.
- Status changed from Open to Rejected
The DelegateClass
defines an anonymous class and defines forwarding methods to the class. The reported (so-called) issue is a natural consequence of the above behavior. include
add methods defined in a module above the current class, so forwarding methods have higher precedence. If you (re)define a method, it overwrites the forwarding method.
My opinion is that your assumption is wrong, so we don't need to fix. If you think we need to implement your assumption, you need to persuade us with the real world use-case.
Matz.
- Related to Bug #19047: DelegateClass displays "method redefined" warning when overriding methods added
If you want to include a module to a delegated class, try the following:
class WithHelper<DelegateClass(Base)
include Helper
end
Matz.
you can also try prepend instead of include:
WithHelper = DelegateClass(Base) { prepend Helper }
This issue occurred for a private module in Rails: https://github.com/rails/rails/pull/46189#discussion_r991440668.
Using include
in a subclass works. Using prepend
also works, and is the workaround I used for the Rails module.
However, my proposed solution for this issue (https://github.com/ruby/delegate/pull/14) also solves #19079 with a performance improvement.
I opened this issue and #19079 because the current behavior seemed surprising to me. In particular, I expected the DelegateClass
block to behave just like a Class.new
block. I feel like that is a reasonable assumption based on the documentation.
But, if my assumption is wrong, then I understand the decision.
Also available in: Atom
PDF
Like0
Like0Like0Like0Like0Like0Like0