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
Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top