errors/exception_chaining.rb
tests/_examples/errors/exception_chaining.rb in the metorex repository.
# Exception Chaining Example
# Demonstrates exception cause chains and tracking the source of errors
# Example 1: Simple exception chain - catching and re-raising
puts "Example 1: Catching and re-raising"
class DatabaseError < StandardError
end
class NetworkError < StandardError
end
def connect_to_database
raise NetworkError.new("Network connection failed")
end
def initialize_system
begin
connect_to_database()
rescue NetworkError => e
puts "Caught NetworkError: #{e.message}"
puts "Re-raising as DatabaseError..."
raise DatabaseError.new("Database initialization failed")
end
end
begin
initialize_system()
rescue DatabaseError => e
puts "Caught DatabaseError: #{e.message}"
end
# Example 2: Multiple levels of exception handling
puts ""
puts "Example 2: Multi-level exception handling"
def level_1
raise RuntimeError.new("Error at level 1")
end
def level_2
begin
level_1()
rescue RuntimeError => e
puts "Level 2 caught: #{e.message}"
raise TypeError.new("Type error in level 2")
end
end
def level_3
begin
level_2()
rescue TypeError => e
puts "Level 3 caught: #{e.message}"
raise ArgumentError.new("Value error in level 3")
end
end
begin
level_3()
rescue ArgumentError => e
puts "Top level caught: #{e.message}"
end
# Example 3: Using $! to access current exception
puts ""
puts "Example 3: Accessing current exception with $!"
begin
raise RuntimeError.new("Original error")
rescue => e
puts "Caught exception: #{e.message}"
puts "Exception binding and $! both reference the current exception"
end
# Example 4: Preserving error context through layers
puts ""
puts "Example 4: Error context preservation"
class ConfigError < StandardError
end
class FileError < StandardError
end
def read_config_file
raise FileError.new("config.txt not found")
end
def load_configuration
begin
read_config_file()
rescue FileError => e
puts "File error occurred: #{e.message}"
raise ConfigError.new("Failed to load configuration")
end
end
def start_application
begin
load_configuration()
rescue ConfigError => e
puts "Configuration error: #{e.message}"
puts "Application cannot start"
end
end
start_application()
# Example 5: Conditional re-raising
puts ""
puts "Example 5: Conditional re-raising"
def risky_operation(should_recover)
begin
raise RuntimeError.new("Something went wrong")
rescue RuntimeError => e
if should_recover
puts "Recovered from error: #{e.message}"
else
puts "Cannot recover, re-raising..."
raise
end
end
end
risky_operation(true)
begin
risky_operation(false)
rescue RuntimeError => e
puts "Caught re-raised error: #{e.message}"
end
Output:
Example 1: Catching and re-raising
Caught NetworkError: Network connection failed
Re-raising as DatabaseError...
Caught DatabaseError: Database initialization failed
Example 2: Multi-level exception handling
Level 2 caught: Error at level 1
Level 3 caught: Type error in level 2
Top level caught: Value error in level 3
Example 3: Accessing current exception with $!
Caught exception: Original error
Exception binding and $! both reference the current exception
Example 4: Error context preservation
File error occurred: config.txt not found
Configuration error: Failed to load configuration
Application cannot start
Example 5: Conditional re-raising
Recovered from error: Something went wrong
Cannot recover, re-raising...
Caught re-raised error: Something went wrong