Pregunta

¿Cómo puedo determinar fácil y programáticamente si un LocalJumpError surgió de la falla inmediata de la persona que llama para suministrar un bloque necesario a un método, o de lo más profundo dentro de ese método y otros que invoca?

Por " fácilmente, " Quiero decir que me gustaría evitar la inspección de cadenas / regexen en $ !. backtrace . También se prefiere una solución aplicable a 1.8 y 1.9.

Motivación: Cuando fallé una llamada al método en ruby, generalmente es porque escribí mal el método ( NoMethodError ), obtuve el número de argumentos incorrectos ( ArgumentError ) o no pasó un bloque necesario ( LocalJumpError ).

Para un objeto envoltorio de representación o decoración en ruby, me gustaría discriminar estos errores de llamada o API de los errores del implementador o del entorno que pueden generar las mismas clases de error Por ejemplo:

...
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
¿Fue útil?

Solución

Para averiguar si LocalJumpError fue causado por el usuario que olvidó pasar un bloque, debe saber dos cosas: si el usuario proporcionó un bloque y si el método necesita un bloque. El primero es fácil: solo verifique si blk es nulo. Sin embargo, el segundo es imposible (al menos en rubí simple).

Así que supongo que analizar la traza de la pila es tu mejor opción.

Otros consejos

Se puede examinar la profundidad relativa de backtrace para hacer un mejor esfuerzo para discriminar el error de la persona que llama de un error posterior más profundo en la pila de llamadas:

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, este cálculo de profundidad relativa cambia de MRI 1.8 a MRI 1.9: las pistas anteriores enviar , la última parece omitirlo en silencio ( goto & amp; sub , tal vez?), por ejemplo. (En 1.9, la traza inversa de LJE es menos profunda que la pila caller (0) , porque 1.9 cuenta explícitamente el bloque rescue como un marco de pila discreto ).

Ahora, esto podría no funcionar en una no MRI, pero dudo que el análisis de la pila de llamadas sea portátil de una implementación a otra, tampoco.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top