开发者

Defining a method that uses an out-of-scope variable in Ruby

I want to make a Test::Unit test_helper method that I can call to wip开发者_如何学JAVAe a bunch of tables after the tests execute. Here's the general idea I have:

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

However, when teardown runs, I get:

undefined local variable or method `models'

To me it looks like the "def" block doesn't obey usual rules for closures; I can't access variables defined outside of its scope.

So, how do I access a variable that's defined outside of a "def" method declaration?


You can do it as a closure with 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

Now the method body is a block and can access models.


Method definitions are not closures in Ruby. The class, module, def, and end keywords are all scope gates. In order to maintain scope across a scope gate you have to pass a block; blocks are closures and thus run in the scope in which they were defined.

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)


Use a class instance variable:

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