Como determinar a origem de um LocalJumpError?
Pergunta
Como posso determinar com facilidade e programaticamente se um LocalJumpError
surgiu da falha imediata do autor da chamada para fornecer um bloco necessária para um método, ou de mais profundo dentro desse método e outros que invoca?
Por "facilmente", quero dizer que eu gostaria de inspeção seqüência de evitar / regexen em $!.backtrace
. Uma solução aplicável a 1,8 e 1,9 é também preferido.
motivação: Quando eu estragar uma chamada de método em ruby, geralmente é porque eu mal digitado o método (NoMethodError
), tem o número de argumentos errado (ArgumentError
) ou negligenciado passar um bloco necessário (LocalJumpError
).
Para um proxy ou decorar objeto de invólucro em ruby, eu gostaria de discriminar estes chamador ou API erros do implementador ou ambiente de erros que podem elevar as mesmas classes de erro. Por exemplo:
...
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
Solução
Para saber se o LocalJumpError foi causado pelo esquecimento usuário para passar um bloco, você precisa saber duas coisas: Se o usuário forneceu um bloco e se o método precisa de um bloco. A primeira é fácil: basta verificar se o preto é nulo. O segundo, porém, é impossível (em ruby ??simples, pelo menos).
Então eu acho que a análise do rastreamento de pilha é sua melhor aposta.
Outras dicas
Pode-se examinar o profundidade relativa do backtrace
para fazer uma melhor erro chamador esforço discriminar de um erro posterior mais profundo na pilha de chamadas:
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
Curiosamente, esse parente mudanças de cálculo profundidade de MRI 1,8 a MRI 1.9 - os antigos trilhos Enviar , o último parece omitir silenciosamente (da ala perl goto &sub
, talvez?), Por exemplo. (De acordo com 1.9, o registo de chamadas LJE é rasa do que a pilha caller(0)
, porque 1,9 conta explicitamente o bloco rescue
como um quadro de pilha discreta).
Agora, este pode não funcionar em condições não-MRI, mas eu duvido que a análise da pilha de chamadas seria portátil de uma implementação para outra, qualquer um.