Как один блок может обнаружить это внутри другого блока? - PullRequest
3 голосов
/ 29 июля 2010

Это мой код:

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 ]

3 голосов
/ 29 июля 2010

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

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

  puts "from block #@block_level"

  yield

  @block_level -= 1
end
2 голосов
/ 29 июля 2010

Этот ответ в основном просто для забавы, я не советую вам его использовать.

Ruby позволяет вам проверять стек вызовов в форме обратной трассировки, но только при возникновении исключения.Итак, давайте создадим исключение, а затем вытянем руку и поймать ее, прежде чем она кому-то еще достанется, а затем: обратная трассировка - это все наше !!

Тогда все, что вам нужно сделать, - это найти обратную трассировку (массив) для любых вызовов методов нашего метода с именем "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
1 голос
/ 29 июля 2010

Что говорит yjerem, просто используйте ensure, чтобы избежать проблем с исключениями, и это звучит как глобальная переменная, а не переменная экземпляра.

def block
  begin
    $block_level ||= 0
    $block_level += 1
    puts "from block #{$block_level}"
    yield
  ensure
    $block_level -= 1
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...