Ruby QuickRef de Ryan Davis dice (sin explicación):
No rescates la excepción. SIEMPRE. o te apuñalaré.
Por qué no? ¿Qué es lo que hay que hacer?
ruby
exception-handling
Juan
fuente
fuente
Respuestas:
TL; DR : utilice en su
StandardError
lugar para la captura de excepciones generales. Cuando se vuelve a generar la excepción original (por ejemplo, cuando se rescata para registrar solo la excepción), el rescateException
probablemente esté bien.Exception
es la raíz de la jerarquía de excepciones de Ruby , por lo que alrescue Exception
rescatar de todo , incluyendo las subclases tales comoSyntaxError
,LoadError
, yInterrupt
.El rescate
Interrupt
evita que el usuario use CTRLCpara salir del programa.El rescate
SignalException
evita que el programa responda correctamente a las señales. Será invencible excepto porkill -9
.Rescatar
SyntaxError
significa queeval
los que fallan lo harán en silencio.Todo esto se puede mostrar ejecutando este programa e intentando CTRLCo
kill
:Rescatar de
Exception
ni siquiera es el valor predeterminado. Haciendono rescata
Exception
, rescata deStandardError
. En general, debe especificar algo más específico que el predeterminadoStandardError
, pero rescatarloException
amplía el alcance en lugar de reducirlo, y puede tener resultados catastróficos y hacer que la búsqueda de errores sea extremadamente difícil.Si tiene una situación en la que desea rescatar
StandardError
y necesita una variable con la excepción, puede usar este formulario:que es equivalente a:
Uno de los pocos casos comunes en los que es sensato rescatar
Exception
es para fines de registro / informes, en cuyo caso debe volver a plantear la excepción de inmediato:fuente
Throwable
en JavaADAPTER_ERRORS = [::ActiveRecord::StatementInvalid, PGError, Mysql::Error, Mysql2::Error, ::ActiveRecord::JDBCError, SQLite3::Exception]
y luegorescue *ADAPTER_ERRORS => e
La verdadera regla es: no descarte las excepciones. La objetividad del autor de su cita es cuestionable, como lo demuestra el hecho de que termina con
Por supuesto, tenga en cuenta que las señales (por defecto) arrojan excepciones, y normalmente los procesos de ejecución prolongada se terminan a través de una señal, por lo que capturar la excepción y no terminar en las excepciones de la señal hará que su programa sea muy difícil de detener. Entonces no hagas esto:
No, en serio, no lo hagas. Ni siquiera ejecutes eso para ver si funciona.
Sin embargo, supongamos que tiene un servidor con subprocesos y desea que todas las excepciones no:
thread.abort_on_exception = true
).Entonces esto es perfectamente aceptable en su hilo de manejo de conexión:
Lo anterior funciona con una variación del controlador de excepciones predeterminado de Ruby, con la ventaja de que tampoco mata su programa. Rails hace esto en su manejador de solicitudes.
Las excepciones de señal se generan en el hilo principal. Los hilos de fondo no los conseguirán, por lo que no tiene sentido tratar de atraparlos allí.
Esto es particularmente útil en un entorno de producción, donde no desea que su programa simplemente se detenga cuando algo sale mal. Luego puede tomar los volcados de la pila en sus registros y agregarlos a su código para lidiar con excepciones específicas más adelante en la cadena de llamadas y de una manera más elegante.
Tenga en cuenta también que hay otra expresión de Ruby que tiene el mismo efecto:
En esta línea, si
do_something
se produce una excepción, Ruby lo atrapa, lo tira ya
lo asigna"something else"
.En general, no haga eso, excepto en casos especiales en los que sabe que no necesita preocuparse. Un ejemplo:
La
debugger
función es una forma bastante agradable de establecer un punto de interrupción en su código, pero si se ejecuta fuera de un depurador y Rails, genera una excepción. Ahora, en teoría, no deberías dejar el código de depuración en tu programa (¡pff! ¡Nadie hace eso!), Pero es posible que quieras mantenerlo allí por un tiempo por alguna razón, pero no ejecutes continuamente tu depurador.Nota:
Si ha ejecutado el programa de otra persona que detecta excepciones de señales y las ignora (diga el código anterior), entonces:
pgrep ruby
ops | grep ruby
busque el PID de su programa infractor y luego ejecútelokill -9 <PID>
.Si está trabajando con el programa de otra persona que, por cualquier motivo, está salpicado de estos bloques de ignorar excepciones, poner esto en la parte superior de la línea principal es una posible solución:
Esto hace que el programa responda a las señales de terminación normales al terminar inmediatamente, sin pasar por los controladores de excepciones, sin limpieza . Por lo tanto, podría causar pérdida de datos o similar. ¡Ten cuidado!
Si necesitas hacer esto:
en realidad puedes hacer esto:
En el segundo caso,
critical cleanup
se llamará cada vez, se produzca o no una excepción.fuente
kill -9
.ensure
se ejecutará independientemente de si se produjo una excepción o no, mientrasrescue
que solo se ejecutará si se generó una excepción.TL; DR
No lo hagas
rescue Exception => e
(y no vuelvas a plantear la excepción), o podrías salirte de un puente.Digamos que estás en un auto (corriendo Ruby). Recientemente instaló un nuevo volante con el sistema de actualización por aire (que utiliza
eval
), pero no sabía que uno de los programadores había estropeado la sintaxis.Estás en un puente y te das cuenta de que vas un poco hacia la barandilla, así que giras a la izquierda.
¡Uy! Probablemente no sea Good ™, afortunadamente, Ruby plantea a
SyntaxError
.El auto debe detenerse de inmediato, ¿verdad?
No
Se nota que algo está mal, y que pisar el frenos de emergencia (
^C
:Interrupt
)Sí, eso no ayudó mucho. Estás bastante cerca del riel, por lo que pones el auto en estacionamiento (
kill
ing:)SignalException
.En el último segundo, sacas las llaves (
kill -9
), y el automóvil se detiene, se estrella contra el volante (el airbag no puede inflarse porque no detuvo el programa con gracia, lo cerró) y la computadora en la parte de atrás de su automóvil se estrella contra el asiento frente a él. Una lata medio llena de Coca-Cola se derrama sobre los papeles. Los comestibles en la parte posterior están triturados, y la mayoría están cubiertos de yema de huevo y leche. El auto necesita reparaciones y limpieza serias. (Pérdida de datos)Esperemos que tenga seguro (copias de seguridad). Ah, sí, debido a que el airbag no se infló, probablemente estés herido (despedido, etc.).
¡Pero espera! Hay
másrazones por las que es posible que desee usarrescue Exception => e
!Digamos que usted es ese automóvil y desea asegurarse de que el airbag se infla si el automóvil excede su impulso de frenado seguro.
Aquí está la excepción a la regla: puede atrapar
Exception
solo si vuelve a aumentar la excepción . Entonces, una mejor regla es nunca tragarException
, y siempre volver a plantear el error.Pero agregar rescate es fácil de olvidar en un lenguaje como Ruby, y poner una declaración de rescate justo antes de volver a plantear un problema se siente un poco no SECO. Y no quieres olvidar la
raise
declaración. Y si lo haces, buena suerte tratando de encontrar ese error.Afortunadamente, Ruby es increíble, solo puedes usar la
ensure
palabra clave, lo que asegura que el código se ejecute. Laensure
palabra clave ejecutará el código pase lo que pase: si se lanza una excepción, si no es así, la única excepción es si el mundo termina (u otros eventos poco probables).¡Auge! Y ese código debería ejecutarse de todos modos. La única razón por la que debe usar
rescue Exception => e
es si necesita acceso a la excepción, o si solo desea que el código se ejecute en una excepción. Y recuerde volver a plantear el error. Cada vez.Nota: Como señaló @Niall, asegúrese de que siempre se ejecute. Esto es bueno porque a veces su programa puede mentirle y no lanzar excepciones, incluso cuando ocurren problemas. Con tareas críticas, como inflar bolsas de aire, debe asegurarse de que suceda sin importar lo que pase. Debido a esto, es una buena idea verificar cada vez que el automóvil se detiene, si se produce una excepción o no. Aunque inflar airbags es una tarea poco común en la mayoría de los contextos de programación, esto es bastante común en la mayoría de las tareas de limpieza.
fuente
ensure
como alternativarescue Exception
es engañosa: el ejemplo implica que son equivalentes, pero como se dijoensure
sucederá si hay una Excepción o no, por lo que ahora sus bolsas de aire se inflarán porque superó las 5 mph, aunque nada salió mal.Porque esto captura todas las excepciones. Es poco probable que su programa pueda recuperarse de cualquiera de ellos.
Debe manejar solo las excepciones de las que sabe cómo recuperarse. Si no anticipa un cierto tipo de excepción, no lo maneje, bloquee ruidosamente (escriba los detalles en el registro), luego diagnostique los registros y corrija el código.
Tragar excepciones es malo, no hagas esto.
fuente
Ese es un caso específico de la regla de que no debe detectar ninguna excepción que no sepa cómo manejar. Si no sabe cómo manejarlo, siempre es mejor dejar que otra parte del sistema lo atrape y lo maneje.
fuente
Acabo de leer una gran publicación de blog al respecto en honeybadger.io :
Ruby's Exception vs StandardError: ¿Cuál es la diferencia?
fuente