Как определить происхождение LocalJumpError? - PullRequest
0 голосов
/ 18 сентября 2009

Как я могу легко и программно определить, возникла ли LocalJumpError из-за немедленной неспособности вызывающей стороны предоставить необходимый блок методу или из глубины внутри этого метода и других, которые он вызывает?

Под словом "легко" я подразумеваю, что хотел бы избежать проверки / регулярного выражения строки на $!.backtrace. Решение, применимое к 1,8 и 1,9, также является предпочтительным.

Мотивация: Когда я запускаю вызов метода в ruby, обычно это происходит из-за того, что я неправильно набрал (NoMethodError) метод, неправильно набрал количество аргументов (ArgumentError) или пренебрег необходимостью передать необходимый блок (LocalJumpError).

Для прокси или декорирования объекта-оболочки в ruby ​​я хотел бы отличить эти ошибки вызывающего или API * от ошибок разработчика или среды , которые могут вызывать те же классы ошибок , Например:

...
def method_missing(sym, *args, &block)
  @wrapped.__send__(sym, *args, &block)
rescue NoMethodError
  raise MyApp::BadInvocation, "duh - no such method" unless @wrapped.respond_to?(sym)
  raise
rescue ArgumentError
  raise MyApp::BadInvocation, "duh - wrong arg count" \
    unless _args_fit_arity?(@wrapped.method(sym), args)
  raise
rescue LocalJumpError
  # XXX - what is the test?
  raise
end

Ответы [ 2 ]

1 голос
/ 18 сентября 2009

Чтобы выяснить, была ли локальная ошибка вызвана тем, что пользователь забыл передать блок, вам необходимо знать две вещи: предоставил ли пользователь блок и нужен ли методу метод. Первое легко: просто проверьте, является ли blk нулевым. Второе, однако, невозможно (по крайней мере, в обычном рубине).

Так что я думаю, что лучше всего разбирать трассировку стека.

0 голосов
/ 19 сентября 2009

Можно исследовать относительную глубину backtrace, чтобы сделать лучшее усилие, чтобы отличить ошибку вызывающего абонента от последующей ошибки глубже в стеке вызовов:

def lje_depth_from_send
  Class.new { def lje; yield end }.new.__send__ :lje
rescue LocalJumpError
  return $!.backtrace.size - caller(0).size
end

def method_missing(sym, *args, &block)
  ...
rescue LocalJumpError
  if !block_given? and ($!.backtrace.size - caller(0).size) == lje_depth_from_send
    raise MyApp::BadInvocation, "duh - you forgot to supply a block"
  end
  raise
end

Интересно, что этот расчет относительной глубины меняется с MRI 1.8 на MRI 1.9 - первые треки отправляют , последние, кажется, молча пропускают его (например, ala perl's goto &sub, возможно?). (В соответствии с 1.9 обратная трассировка LJE на меньше , чем стек caller(0), потому что 1.9 явно считает блок rescue как кадр дискретного стека).

Теперь, это может не работать при отсутствии MRI, но я сомневаюсь, что разбор стека вызовов будет переносимым из одной реализации в другую.

...