在 Rails 中(至少 3.2 版;我没有 4 可以在那里尝试),如果给定 a ,即使它委托的对象可以正常工作,也会ActiveRecord::Base#find窒息。SimpleDelegator
这样做的原因是在创建 SQL 语句时AR::Base#find将值传递给AR::ConnectionAdapters::Quoting#quote,并且由于它不知道如何处理SimpleDelegator,它试图将其传递给YAML.dump,这会引发异常。AR确定如何通过类的声明来引用case(即String === value,等)。
现在,当然,即使 aSimpleDelegator包含 a String,它的类也是SimpleDelegator,所以上面的检查会失败。但是,SimpleDelegator有一个__getobj__方法可以访问被委托给的实际对象:
> s = SimpleDelegator.new("test")
#=> "test"
> String === s
#=> false
> String === s.__getobj__
#=> true
为了解决这个问题,我可以重写Class#===以SimpleDelegator考虑:
class Class
def ===(other)
return super(other.__getobj__) if other.is_a?(SimpleDelegator)
super
end
end
> String === s
#=> true
但是,这显然看起来不是一种安全的方法(我不知道这是否会对任何事情产生负面影响,但至少SimpleDelegator会破坏类平等)。另一方面,这使得处理其他代码实例变得更容易,比如AR::ConnectionAdapters::Quoting#quote我还不知道的代码(例如,与专门的猴子补丁相反quote)SimpleDelegator。
Module#===是 MRI 中的原生 C 方法,并使用了一种名为rb_obj_is_kind_of. 我曾希望覆盖SimpleDelegator#kind_of?可以让我以更安全的方式在这里做我想做的事情,但它似乎没有任何影响(我想rb_obj_is_kind_of与 没有任何关系Object#kind_of?)。
有没有办法以“安全”的方式做到这一点,或者我只是在个别案例出现时被猴子修补?