Vuelva a subir (la misma excepción) después de detectar una excepción en Ruby

84

Estoy tratando de mejorar mis habilidades con Ruby detectando excepciones. Quiero saber si es común volver a generar el mismo tipo de excepción cuando tiene varias llamadas a métodos. Entonces, ¿tendría sentido el siguiente código? ¿Está bien volver a generar el mismo tipo de excepción o no debería detectarlo en el método de proceso?

class Logo
  def process
    begin
      @processed_logo = LogoProcessor::create_image(self.src)
    rescue CustomException
      raise CustomException
    end
  end
end

module LogoProcessor
  def self.create_image
    raise CustomException if some_condition
  end
end
Hommer Smith
fuente

Respuestas:

168

A veces solo queremos saber que ocurrió un error , sin tener que manejarlo realmente .

A menudo ocurre que el responsable de manejar los errores es el usuario del objeto: el llamador. ¿Qué pasa si estamos interesados ​​en el error, pero no queremos asumir esa responsabilidad? Rescatamos el error, hacemos lo que sea necesario y luego propagamos la señal por la pila como si nada hubiera pasado.

Por ejemplo, ¿qué pasaría si quisiéramos registrar el mensaje de error y luego dejar que la persona que llama lo solucione?

begin
  this_will_fail!
rescue Failure => error
  log.error error.message
  raise
end

Llamar raisesin argumentos generará el último error. En nuestro caso, estamos resubiendo error.

En el ejemplo que presentó en su pregunta, simplemente no es necesario volver a plantear el error. Simplemente puede dejar que se propague por la pila de forma natural. La única diferencia en su ejemplo es que está creando un nuevo objeto de error y elevándolo en lugar de volver a generar el último.

Matheus Moreira
fuente
Interesante. Mi pregunta es, si no capto el error en la definición de proceso, entonces tendría que detectarlo cuando llamo al método de proceso, por ejemplo:, begin @logo.process; rescue...pero entonces no estaría detectando una excepción lanzada por el proceso en sí, sino de algo que ha sido llamado desde dentro del proceso. ¿Es correcto hacerlo?
Hommer Smith
2
Esto perdería la stacktraceexcepción original, probablemente desee incluir la excepción causeque está disponible en ruby> 2.1
bjhaid
4
@bjhaid Llamar raisede la manera de esta respuesta conserva la excepción original por completo, incluido el backtrace. causeno se aplica a este caso. Por el contrario, se llena automáticamente cuando un rescuebloque genera una nueva excepción.
representante
@HommerSmith: ¿Qué pasa si la línea anterior a raise (log.error en este caso, pero podría ser cualquier cosa) falla? Estoy pensando en "asegurarlo", pero, dentro del seguro, necesitaría usar la referencia al error como el argumento de "subir". ¿Qué piensas sobre eso?
jgomo3
1
@ RafałCieślak Cada vez que se genera un error, se asigna a la $!variable global. Llamar raisesin argumentos genera el error contenido en $!, efectivamente levantando el último error. Sin embargo, raise errorgenerará el error contenido en la errorvariable local, que puede ser o no el mismo objeto contenido en $!. En mi ejemplo, $!es lo mismo que error. Sin embargo, también es posible hacer esto:error = Exception.new; raise error
Matheus Moreira
3

Esto generará el mismo tipo de error que el original, pero puede personalizar el mensaje.

rescue StandardError => e
  raise e.class, "Message: #{e.message}"
FreePender
fuente
Sugeriría que es una mala idea detectar StandardError ya que esto incluye todo tipo de funciones de nivel inferior y podría bloquear su programa.
Paul Whitehead
1
Creo que esta es una "excepción" a la regla, ya que volveremos a plantear la excepción inmediatamente.
FreePender