我注意到(并在太阳黑子代码中验证)以下行为
class Foo < ActiveRecord::Base
def bar
search_str = "foo"
Boo.search do
keywords(search_str)
p self.id
p self
end
end
end
在上面的代码中,DSL block 可以访问定义在
上下文。但是 block 内的 self
指向一个实例
Sunspot::DSL::Search
类(而不是 Foo
类的实例。)
当我尝试访问 self.id
时,而不是获取 Foo
的 id
目的;我获得了 Sunspot::DSL::Search
对象的 id
。
我认为 Sunpot 正在 Util.instance_eval_or_call
方法中执行一些绑定(bind)交换/委托(delegate)魔法。
我很好奇太阳黑子为什么这样做以及为什么没有警告 文档中的这种行为。
编辑:
太阳黑子搜索方法可以在这个link找到
下面的代码将说明我的观点。在方法 foo
中,我有一个行为符合预期的 block 。在方法 bar
中,该 block 没有行为。
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 源代码树中找到了修改正常 block 行为的代码。我的问题是关于像这样安装绑定(bind)的原因。
注释3
具体来说,我在调用 block 内部的 id
方法时发现了一个问题。 search
方法将 block 内的方法调用委托(delegate)给 DSL 对象,如果找不到该方法,则将调用重新委托(delegate)给调用上下文。在注册委托(delegate)代码之前,搜索方法会从 DSL 对象中剥离除基本方法之外的所有方法。 id
方法没有被删除。这是造成问题的原因。对于所有其他方法,委托(delegate)工作正常。
太阳黑子方法文档中未记录此行为。
最佳答案
好的,我知道它是如何工作的:
神奇之处在于 util.rb 中的 ContextBoundDelegate
。
- 它创建一个空白的 slate 委托(delegate)者对象。
- 委托(delegate)者将所有方法调用转发给“接收者”。在您的示例中,“接收者”可能是包含方法
keywords
、with
和any_of
等方法的对象。 - 如果在“receiver”中找不到给定方法,则它将方法调用转发到“context”对象
- 上下文对象是保存 block 绑定(bind)的对象。
- 您可以通过执行以下操作找到给定 block 的上下文对象:
eval('self', block.binding)
理由:
因此,所有这一切的效果是,该 block 不仅可以访问搜索对象中的方法(类似于 instance_eval
),而且还可以访问该对象的调用范围中的本地方法。 block 。
当然,该 block 还可以访问该 block 的调用范围内的局部变量,但这只是正常的闭包行为。
但是,该 block 不能访问该 block 调用范围内的实例变量。
以下代码可能有用,因为它遵循大致相同的想法,但更简单且不太复杂:Using methods from two different scopes?
关于ruby-on-rails - 为什么太阳黑子会更改搜索 DSL block 中的 `self`?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3739851/