Exception handling is a crucial part of any programming language, and Ruby is no exception. It allows you to manage errors gracefully and ensure that your program can handle unexpected situations without crashing.
Key Concepts
- Exceptions: An exception is an event that disrupts the normal flow of a program. In Ruby, exceptions are objects that are instances of the
Exceptionclass or its subclasses. - Raising Exceptions: You can raise exceptions using the
raisekeyword. - Rescuing Exceptions: You can handle exceptions using the
begin-rescue-endblock. - Ensuring Execution: The
ensureblock allows you to run code regardless of whether an exception was raised or not. - Custom Exceptions: You can define your own exception classes by inheriting from the
StandardErrorclass.
Raising Exceptions
You can raise an exception using the raise keyword. Here’s a simple example:
def divide(a, b)
raise "Division by zero error" if b == 0
a / b
end
begin
puts divide(10, 0)
rescue => e
puts "An error occurred: #{e.message}"
endExplanation
raise "Division by zero error": Raises an exception with the message "Division by zero error" ifbis zero.begin-rescue-endblock: This block is used to handle exceptions. If an exception is raised within thebeginblock, therescueblock will catch it.rescue => e: Captures the exception object in the variablee.e.message: Retrieves the error message from the exception object.
Rescuing Specific Exceptions
You can rescue specific exceptions by specifying the exception class:
begin
File.open("non_existent_file.txt")
rescue Errno::ENOENT => e
puts "File not found: #{e.message}"
endExplanation
rescue Errno::ENOENT => e: This line rescues onlyErrno::ENOENTexceptions, which are raised when a file is not found.
Ensuring Execution
The ensure block runs code regardless of whether an exception was raised or not:
begin
file = File.open("example.txt", "w")
file.write("Hello, world!")
rescue => e
puts "An error occurred: #{e.message}"
ensure
file.close if file
endExplanation
ensureblock: Ensures that the file is closed whether an exception occurs or not.
Custom Exceptions
You can create custom exceptions by subclassing StandardError:
class CustomError < StandardError; end
def risky_method
raise CustomError, "Something went wrong!"
end
begin
risky_method
rescue CustomError => e
puts "Caught a custom error: #{e.message}"
endExplanation
class CustomError < StandardError; end: Defines a custom exception class.raise CustomError, "Something went wrong!": Raises an instance of the custom exception.rescue CustomError => e: Catches the custom exception.
Practical Exercises
Exercise 1: Basic Exception Handling
Write a method safe_divide that takes two arguments and returns their division. If the second argument is zero, it should raise an exception with the message "Cannot divide by zero".
def safe_divide(a, b)
# Your code here
end
begin
puts safe_divide(10, 2) # Should print 5
puts safe_divide(10, 0) # Should raise an exception
rescue => e
puts "Error: #{e.message}"
endSolution
def safe_divide(a, b)
raise "Cannot divide by zero" if b == 0
a / b
end
begin
puts safe_divide(10, 2) # Should print 5
puts safe_divide(10, 0) # Should raise an exception
rescue => e
puts "Error: #{e.message}"
endExercise 2: Custom Exception
Create a custom exception NegativeNumberError and write a method square_root that takes a number and returns its square root. If the number is negative, it should raise the custom exception.
class NegativeNumberError < StandardError; end
def square_root(number)
# Your code here
end
begin
puts square_root(9) # Should print 3.0
puts square_root(-1) # Should raise NegativeNumberError
rescue NegativeNumberError => e
puts "Error: #{e.message}"
endSolution
class NegativeNumberError < StandardError; end
def square_root(number)
raise NegativeNumberError, "Cannot take the square root of a negative number" if number < 0
Math.sqrt(number)
end
begin
puts square_root(9) # Should print 3.0
puts square_root(-1) # Should raise NegativeNumberError
rescue NegativeNumberError => e
puts "Error: #{e.message}"
endCommon Mistakes and Tips
- Not rescuing specific exceptions: Always try to rescue specific exceptions rather than using a generic
rescueclause. This makes your code more robust and easier to debug. - Forgetting the
ensureblock: Use theensureblock to release resources like file handles or database connections. - Raising exceptions unnecessarily: Only raise exceptions for truly exceptional conditions. Overusing exceptions can make your code harder to read and maintain.
Conclusion
In this section, you learned how to handle exceptions in Ruby using the raise, rescue, and ensure keywords. You also learned how to create custom exceptions to handle specific error conditions. Exception handling is a powerful tool that helps you write robust and error-resistant code. In the next section, we will dive into file input/output operations in Ruby.
Ruby Programming Course
Module 1: Introduction to Ruby
Module 2: Basic Ruby Concepts
Module 3: Working with Collections
Module 4: Object-Oriented Programming in Ruby
- Classes and Objects
- Instance Variables and Methods
- Class Variables and Methods
- Inheritance
- Modules and Mixins
Module 5: Advanced Ruby Concepts
Module 6: Ruby on Rails Introduction
- What is Ruby on Rails?
- Setting Up Rails Environment
- Creating a Simple Rails Application
- MVC Architecture
- Routing
Module 7: Testing in Ruby
- Introduction to Testing
- Unit Testing with Minitest
- Behavior-Driven Development with RSpec
- Mocking and Stubbing
