Ruby - Excepciones

La ejecución y la excepción siempre van juntas. Si está abriendo un archivo, que no existe, si no manejó esta situación correctamente, se considera que su programa es de mala calidad.

El programa se detiene si ocurre una excepción. Por lo tanto, las excepciones se utilizan para manejar varios tipos de errores, que pueden ocurrir durante la ejecución de un programa y tomar la acción apropiada en lugar de detener el programa por completo.

Ruby proporciona un buen mecanismo para manejar excepciones. Incluimos el código que podría generar una excepción en un bloque de inicio / fin y usamos cláusulas de rescate para decirle a Ruby los tipos de excepciones que queremos manejar.

Sintaxis

begin  
# -  
rescue OneTypeOfException  
# -  
rescue AnotherTypeOfException  
# -  
else  
# Other exceptions
ensure
# Always will be executed
end

Todo, desde el principio hasta el rescate, está protegido. Si ocurre una excepción durante la ejecución de este bloque de código, el control se pasa al bloque entre el rescate y el final .

Para cada cláusula de rescate en el bloque de inicio , Ruby compara la excepción planteada con cada uno de los parámetros por turno. La coincidencia tendrá éxito si la excepción nombrada en la cláusula de rescate es el mismo que el tipo de la excepción lanzada actualmente, o es una superclase de esa excepción.

En el caso de que una excepción no coincida con ninguno de los tipos de error especificados, se nos permite usar una cláusula else después de todas las cláusulas de rescate .

Ejemplo

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
      file = STDIN
end
print file, "==", STDIN, "\n"

Esto producirá el siguiente resultado. Puede ver que STDIN se sustituye al archivo porque no se pudo abrir .

#<IO:0xb7d16f84>==#<IO:0xb7d16f84>

Usar declaración de reintento

Se puede capturar una excepción el uso de rescate del bloque y el uso de reintento sentencia que debe ejecutarse comienzan bloque desde el principio.

Sintaxis

begin
   # Exceptions raised by this code will 
   # be caught by the following rescue clause
rescue
   # This block will capture all types of exceptions
   retry  # This will move control to the beginning of begin
end

Ejemplo

#!/usr/bin/ruby

begin
   file = open("/unexistant_file")
   if file
      puts "File opened successfully"
   end
rescue
   fname = "existant_file"
   retry
end

El siguiente es el flujo del proceso:

  • Se produjo una excepción al abrir.
  • Fui a rescatar. fname fue reasignado.
  • Reintentar fue al principio del comienzo.
  • Este archivo de tiempo se abre correctamente.
  • Continuó el proceso esencial.

NOTE- Observe que si el archivo de nombre reemplazado no existe, este código de ejemplo se reintenta infinitamente. Tenga cuidado si usa Reintentar para un proceso de excepción.

Usar declaración de aumento

Puede usar una declaración de aumento para generar una excepción. El siguiente método genera una excepción cada vez que se llama. Se imprimirá su segundo mensaje.

Sintaxis

raise 

OR

raise "Error Message" 

OR

raise ExceptionType, "Error Message"

OR

raise ExceptionType, "Error Message" condition

El primer formulario simplemente vuelve a generar la excepción actual (o un RuntimeError si no hay una excepción actual). Esto se usa en manejadores de excepciones que necesitan interceptar una excepción antes de pasarla.

El segundo formulario crea una nueva excepción RuntimeError , estableciendo su mensaje en la cadena dada. Esta excepción luego se genera en la pila de llamadas.

El tercer formulario usa el primer argumento para crear una excepción y luego establece el mensaje asociado al segundo argumento.

La cuarta forma es similar a la tercera, pero puede agregar cualquier declaración condicional como a menos que genere una excepción.

Ejemplo

#!/usr/bin/ruby

begin  
   puts 'I am before the raise.'  
   raise 'An error has occurred.'  
   puts 'I am after the raise.'  
rescue  
   puts 'I am rescued.'  
end  
puts 'I am after the begin block.'

Esto producirá el siguiente resultado:

I am before the raise.  
I am rescued.  
I am after the begin block.

Un ejemplo más que muestra el uso de raise -

#!/usr/bin/ruby

begin  
   raise 'A test exception.'  
rescue Exception => e  
   puts e.message  
   puts e.backtrace.inspect  
end

Esto producirá el siguiente resultado:

A test exception.
["main.rb:4"]

Usar declaración de garantía

