Вопрос

Как я могу легко и программно определить, является ли 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
Это было полезно?

Решение

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

Поэтому я думаю, что лучше всего вам подойдет анализ трассировки стека.

Другие советы

Можно изучить относительная глубина 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 - прежний трек отправлять, последний, кажется, молча опускает это (аля Perl goto &sub, возможно?), например.(В версии 1.9 обратная трассировка LJE более мелкий чем caller(0) стек, потому что 1.9 явно учитывает rescue блок как дискретный кадр стека).

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top