@instance_variable недоступно внутри блока ruby?

Со следующим кодом:

def index
  @q = ""
  @q = params[:search][:q] if params[:search]
  q = @q
  @search = Sunspot.search(User) do
    keywords q
  end
  @users = @search.results
end

Если вместо q используется @q, поиск всегда возвращает результаты для пустого запроса (""). Почему это? Является ли переменная @q недоступной для блока do... end?

Ответы

Ответ 1

Это зависит от того, как вызывается блок. Если он вызывается с использованием ключевого слова yield или метода Proc#call, то вы сможете использовать переменные экземпляра в блоке. Если он вызывается с использованием Object#instance_eval или Module#class_eval, тогда контекст блока будет изменен, и вы не сможете получить доступ к своим переменным экземпляра.

@x = "Outside the class"

class Test
  def initialize
    @x = "Inside the class"
  end

  def a(&block)
    block.call
  end

  def b(&block)
    self.instance_eval(&block)
  end
end

Test.new.a { @x } #=> "Outside the class"
Test.new.b { @x } #=> "Inside the class"

В вашем случае похоже, что Sunspot.search вызывает ваш блок в другом контексте с помощью instance_eval, потому что блоку нужен легкий доступ к этому методу keywords.

Ответ 2

Как говорит Джереми, Sunspot выполняет поиск DSL в новой области.

Чтобы использовать переменную экземпляра в блоке Sunspot.search, вам необходимо передать ей аргумент. Что-то вроде этого должно работать (не проверено):

  @q = params[:search][:q] if params[:search]
  @search = Sunspot.search(User) do |query|
    query.keywords @q
  end
  @users = @search.results

См. здесь лучшее объяснение: http://groups.google.com/group/ruby-sunspot/msg/d0444189de3e2725