为什么Sunspot会改变搜索DSL块中的`self`?

时间:2010-09-18 00:13:31

标签: ruby-on-rails ruby sunspot

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

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的{​​{1}} 宾语;我得到Foo对象的id

我认为Sunpot正在Sunspot::DSL::Search方法中进行一些绑定交换/委托魔术。

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

修改

可以在此link

找到太阳黑子搜索方法

以下代码将说明我的观点。在方法Util.instance_eval_or_call中,我有一个行为符合预期的块。在方法foo中,块不起作用。

bar

注2

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

注3

具体来说,我在块中调用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 方法时发现了一个问题。 id方法将块内的方法调用委托给DSL对象,如果找不到方法,则将调用重新委托给调用上下文。在注册委托代码之前,搜索方法从DSL对象中删除除基本方法之外的所有方法。 search方法未被删除。这导致了这个问题。对于所有其他方法,委托工作正常。

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

2 个答案:

答案 0 :(得分:5)

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

魔法可以在util.rb中找到ContextBoundDelegate

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

<强>理由:

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

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

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

以下代码可能有用,因为它遵循大致相同的想法,但更简单,更复杂:Using methods from two different scopes?

答案 1 :(得分:1)

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

我假设instance_eval(自我更改)用于向块提供keywords和其他相关方法。