method_missing覆盖未起作用

4
我编写了一个方便的ActiveRecord扩展,将方法委托给基本对象(基于多表继承)。
class ActiveRecord::Base
  def self.acts_as(base)
    class_eval %Q{
      def method_missing(method, *args, &blk)
        #{base}.send(method, *args, &blk)
      rescue NoMethodError
        super
      end
    }
  end
end

我有一个状态类和一个基本

# 状态类
class MyState < ActiveRecord::Base
  belongs_to :my_object
  acts_as :my_object
end
# 基本类 class MyObject < ActiveRecord::Base has_one :head, :class_name => 'MyState' has_one :tail, :class_name => 'MyState'

当我尝试使用它时,我发现它在某些情况下不起作用。 更具体地说,

> MyState.first.some_method_in_base
空值
> MyObject.first.tail.some_method_in_base
NoMethodError: #<ActiveRecord::Associations::HasOneAssociation:0xABCDEFG>没有定义方法`some_method_in_base'

有人能解释一下为什么一个可以工作,而另一个不行吗?


我建议您使用 ActiveSupport 提供的版本,它叫做 #delegate:http://api.rubyonrails.org/classes/Module.html#M000051 - François Beausoleil
是的,我最终使用了委托,但它们并不像将所有缺失方法转发到基类的概念那样方便。 - Yitao
1个回答

5
当你运行MyObject.first.tail时,实际响应的对象是一个AssociationProxy类,该类已删除大多数基本实例方法,并通过method_missing将未知方法委托给@target。您可以通过运行以下命令获取有关代理的更多详细信息:
MyObject.first.proxy_owner
MyObject.first.proxy_reflection
MyObject.first.proxy_target

如果你查看代码,你会发现AssociationProxy代理了一些方法到你的MyState类中,只有当MyState响应some_method_in_base时才会这样做,如下所示。

  private
    # Forwards any missing method call to the \target.
    def method_missing(method, *args, &block)
      if load_target
        if @target.respond_to?(method)
          @target.send(method, *args, &block)
        else
          super
        end
      end
    end

因此,您在目标类中定义的method_missing从未被调用。

我之前在 AssociationProxy 中看到了 method_missing 定义,但我就是想不明白它是干嘛用的。现在终于明白了,谢谢! - Yitao
不客气!欢迎加入StackOverflow社区!如果答案正确并对您有帮助,您应该点击答案左侧的复选框轮廓将其标记为接受的答案。如果您愿意,还可以投票支持它。更多信息请参见http://stackoverflow.com/faq中的“如何在此处提问?”部分。 - Vlad Zloteanu
好的,复选框确实不太显眼。感谢你指出来。 - Yitao

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接