Exception Handling
What are Exceptions?
Exceptions are Ruby's way of handling errors and unexpected situations during program execution.
Basic Exception Handling
begin
# Code that might raise an exception
result = 10 / 0
rescue
puts "An error occurred"
end
Specific Exception Types
begin
file = File.open("nonexistent.txt")
rescue Errno::ENOENT
puts "File not found"
rescue Errno::EACCES
puts "Permission denied"
end
Exception Objects
begin
1 / 0
rescue ZeroDivisionError => e
puts "Error: #{e.message}"
puts "Backtrace: #{e.backtrace.first}"
end
Multiple Rescue Clauses
begin
# risky code
rescue ZeroDivisionError
puts "Division by zero"
rescue ArgumentError
puts "Invalid argument"
rescue => e
puts "Other error: #{e.message}"
end
Ensure Clause
Code that always runs, regardless of exceptions:
file = nil
begin
file = File.open("example.txt", "w")
file.write("Hello, World!")
rescue IOError => e
puts "IO Error: #{e.message}"
ensure
file.close if file
puts "File operation completed"
end
Else Clause
Code that runs only if no exceptions occur:
begin
result = 10 / 2
rescue ZeroDivisionError
puts "Division by zero"
else
puts "Division successful: #{result}"
ensure
puts "Operation completed"
end
Raising Exceptions
def divide(a, b)
raise ArgumentError, "Division by zero" if b == 0
a / b
end
begin
divide(10, 0)
rescue ArgumentError => e
puts e.message # "Division by zero"
end
Custom Exceptions
class CustomError < StandardError
end
class ValidationError < StandardError
attr_reader :field
def initialize(field, message)
@field = field
super(message)
end
end
def validate_user(user)
raise ValidationError.new(:email, "Invalid email") unless user[:email] =~ /\A[^@\s]+@[^@\s]+\z/
end
begin
validate_user({email: "invalid"})
rescue ValidationError => e
puts "Validation failed for #{e.field}: #{e.message}"
end
Retry
attempts = 0
begin
attempts += 1
# Code that might fail
1 / 0
rescue ZeroDivisionError
retry if attempts < 3
puts "Failed after #{attempts} attempts"
end
Exception Hierarchy
Exception
├── NoMemoryError
├── ScriptError
│ ├── LoadError
│ ├── NotImplementedError
│ └── SyntaxError
├── SecurityError
├── SignalException
├── StandardError
│ ├── ArgumentError
│ ├── IOError
│ │ ├── EOFError
│ │ └── Errno::*
│ ├── IndexError
│ │ └── StopIteration
│ ├── LocalJumpError
│ ├── NameError
│ │ └── NoMethodError
│ ├── RangeError
│ │ └── FloatDomainError
│ ├── RegexpError
│ ├── RuntimeError
│ ├── SystemCallError
│ │ └── Errno::*
│ ├── ThreadError
│ ├── TypeError
│ └── ZeroDivisionError
├── SystemExit
└── SystemStackError
Best Practices
Rescue Specific Exceptions
# Bad
begin
# code
rescue
# catches everything
end
# Good
begin
# code
rescue StandardError => e
# handles expected errors
end
Use Ensure for Cleanup
# Bad
begin
file = File.open("file.txt")
# process file
rescue
# handle error
end
file.close
# Good
begin
file = File.open("file.txt")
# process file
rescue
# handle error
ensure
file.close if file
end
Don't Suppress Exceptions
# Bad - silently ignores errors
begin
risky_operation
rescue
# do nothing
end
# Good - log and handle appropriately
begin
risky_operation
rescue => e
logger.error("Operation failed: #{e.message}")
raise # re-raise if needed
end
Create Meaningful Custom Exceptions
class PaymentError < StandardError
attr_reader :payment_id
def initialize(payment_id, message = "Payment failed")
@payment_id = payment_id
super(message)
end
end
Use Exception for Flow Control Sparingly
Exceptions should be for exceptional situations, not normal flow control.
Exception Safety
class SafeArray
def initialize
@data = []
end
def [](index)
raise IndexError, "Index out of bounds" if index < 0 || index >= @data.length
@data[index]
end
def []=(index, value)
raise IndexError, "Index out of bounds" if index < 0
@data[index] = value
end
end
Logging Exceptions
require 'logger'
logger = Logger.new(STDOUT)
begin
# risky code
rescue => e
logger.error("Exception occurred: #{e.class.name}: #{e.message}")
logger.error(e.backtrace.join("\n"))
end
Proper exception handling is crucial for building robust Ruby applications. Always catch specific exceptions, clean up resources in ensure blocks, and provide meaningful error messages.
