一个块怎么能检测到它在另一个块里?

时间:2010-07-28 22:53:35

标签: ruby

这是我的代码:

def block
  puts "from block"
  yield
end

block do
  puts "from command line"
  block do

  end
end

这是输出:

from block
from command line
from block

我想知道第二个块是如何检测到它在另一个块内(同一方法)。

这样输出就是:

from block 1
from command line
from block 2

这可能吗?因为我希望嵌套块知道这一点并运行一些额外的代码。

谢谢!

3 个答案:

答案 0 :(得分:3)

您可以使用实例变量跟踪块级别,每当您输入块时都会增加它,并在您离开块时递减它:

def block
  @block_level ||= 0
  @block_level += 1

  puts "from block #@block_level"

  yield

  @block_level -= 1
end

答案 1 :(得分:2)

这个答案主要是为了好玩,我不建议您使用它。

Ruby允许您以回溯的形式检查调用堆栈,但仅在引发异常时才会检查。所以,让我们提出一个异常,然后伸出手臂,在它传给任何人之前抓住它,然后:回溯是我们的!

然后你需要做的就是搜索backtrace(一个数组)对我们名为“block”的方法的任何方法调用,并对它们进行计数。

class InspectBacktrace < Exception
end

def block
  raise InspectBacktrace
rescue InspectBacktrace => e
  level = e.backtrace.count { |x| x =~ /in `block'/ }
  puts "from block #{level}"
  yield
end

block do
  puts "from command line"
  block do
    puts "from command line"
    block do
      puts "from command line"
    end
  end
end

输出:

from block 1
from command line
from block 2
from command line
from block 3
from command line

编辑:我已经遇到了Kernel#caller方法,它只是给你当前的执行堆栈而没有麻烦。所以下面的代码可能是可以接受的,只要你在相同的文件中没有两个名为“block”的方法相互调用:

def block
  level = caller.count { |x| x =~ /^#{ Regexp.escape(__FILE__) }:\d+:in `block'$/ } + 1
  puts "from block #{level}"
  yield
end

答案 2 :(得分:1)

yjerem说的是,只需使用ensure来避免异常的麻烦,它听起来像一个全局变量,而不是实例变量。

def block
  begin
    $block_level ||= 0
    $block_level += 1
    puts "from block #{$block_level}"
    yield
  ensure
    $block_level -= 1
  end
end