A veces, debe garantizar que se realice algún procesamiento al final de un bloque de código, independientemente de si se generó una excepción. Por ejemplo, puede tener un archivo abierto al ingresar al bloque y debe asegurarse de que se cierre cuando el bloque sale.

La garantizar cláusula hace precisamente esto. asegurar va después de la última cláusula de rescate y contiene un fragmento de código que siempre se ejecutará cuando finalice el bloque. No importa si las salidas de bloque normalmente, si eleva y libra una excepción, o si se ha interrumpido por una excepción no detectada, al asegurar bloque conseguirá ejecutar.

Sintaxis

begin 
   #.. process 
   #..raise exception
rescue 
   #.. handle error 
ensure 
   #.. finally ensure execution
   #.. This will always execute.
end

Ejemplo

begin
   raise 'A test exception.'
rescue Exception => e
   puts e.message
   puts e.backtrace.inspect
ensure
   puts "Ensuring execution"
end

Esto producirá el siguiente resultado:

A test exception.
["main.rb:4"]
Ensuring execution

Usando la instrucción else

Si la cláusula else está presente, va después de las cláusulas de rescate y antes de cualquier seguro .

El cuerpo de una cláusula else se ejecuta solo si el cuerpo principal del código no genera excepciones.

Sintaxis

begin 
   #.. process 
   #..raise exception
rescue 
   # .. handle error
else
   #.. executes if there is no exception
ensure 
   #.. finally ensure execution
   #.. This will always execute.
end

Ejemplo

begin
   # raise 'A test exception.'
   puts "I'm not raising exception"
rescue Exception => e
   puts e.message
   puts e.backtrace.inspect
else
   puts "Congratulations-- no errors!"
ensure
   puts "Ensuring execution"
end

Esto producirá el siguiente resultado:

I'm not raising exception
Congratulations-- no errors!
Ensuring execution

El mensaje de error generado se puede capturar usando $! variable.

Atrapar y lanzar

Si bien el mecanismo de excepción de aumento y rescate es excelente para abandonar la ejecución cuando las cosas salen mal, a veces es bueno poder saltar de alguna construcción profundamente anidada durante el procesamiento normal. Aquí es donde atrapar y lanzar son útiles.

La captura define un bloque que está etiquetado con el nombre de pila (que puede ser un símbolo o una cadena). El bloqueo se ejecuta normalmente hasta que se encuentra un lanzamiento.

Sintaxis

throw :lablename
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end

OR

throw :lablename condition
#.. this will not be executed
catch :lablename do
#.. matching catch will be executed after a throw is encountered.
end

Ejemplo

El siguiente ejemplo usa un lanzamiento para terminar la interacción con el usuario si '!' se escribe en respuesta a cualquier solicitud.

def promptAndGet(prompt)
   print prompt
   res = readline.chomp
   throw :quitRequested if res == "!"
   return res
end

catch :quitRequested do
   name = promptAndGet("Name: ")
   age = promptAndGet("Age: ")
   sex = promptAndGet("Sex: ")
   # ..
   # process information
end
promptAndGet("Name:")

Debería probar el programa anterior en su máquina porque necesita interacción manual. Esto producirá el siguiente resultado:

Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby

Excepción de clase

Las clases y módulos estándar de Ruby generan excepciones. Todas las clases de excepción forman una jerarquía, con la clase Exception en la parte superior. El siguiente nivel contiene siete tipos diferentes:

  • Interrupt
  • NoMemoryError
  • SignalException
  • ScriptError
  • StandardError
  • SystemExit

Hay otra excepción a este nivel, Fatal, pero el intérprete de Ruby solo lo usa internamente.

Tanto ScriptError como StandardError tienen varias subclases, pero no necesitamos entrar en detalles aquí. Lo importante es que si creamos nuestras propias clases de excepción, deben ser subclases de la clase Exception o de una de sus descendientes.

Veamos un ejemplo:

class FileSaveError < StandardError
   attr_reader :reason
   def initialize(reason)
      @reason = reason
   end
end

Ahora, mire el siguiente ejemplo, que usará esta excepción:

File.open(path, "w") do |file|
begin
   # Write out the data ...
rescue
   # Something went wrong!
   raise FileSaveError.new($!)
end
end

La línea importante aquí es subir FileSaveError.new ($!) . Llamamos a raise para señalar que se ha producido una excepción, pasándole una nueva instancia de FileSaveError, con la razón de que la excepción específica provocó que la escritura de los datos fallara.