From: usa@... Date: 2016-04-22T05:23:01+00:00 Subject: [ruby-core:75085] [Ruby trunk Bug#11916] Fix delegating to 'args' and 'block' Issue #11916 has been updated by Usaku NAKAMURA. Backport changed from 2.0.0: WONTFIX, 2.1: WONTFIX, 2.2: REQUIRED, 2.3: REQUIRED to 2.0.0: WONTFIX, 2.1: WONTFIX, 2.2: DONE, 2.3: REQUIRED ruby_2_2 r54673 merged revision(s) 53381,53382,53511,53512. ---------------------------------------- Bug #11916: Fix delegating to 'args' and 'block' https://bugs.ruby-lang.org/issues/11916#change-58208 * Author: Elliot Winkler * Status: Closed * Priority: Normal * Assignee: * ruby -v: 2.4.0dev * Backport: 2.0.0: WONTFIX, 2.1: WONTFIX, 2.2: DONE, 2.3: REQUIRED ---------------------------------------- If you have a class that uses Forwardable to delegate a method to another object, and the method that returns the delegate object is called `args` or `block`, then Forwardable will fail to work. Here's a simple example: class ModelCreator extend Forwardable attr_reader :args def_delegator :args, :model_name def initialize(args) @args = args end end ModelCreator.new.model_name If you run the last line above, then you'll get: NoMethodError: undefined method `model_name' for []:Array This error occurs because `def_delegator` -- as it is written in Ruby -- uses metaprogramming to add methods to the class that will then delegate to the delegate object. So it's as if we had written: class ModelCreator extend Forwardable attr_reader :args def model_name(*args, &block) args.model_name(*args, &block) end def initialize(args) @args = args end end As you can see, `def_delegator` will not only forward the method call onto the delegate object, it will also forward any arguments provided as well. It is here that the bug arises: it splats all of the arguments into a variable which is called `args`, and because of how variable scope works in Ruby, it then attempts to call `model_name` on *this* variable and *not* our delegate object method. The fix is to call the delegate object method manually using `__send__`. (This assumes, of course, that the given receiver is, in fact, the name of a method and not the name of an instance variable, which is also a possibility.) We use `__send__` because the delegate object method could be private. So, that looks like this: def model_name(*args, &block) __send__(:args).model_name(*args, &block) end Because `def_delegators` and `delegate` use `def_delegator` internally, they also get this fix as well. ---Files-------------------------------- fix-def-delegator.patch (6.99 KB) -- https://bugs.ruby-lang.org/ Unsubscribe: