Определение метода, который использует переменную вне области видимости в Ruby - PullRequest
3 голосов
/ 09 мая 2011

Я хочу создать метод Test :: Unit test_helper, который я могу вызвать, чтобы стереть несколько таблиц после выполнения тестов.Вот общая идея, которая у меня есть:

def self.wipe_models(*models)
  def teardown
    models.each do |model|
      model = model.to_s.camelize.constantize
      model.connection.execute "delete from #{model.table_name}"
    end
  end
end

Однако, когда teardown запускается, я получаю:

неопределенная локальная переменная или метод `models '

Мне кажется, что блок "def" не подчиняется обычным правилам для замыканий;Я не могу получить доступ к переменным, определенным вне его области действия.

Итак, как мне получить доступ к переменной, определенной вне объявления метода "def"?

Ответы [ 3 ]

4 голосов
/ 09 мая 2011

Определения методов не являются замыканиями в Ruby. Ключевые слова class, module, def и end являются воротами области. Чтобы поддерживать область действия через ворота области, вы должны передать блок; блоки являются замыканиями и, следовательно, выполняются в области, в которой они были определены.

def foo
  # since we're in a different scope than the one the block is defined in,
  # setting x here will not affect the result of the yield
  x = 900
  puts yield  #=> outputs "16"
end

# x and the block passed to Proc.new have the same scope
x = 4
square_x = Proc.new { x * x }


foo(&square_x)
4 голосов
/ 09 мая 2011

Вы можете сделать это как замыкание с помощью define_method:

def self.wipe_models(*models)
  define_method(:teardown) do
    models.each do |model|
      model = model.to_s.camelize.constantize
      model.connection.execute "delete from #{model.table_name}"
    end
  end
end

Теперь тело метода является блоком и может иметь доступ к models.

1 голос
/ 09 мая 2011

Используйте переменную экземпляра класса:

cattr_accessor :models_to_wipe

def self.wipe_models(*models)
  self.models_to_wipe = models
end

def teardown
  self.class.models_to_wipe.each do |model|
    model = model.to_s.camelize.constantize
    model.connection.execute "delete from #{model.table_name}"
  end
end
...