我可以通过将声明放在私人部分中来使attr_reader(以及相关的attr_writer和attr_accessor)方法是私有的:
class Foo private attr_reader :b end Foo.new.b # => NoMethodError: private method `b' called for #<Foo:>
但是,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"
将代理私有化可以通过将其更改为:
private def_delegator :foo,:b
但是我预计上面的Bar.new.b会出现一个NoMethodError错误.为什么代表团不是私人的?
def_delegator(def_instance_delegator的别名)的方法定义只是拯救(已移除块):
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
这意味着module_eval不尊重它在私有部分中被调用.为什么?
解决方法
是的,问题是使用module_eval,因为它在评估传递的字符串之前显式设置公共可见性.
它在CRuby和JRuby中的行为方式相同.例如,CRuby的被证明的代码是 eval_under的功能.
它在CRuby和JRuby中的行为方式相同.例如,CRuby的被证明的代码是 eval_under的功能.
如你所知,当您将def_delegate传递给私有方法时,它将变为私有的. def_delegate首先将传递的方法定义为public(由底层的module_eval),然后通过private重置为私有可见性.
如果当前Module.module_eval的行为是正确的或者在Forwardable.def_instance_delegator中存在错误,那么这不是100%清楚.在文档指南中使用module_eval示例将其用于相关类/模块之外,并且不期望可见性参数,因此将方法的可见性设置为public似乎是合乎逻辑的.
解决方案将是Module.module_eval处理可选可见性参数,并在发送到隐式或显式自身时尊重当前可见性(如果可能的话怀疑)或修复Forwardable.def_instance_delegator实现来定义具有更合适的Module.define_method而不是module_eval的方法.
无论如何,这是填写http://bugs.ruby-lang.org错误报告的好候选人.