2009-09-18 2 views
0

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

«легко», я имею в виду, что я хотел бы избегать проверки цепей/regexen на $!.backtrace. Также предпочтительным является решение, применимое к 1,8 и 1,9.

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

Для проксирования или декорирования объекта оболочки в рубине, я хотел бы, чтобы различать эти звонящих по телефону или 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 

ответ

1

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

Итак, я думаю, что разбор трассировки стека - ваш лучший выбор.

+0

+1 Мне не пришло в голову проверить, действительно ли был поставлен блок. (Тем не менее, мне все равно не нравятся разборки backtrace * per se *.) – pilcrow

+0

@ sepp2k, я думаю, что нашел лучшую ставку ... опубликует через мгновение. – pilcrow

+0

@ sepp2k, я думаю, что побеждаю за строгую точность, но вы выигрываете для простоты, что является лучшим критерием здесь. :) – pilcrow

0

можно рассматривать относительную глубину 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 

Интересно, что это относительные изменения расчета глубины от МРТ 1.8 к MRI 1.9 - прежние треки пришлите, последний, кажется, молча опустил его (например, goto &sub). (До 1,9, обратная трасса LJE равна более мелким, чем стек caller(0), поскольку 1.9 явно подсчитывает блок rescue как дискретный стек кадров).

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

Смежные вопросы