【问题标题】:Is there a way to safely override Module#=== for a given type?有没有办法安全地覆盖给定类型的 Module#=== ?
【发布时间】:2013-10-12 03:01:07
【问题描述】:

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

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

当然,现在即使SimpleDelegator 包含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_ofObject#kind_of? 没有任何关系)。

有没有办法以“安全”的方式做到这一点,或者我只是在个别案例出现时被猴子修补?

【问题讨论】:

  • 比起猴子补丁Class,我至少会实现SimpleDelegator#class ; String ; end。虽然它似乎仍然不是一个完美的解决方案。
  • 不幸的是,这似乎也不起作用;而s.class 变成StringString === s 仍然是假的。
  • 请见谅;看起来我只检查了类名。但我终于为你找到了奇怪的 hack,请在几分钟内看到答案。

标签: ruby delegates monkeypatching


【解决方案1】:

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

希望对你有帮助。

【讨论】:

  • 这里假设SimpleDelegator的payload是String;它实际上可以是任何东西。虽然这可能会绕过字符串的崩溃,但它并不能真正解决问题。它会生成一个无效的 SQL 查询并以这种方式失败。
  • 无论如何,我已经为AR#find 提供了一个可以正常使用SimpleDelegators 的猴子补丁,所以这不是问题;如果可能的话,我想找到一种方法来做到这一点,而不必修补 each 实例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-22
  • 2010-10-15
  • 2023-03-20
  • 2018-10-05
相关资源
最近更新 更多