Ruby 是否有一种方法可以安全地覆盖给定类型的模块#==?
在Rails中(至少3.2版;我没有4个版本可以在那里尝试),Ruby 是否有一种方法可以安全地覆盖给定类型的模块#==?,ruby,delegates,monkeypatching,Ruby,Delegates,Monkeypatching,在Rails中(至少3.2版;我没有4个版本可以在那里尝试),ActiveRecord::Base#find如果给定了SimpleDelegator,即使它所委托的对象可以正常工作,也会阻塞 原因是AR::Base#find在创建SQL语句时将值传递到AR::ConnectionAdapters::Quoting#quote,并且由于它不知道如何处理SimpleDelegator,因此它尝试将其传递到YAML.dump,这会引发异常AR确定如何通过类的case语句引用(即String==valu
ActiveRecord::Base#find
如果给定了SimpleDelegator
,即使它所委托的对象可以正常工作,也会阻塞
原因是AR::Base#find
在创建SQL语句时将值传递到AR::ConnectionAdapters::Quoting#quote
,并且由于它不知道如何处理SimpleDelegator
,因此它尝试将其传递到YAML.dump
,这会引发异常AR
确定如何通过类的case
语句引用(即String==value
等)
当然,现在,即使SimpleDelegator
包含字符串
,其类也是SimpleDelegator
,因此上述检查将失败。但是,SimpleDelegator
有一个\uuuu getobj\uuuu
方法,该方法提供对委托给以下对象的实际对象的访问:
> s = SimpleDelegator.new("test")
#=> "test"
> String === s
#=> false
> String === s.__getobj__
#=> true
为了解决这个问题,我可以覆盖类#=
以将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
)
是MRI中的一种原生C方法,它使用了一种称为的方法rb\u obj\u is\u kind\u of。我曾希望重写SimpleDelegator#kind#of?
可以让我以一种更安全的方式做我想做的事情,但它似乎没有任何影响(我猜rb#obj#u是#kind#u of
与Object#kind#of?
)
有没有办法以“安全”的方式做到这一点,或者我只是在个别案例出现时被困在猴子身上打补丁?看起来你可能会四处闯荡,欺骗
AR
。它试图YAML.dump
?好吧,我们会帮助它:
class A < SimpleDelegate
def initialize *args
@s = 'voilá'
end
# required by YAML (Psych)
def encode_with coder
coder.tag = nil
coder.represent_scalar(nil, @s)
end
end
require 'yaml'
puts YAML.dump(A.new)
# ⇒ --- voilá
希望它能有所帮助。与monkeypatching
Class
相比,我至少要实现SimpleDelegator\Class;一串结束
。尽管这似乎仍然不是一个完美的解决方案。不幸的是,这似乎也不起作用;虽然s.class
变为String
,String==s
仍然为false。请原谅;看起来我只检查了类名。但是我终于为您找到了一个奇怪的黑客,请在几分钟内看到答案。这假设SimpleDelegator
的负载是一个字符串
;事实上,它可以是任何东西。虽然这可以避免字符串崩溃,但并不能真正解决问题。它将生成一个无效的SQL查询,并以这种方式失败。无论如何,我已经为AR#find
提供了一个工作猴补丁,以便正确使用SimpleDelegator
,所以这不是问题;如果可能的话,我想找到一种方法来做到这一点,而不必对出现的每个实例进行猴子补丁。
module DelegateYamler
def encode_with coder
coder.tag = nil
coder.represent_scalar(nil, __getobj__)
end
end