tags:

views:

73

answers:

3

This is my code:

def block
  puts "from block"
  yield
end

block do
  puts "from command line"
  block do

  end
end

Here is the output:

from block
from command line
from block

I wonder how the second block could detect that its inside another block (of the same method).

So that the output will be this instead:

from block 1
from command line
from block 2

Is this possible? Because I want the nested block to be aware of this and run some additional code.

Thanks!

+3  A: 

You could keep track of the block level with an instance variable, increment it whenever you enter a block, and decrement it whenever you leave a block:

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

  puts "from block #@block_level"

  yield

  @block_level -= 1
end
yjerem
I like this simple answer
never_had_a_name
+1  A: 

This answer is mostly just for fun, I don't suggest you use it.

Ruby lets you inspect the call stack in the form of a backtrace, but only when an exception is raised. So let's raise an exception and then stick out our arm and catch it before it goes to anyone else, and then: the backtrace is all ours!!

Then all you need to do is search the backtrace (an array) for any method calls to our method named "block", and count them.

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

Output:

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

Edit: I've since come across the Kernel#caller method which just gives you the current execution stack with no hassles. So the following code might be acceptable, as long as you don't have two methods named "block" in the same file that call each other:

def block
  level = caller.count { |x| x =~ /^#{ Regexp.escape(__FILE__) }:\d+:in `block'$/ } + 1
  puts "from block #{level}"
  yield
end
yjerem
i like that you are creative =)
never_had_a_name
+1  A: 

What yjerem says, just use ensure to avoid troubles with exceptions, and it sounds like a global variable, not instance variable.

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