gpt4 book ai didi

ruby - 有没有办法安全地覆盖给定类型的 Module#=== ?

转载 作者:数据小太阳 更新时间:2023-10-29 08:12:36 25 4
gpt4 key购买 nike

在 Rails 中(至少是 3.2 版;我没有 4 可以在那里尝试),ActiveRecord::Base#find 如果给定一个 SimpleDelegator 就会卡住,即使它委托(delegate)的对象可以正常工作。

原因是 AR::Base#find 在创建 SQL 语句时将值传递给 AR::ConnectionAdapters::Quoting#quote,因为它不知道如何处理 SimpleDelegator,它会尝试将其传递给 YAML.dump,这会引发异常。 AR 确定如何通过类的 case 语句进行引用(即 String === value 等)。

现在,当然,即使 SimpleDelegator 包含一个 String,它的类是 SimpleDelegator,所以上面的检查也会失败。但是,SimpleDelegator 有一个 __getobj__ 方法,它提供对被委托(delegate)给的实际对象的访问:

> 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? 有任何关系)。

有什么方法可以“安全”地做到这一点,还是我只是在个别案例出现时被猴子修补?

最佳答案

看起来你可能会四处乱窜并欺骗 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á

现在 AR 应该可以转储它了。 encode_with 方法可能会被放入 CheatModule 中,以便在您需要欺骗 AR 的任何地方包含它:

module DelegateYamler
def encode_with coder
coder.tag = nil
coder.represent_scalar(nil, __getobj__)
end
end

希望对您有所帮助。

关于ruby - 有没有办法安全地覆盖给定类型的 Module#=== ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19192639/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com