Lesson 11 / 25
Exceptions and Error Handling
Raise, rescue, retry and ensure, and design custom exception classes.
Handling failure gracefully
Raise exceptions with raise: raise ArgumentError, "amount must be positive". Handle them with begin ... rescue ... else ... ensure ... end, or directly in a method body: rescue clauses match exception classes (most specific first), else runs when nothing was raised, and ensure always runs, for cleanup. retry re-runs the begin block, useful with a counter for transient network errors. Rescue StandardError (what a bare rescue catches) or specific subclasses, never Exception, which also includes system signals and SystemExit, making programs hard to stop. Define custom exceptions by subclassing StandardError, adding attributes for context, and group them under a base class for your library (PaymentError < StandardError). Methods with ! commonly raise on failure while their plain versions return nil or false (save! versus save in Rails). Use ensure or block-based APIs (File.open with a block) so resources are always released. Inline rescue modifiers (value = risky rescue nil) hide all errors and should be avoided.
Custom errors, retry and ensure
Specific rescues, limited retries and guaranteed cleanup.
class PaymentError < StandardError; end
class PaymentDeclined < PaymentError
attr_reader :reason
def initialize(reason)
@reason = reason
super("Payment declined: #{reason}")
end
end
class GatewayTimeout < PaymentError; end
def charge(amount, token)
attempts = 0
begin
attempts += 1
gateway_call(amount, token)
rescue GatewayTimeout
retry if attempts < 3 # transient: try again
raise
rescue PaymentDeclined => e
puts "Declined (#{e.reason}); not retrying"
nil
else
puts "Charged on attempt #{attempts}"
ensure
puts "payment attempt finished" # always runs
end
end
def gateway_call(amount, token)
raise ArgumentError, "amount must be positive" unless amount.positive?
raise PaymentDeclined, "card blocked" if token.start_with?("blocked")
"pay_123"
endA safety net with a cleanup crew
rescue is the safety net that catches specific falls, retry lets the performer try the jump again a limited number of times, and ensure is the cleanup crew that sweeps the stage whatever happened.
Quick check: Why should you rescue StandardError rather than Exception?
- Exception does not exist
- Exception also catches signals and SystemExit, making programs hard to interrupt or exit
- StandardError is faster
- Ruby forbids rescuing Exception
Answer
Exception also catches signals and SystemExit, making programs hard to interrupt or exit — Rescuing Exception swallows interrupts and exit requests, not just application errors.