Project

General

Profile

Actions

Bug #19079

closed

Modules included in a DelegateClass cannot override delegate methods

Added by jonathanhefner (Jonathan Hefner) 3 months ago. Updated about 2 months ago.

Status:
Rejected
Priority:
Normal
Assignee:
-
Target version:
-
ruby -v:
ruby 3.1.2p20
[ruby-core:110483]

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.


Related issues 1 (0 open1 closed)

Related to Ruby master - Bug #19047: DelegateClass displays "method redefined" warning when overriding methodsClosedActions

Updated by jonathanhefner (Jonathan Hefner) 3 months ago

I have submitted a PR with the proposed solution: https://github.com/ruby/delegate/pull/14.

Updated by matz (Yukihiro Matsumoto) about 2 months ago

  • 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.

Actions #3

Updated by matz (Yukihiro Matsumoto) about 2 months ago

  • Related to Bug #19047: DelegateClass displays "method redefined" warning when overriding methods added

Updated by matz (Yukihiro Matsumoto) about 2 months ago

If you want to include a module to a delegated class, try the following:

class WithHelper<DelegateClass(Base)
  include Helper
end

Matz.

Updated by Hanmac (Hans Mackowiak) about 2 months ago

you can also try prepend instead of include:

WithHelper = DelegateClass(Base) { prepend Helper }

Updated by jonathanhefner (Jonathan Hefner) about 2 months ago

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.

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0