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

Har*_*tty 2 ruby ruby-on-rails sunspot

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

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

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

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

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

编辑:

可以在此链接中找到太阳黑子搜索方法

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

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
Run Code Online (Sandbox Code Playgroud)

笔记2

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

注3

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

Sunns方法文档中未记录此行为.

hor*_*guy 5

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

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

  • 它创建一个空白的平板委托对象.
  • 委托者将所有方法调用转发给"接收者".在你的榜样"接收器"大概是包含方法的对象keywordswithany_of等.
  • 如果在'receiver'中找不到给定的方法,则它将方法调用转发到'context'对象
  • 上下文对象是保存块的绑定的对象.
  • 您可以通过执行以下操作找到给定块的上下文对象: eval('self', block.binding)

理由:

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

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

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

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