为什么在私有部分中声明委托方法是公开的?

spi*_*ann 15 ruby delegates metaprogramming ruby-on-rails

我可以通过将声明放在一个部分中来使attr_reader(和相关的attr_writerattr_accessor)方法私有化private:

class Foo
private
  attr_reader :b
end

Foo.new.b # => NoMethodError: private method `b' called for #<Foo:>
Run Code Online (Sandbox Code Playgroud)

但是,Rails' delegate和Ruby标准库def_delegate不能以这种方式工作.这些委托方法始终是公开的.

class Foo
  attr_reader :b
  def initialize
    @b = 'b'
  end
end

require 'forwardable'
class Bar
  attr_reader :foo
  def initialize
    @foo = Foo.new
  end
  extend Forwardable
private
  def_delegator :foo, :b
end

Bar.new.b # => "b"
Run Code Online (Sandbox Code Playgroud)

通过将委托更改为以下内容,可以轻松完成委托授权:

private def_delegator :foo, :b
Run Code Online (Sandbox Code Playgroud)

但我预计会NoMethodError出现Bar.new.b上述错误.为什么代表团不私有?

def_delegator(别名for def_instance_delegator)的方法定义就是rescue(删除了块):

def def_instance_delegator(accessor, method, ali = method)
  line_no = __LINE__; str = %Q{
    def #{ali}(*args, &block)
      #{accessor}.__send__(:#{method}, *args, &block)
    end
  }
  module_eval(str, __FILE__, line_no)
end
Run Code Online (Sandbox Code Playgroud)

这意味着module_eval不尊重它在一个private部分中被调用.为什么?

Dav*_*ric 5

是的,问题在于,module_eval因为它在评估传递的字符串之前显式设置公共可见性.它在CRuby和JRuby中的行为方式相同.例如,CRuby的有罪代码在eval_under函数中.

正如你所知,当你传递def_delegateprivate方法时,它变得私密.def_delegate首先将传递的方法定义为public(通过底层的module_eval),然后通过私有可见性重置private.

如果当前Module.module_eval的行为是正确的或者存在错误,那么它不是100%清楚的Forwardable.def_instance_delegator.module_eval文档指南中的示例在相关的类/模块之外使用它并且它不期望可见性参数,因此它似乎合乎逻辑地将方法的可见性设置为公共.

解决方案将是Module.module_eval处理可选的可见性参数,在发送到隐式或显式self(如果可能的话,怀疑)时尊重当前可见性,或修复Forwardable.def_instance_delegator实现以更合适地定义方法Module.define_method而不是module_eval.无论如何,这是填写http://bugs.ruby-lang.org上的错误报告的好选择.