Manejo de Excepciones
Introducción
En esta sección aprenderemos a manejar errores y excepciones en Ruby. Veremos cómo podemos rescatar excepciones y cómo podemos crear nuestras propias excepciones personalizadas. También veremos cómo podemos realizar pruebas unitarias en Ruby y cómo podemos utilizar herramientas de debugging para encontrar errores en nuestro código.
Rescate de Excepciones
En Ruby, las excepciones son objetos que representan un error en la ejecución de un programa. Cuando se produce un error, Ruby lanza una excepción y detiene la ejecución del programa. Para evitar que el programa se detenga, podemos rescatar excepciones utilizando la palabra clave rescue.
begin
# Código que puede lanzar una excepción
= File.open("data.txt")
file = file.read
contents puts contents
rescue Errno::ENOENT
# Código que se ejecuta si el archivo no existe
puts "El archivo no existe"
rescue Errno::EACCES
# Código que se ejecuta si no se tiene acceso al archivo
puts "No se tiene acceso al archivo"
rescue => e
# Código que se ejecuta para cualquier otra excepción
puts "Se ha producido un error: #{e.message}"
ensure
# Código que se ejecuta siempre, sin importar si se lanzó una excepción o no
.close if file
fileend
En el ejemplo anterior, el bloque begin se utiliza para envolver el código que puede lanzar una excepción. Si se produce una excepción, el bloque rescue se ejecuta y se captura la excepción. Podemos utilizar varios bloques rescue para capturar diferentes excepciones. También podemos utilizar un bloque rescue sin argumentos para capturar cualquier excepción. El bloque ensure se ejecuta siempre, sin importar si se lanzó una excepción o no.
Creación de Excepciones Personalizadas
En Ruby, podemos crear nuestras propias excepciones personalizadas heredando de la clase Exception.
class MyError < Exception
end
begin
raise MyError, "Se ha producido un error"
rescue MyError => e
puts "Se ha producido un error personalizado: #{e.message}"
end
En el ejemplo anterior, creamos una excepción personalizada llamada MyError que hereda de la clase Exception. Luego, lanzamos la excepción MyError utilizando la palabra clave raise y capturamos la excepción utilizando un bloque rescue.
Ejemplo práctico: Crear una excepción personalizada llamada NegativeNumberError que se lanza cuando se intenta calcular la raíz cuadrada de un número negativo.
class NegativeNumberError < Exception
end
def square_root(x)
raise NegativeNumberError, "No se puede calcular la raíz cuadrada de un número negativo" if x < 0
Math.sqrt(x)
end
begin
puts square_root(-1)
rescue NegativeNumberError => e
puts "Se ha producido un error: #{e.message}"
end
Pruebas y Debugging
En Ruby, podemos realizar pruebas unitarias utilizando la biblioteca MiniTest o RSpec. También podemos utilizar las gemas pry y byebug para realizar debugging en nuestro código.
require 'minitest/autorun'
class NegativeNumberError < StandardError; end
def square_root(number)
raise NegativeNumberError if number.negative?
Math.sqrt(number)
end
class TestSquareRoot < Minitest::Test
def test_square_root
2, square_root(4)
assert_equal 0, square_root(0)
assert_equal NegativeNumberError do
assert_raises -1)
square_root(end
end
end
En el ejemplo anterior, creamos una prueba unitaria utilizando la biblioteca MiniTest para la función square_root. La prueba verifica que se lanza una excepción NegativeNumberError cuando se intenta calcular la raíz cuadrada de un número negativo.
Ejemplo práctico: Crear una prueba unitaria para la función square_root que verifica que se lanza una excepción NegativeNumberError cuando se intenta calcular la raíz cuadrada de un número negativo.
require 'minitest/autorun'
class NegativeNumberError < StandardError
end
def square_root(number)
raise NegativeNumberError if number < 0
Math.sqrt(number)
end
class TestSquareRoot < Minitest::Test
def test_square_root
2, square_root(4)
assert_equal 0, square_root(0)
assert_equal NegativeNumberError do
assert_raises -1)
square_root(end
end
end
En el ejemplo anterior, creamos una prueba unitaria utilizando la biblioteca MiniTest para la función square_root. La prueba verifica que se lanza una excepción NegativeNumberError cuando se intenta calcular la raíz cuadrada de un número negativo.
Ejercicios Prácticos
- Crear una excepción personalizada llamada InvalidEmailError que se lanza cuando se intenta crear un objeto Email con una dirección de correo electrónico inválida.
Ver respuesta
class InvalidEmailError < Exception
end
En el ejercicio anterior, creamos una excepción personalizada llamada InvalidEmailError que hereda de la clase Exception.
- Crear una clase Email que tiene un atributo address que representa la dirección de correo electrónico. La clase debe tener un constructor que recibe la dirección de correo electrónico y lanza una excepción InvalidEmailError si la dirección de correo electrónico es inválida.
Ver respuesta
class Email
attr_reader :address
def initialize(address)
raise InvalidEmailError, "Dirección de correo electrónico inválida" unless address.match?(/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i)
@address = address
end
end
begin
= Email.new("
email puts email.address
rescue InvalidEmailError => e
puts "Se ha producido un error: #{e.message}"
end
En el ejercicio anterior, creamos una clase Email que tiene un atributo address que representa la dirección de correo electrónico. El constructor de la clase lanza una excepción InvalidEmailError si la dirección de correo electrónico es inválida.
- Crear una prueba unitaria para la clase Email que verifica que se lanza una excepción InvalidEmailError cuando se intenta crear un objeto Email con una dirección de correo electrónico inválida.
Ver respuesta
require 'minitest/autorun'
class InvalidEmailError < StandardError; end
class Email
attr_reader :address
def initialize(address)
raise InvalidEmailError, "Dirección de correo electrónico inválida" unless address.match?(/\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i)
@address = address
end
end
class TestEmail < Minitest::Test
def test_email
InvalidEmailError do
assert_raises Email.new("invalid_email")
end
end
end
En el ejercicio anterior, creamos una prueba unitaria utilizando la biblioteca MiniTest para la clase Email. La prueba verifica que se lanza una excepción InvalidEmailError cuando se intenta crear un objeto Email con una dirección de correo electrónico inválida.
- Crear una excepción personalizada llamada InvalidPasswordError que se lanza cuando se intenta crear un objeto Password con una contraseña inválida.
Ver respuesta
class InvalidPasswordError < Exception
end
En el ejercicio anterior, creamos una excepción personalizada llamada InvalidPasswordError que hereda de la clase Exception.
- Crear una clase Password que tiene un atributo value que representa la contraseña. La clase debe tener un constructor que recibe la contraseña y lanza una excepción InvalidPasswordError si la contraseña es inválida.
Ver respuesta
class Password
attr_reader :value
def initialize(value)
raise InvalidPasswordError, "Contraseña inválida" unless value.match?(/\A(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{8,}\z/)
@value = value
end
end
begin
= Password.new("password")
password puts password.value
rescue InvalidPasswordError => e
puts "Se ha producido un error: #{e.message}"
end
En el ejercicio anterior, creamos una clase Password que tiene un atributo value que representa la contraseña. El constructor de la clase lanza una excepción InvalidPasswordError si la contraseña es inválida.
Conclusiones
En esta sección aprendimos a manejar errores y excepciones en Ruby. Vimos cómo podemos rescatar excepciones y cómo podemos crear nuestras propias excepciones personalizadas. También aprendimos a realizar pruebas unitarias en Ruby y a utilizar herramientas de debugging para encontrar errores en nuestro código.