2

我注意到(并在太阳黑子代码中验证)以下行为

class Foo < ActiveRecord::Base
  def  bar
    search_str = "foo"
    Boo.search do
      keywords(search_str)
      p self.id
      p self
   end
 end
end

在上面的代码中,DSL 块可以访问上下文中定义的变量。但是self内部块指向类的实例 Sunspot::DSL::Search(而不是类的实例 Foo)。当我尝试访问self.id时,而不是获取 对象id的;Foo我得到id一个Sunspot::DSL::Search对象。

我认为 Sunpot 在 Util.instance_eval_or_call方法上做了一些绑定交换/委托魔术。

我很好奇为什么 Sunspot 会这样做,以及为什么文档中没有关于这种行为的警告。

编辑:

太阳黑子搜索方法可以在这个链接找到

下面的代码将说明我的观点。在该方法foo中,我有一个行为符合预期的块。在 methodbar中,块没有行为。

class Order < ActiveRecord::Base  

  def foo
    p self.class.name # prints Order

    # The `self` inside the block passed to the each method
    # points to an object of type Order (as expected)
    # This is the normal block behavior.
    [1,2,3].each do |val|
      p self.class.name # prints Order
    end
  end


  def bar

    p self.class.name # prints Order

    # the `self` inside the block passed to the search method
    # points to an object of type Sunspot::DSL::Search.
    # This is NOT the normal block behavior.

    Order.search do
      keywords("hello")
      p self.class.name # prints Sunspot::DSL::Search
    end
end

笔记2

我在 Sunspot 源代码树中找到了修改正常块行为的代码。我的问题是关于像这样操纵绑定的原因。

注3

具体来说,我在调用id块内部的方法时发现了一个问题。该search方法将块内的方法调用委托给 DSL 对象,如果找不到该方法,则将调用重新委托给调用上下文。在注册委托代码之前,搜索方法会从 DSL 对象中去除除基本方法之外的所有方法。该id方法没有被剥离。这导致了问题。对于所有其他方法,委派工作正常。

Sunspot 方法文档中未记录此行为。

4

2 回答 2

5

好的,我知道它是如何工作的:

魔法ContextBoundDelegate在 util.rb中找到。

  • 它创建一个空白的 slate 委托对象。
  • 委托人将所有方法调用转发给“接收者”。在您的示例中,“接收者”可能是包含方法keywordswith的对象any_of
  • 如果在'receiver'中没有找到给定的方法,那么它将方法调用转发到'context'对象
  • 上下文对象是保存块绑定的对象。
  • 您可以通过执行以下操作找到给定块的上下文对象:eval('self', block.binding)

理由:

所以所有这一切的效果是,块不仅可以访问搜索对象中的方法(a la instance_eval),而且还可以访问块调用范围内的本地方法。

当然,该块也可以访问该块的调用范围内的局部变量,但这只是正常的闭包行为。

但是,该块不能访问该块的调用范围内的实例变量。

以下代码可能有用,因为它遵循大致相同的想法,但更简单且不太复杂:使用来自两个不同范围的方法?

于 2010-09-18T07:16:46.183 回答
1

不就是一个instance_eval吗?除非您正在谈论从调用上下文访问实例变量,否则这是正常的关闭行为。

我假设instance_eval(自我的变化)用于为keywords块提供和其他相关方法。

于 2010-09-18T05:02:27.067 回答