Почему Sunspot меняет `self` в поисковом DSL-блоке? - PullRequest
2 голосов
/ 18 сентября 2010

Я заметил (и подтвердил в коде пятна) следующее поведение

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 делает это ипочему нет предупреждения об этом поведении в документации.

Редактировать:

Метод поиска 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

Note2

Я нашел код в дереве исходного кода Sunspot, который изменяет обычный блокповедение.Мой вопрос о причине фальсификации привязки следующим образом.

Note3

В частности, я обнаружил проблему при вызове метода id в стороне блока.Метод search делегирует вызов метода внутри блока объекту DSL, и если он не находит метод, то вызов повторно делегируется в контекст вызова.Метод поиска удаляет все, кроме основных методов, из объекта DSL перед регистрацией кода делегирования.Метод id не удаляется.Это вызывает проблему.Для всех остальных методов делегирование работает нормально.

Это поведение не описано в документации по методу Sunspot.

Ответы [ 2 ]

5 голосов
/ 18 сентября 2010

Хорошо, я знаю, как это работает:

Волшебство находится в ContextBoundDelegate в util.rb.

  • Он создает пустой объект делегата сланца.
  • Делегатор перенаправляет все вызовы методов «получателю».В вашем примере «приемник», вероятно, является объектом, который содержит методы keywords и with и any_of и т. Д.
  • Если данный метод не найден в «приемнике», то он пересылаетвызов метода для объекта 'context'
  • Объект context - это объект, который содержит привязки для блока.
  • Чтобы найти объект контекста для данного блока, выполните следующее: eval('self', block.binding)

Обоснование:

Таким образом, результатом всего этого является то, что блок не только получает доступ к методам в объекте поиска (а-ля * 1026)*) но он также получает доступ к локальным методам в вызывающей области блока.

Блок также, конечно, получает доступ к локальным переменным в вызывающей области блока, но это просто нормальное закрытиеповедение.

Блок не , однако, получает доступ к переменным экземпляра в вызывающей области блока.

Следующий код может быть полезен, так как он примерно соответствуетта же идеяЭто гораздо проще и не так сложно: Использовать методы из двух разных областей?

1 голос
/ 18 сентября 2010

Разве это не просто instance_eval?Если вы не говорите о доступе к переменным экземпляра из вызывающего контекста, это нормальное поведение закрытия.

Я предполагаю, что instance_eval (изменение в self) используется для обеспеченияkeywords и другие связанные с ним методы для блока.

...