We can put a class or module after a rescue statement, but in the code below, I see a method following rescue, which does not fit into this pattern. How is it working and how is it producing the output it has been designed to show?
def errors_with_message(pattern)
# Generate an anonymous "matcher module" with a custom threequals
m = Module.new
(class << m; self; end).instance_eval do
define_method(:===) do |e|
pattern === e.message
end
end
m
end
puts "About to raise"
begin
raise "Timeout while reading from socket"
rescue errors_with_message(/socket/)
puts "Ignoring socket error"
end
puts "Continuing..."
Output
About to raise
Ignoring socket error
Continuing...
Rescue requires a class or a module, true. So, that method creates an anonymous module with special behaviour. You see, when rescue searches for a handler, it applies === operator to exception classes/modules you provided, passing as an argument the actual exception.
begin
# do something
rescue MyCustomError
# process your error
rescue StandardError
# process standard error
end
So, if StandardError (or one of its descendants) was raised, the first handler will be skipped and second handler will be matched.
Now, the module from errors_with_message is special. It redefines threequals operator to match on exception message. So, if an error was raised and its message contains word "socket", this handler will match. Cool trick, huh?
Related
Here's what I'm trying to do:
class Foo
def foo
raise "lol noob"
end
# ... many other methods here ...
rescue StandardError => e
puts "error caught: #{e}"
end
Foo.new.foo
RuntimeError: lol noob
from (pry):45:in `foo'
As you can see, this does not work.
What I'm trying to avoid is to put a rescue block into every single method, given that they're many. Is it possible? If not, what's the best practice?
TL;DR
In Ruby, you generally have to wrap the caller with rescue, rather than the receiver.
Explanation
It's likely that you're finding this behavior surprising because you're not thinking of classes as executable code. A rescue clause in a class definition will capture exceptions raised while the class is being interpreted. Consider the following example:
class Foo
raise 'bar'
rescue
'Rescued!'
end
#=> "Rescued!"
Here, the rescue clause works because the exception is raised while the class code is executing. However, some Foo#bar method wouldn't normally get rescued by this clause (except possibly while being defined) because Foo is no longer the caller, it's the receiver.
A rescue clause on the class will catch exceptions raised when the class itself is being defined. However, to rescue within a method at run-time, you need to rescue within the caller (in this case, the #bar method) as follows:
class Foo
def bar
raise 'method exception'
rescue
'Rescued method exception.'
end
end
Foo.new.bar
#=> "Rescued method exception."
It would make no sense to have a common rescue block for a class, each method does different things right? How would you handle the variety of errors all in the same way? Error handling is just as much part of the logic as the "main" part of the method(s).
There's no silver bullet here, you rescue where you need to, and do what is needed when it is needed.
The Exceptional Ruby book may be of interest if you want to learn some common best practices.
If you're absolutely sure that the only error handling you need to do is log out errors, you can abstract away some of the repetitive error logging by defining a helper method that takes in a block and do your error handling there.
For example,
class Foo
def foo
handle_exception do
raise "lol noob"
end
end
def bar
handle_exception do
raise "rofl"
end
end
def handle_exception
yield
rescue => e
puts "error caught: #{e}"
end
end
Foo.new.foo # error caught: lol noob
Foo.new.bar # error caught: rofl
This has the benefit that later on you decide that want some alternate behavior, ie adding in a backtrace, you only have to touch one line of code:
def handle_exception
yield
rescue => e
puts "error caught: #{e}\n#{e.backtrace.join("\n")}"
end
I am trying to get used to testing my code with simple driver snippets and want to test whether an Argument Error is thrown without escaping out of the program. Here is the code I am using
class Die
def initialize(sides)
#sides=sides
unless #sides>0
raise ArgumentError.new("Your number sucks, yo")
end
end
#returns the number of sides of a die
def sides
#sides
end
#generates a random die roll based on the number of sides
def roll
rand(#sides)+1
end
end
And here is what I am attempting to call to get a test.
p bad=Die.new(0)=="Your number sucks, yo"
What I want it to return is "true". What it does return in the terminal is :
w3p1_refact.rb:33:in `initialize': Your number sucks, yo (ArgumentError)
from w3p1_refact.rb:69:in `new'
from w3p1_refact.rb:69:in `<main>'
Can I rewrite this to return what I am looking for?
From the documentation of Exception
When an exception has been raised but not yet handled (in rescue, ensure, at_exit and END blocks) the global variable $! will contain the current exception and $# contains the current exception’s backtrace.
So once I have just raised exception in the $! global variable, I can use Exception#message method, which returns the exception’s message or name.
You use Kernel#raise
With no arguments, raises the exception in $! or raises a RuntimeError if $! is nil. With a single String argument, raises a RuntimeError with the string as a message. Otherwise, the first parameter should be the name of an Exception class (or an object that returns an Exception object when sent an exception message). The optional second parameter sets the message associated with the exception, and the third parameter is an array of callback information. Exceptions are caught by the rescue clause of begin...end blocks.
I would do as below :
class Die
def initialize(sides)
#sides=sides
unless #sides>0
raise ArgumentError.new("Your number sucks, yo")
# As per the doc you could write the above line as below also
# raise ArgumentError, "Your number sucks, yo"
end
end
#returns the number of sides of a die
def sides
#sides
end
#generates a random die roll based on the number of sides
def roll
rand(#sides)+1
end
end
Die.new(0) rescue $!.message == "Your number sucks, yo"
# => true
The above inline rescue code can be written as also :
begin
Die.new(0)
rescue ArgumentError => e
bad = e.message
end
bad == "Your number sucks, yo" # => true
I have seen Ruby code that raises exceptions using a class:
raise GoatException, "Maximum of 3 goats per bumper car."
Other code uses an instance:
raise GoatException.new "No leotard found suitable for goat."
Both of these are rescued the same way. Is there any reason to use an instance vs a class?
It makes no difference; the exception class will be instiantiated in either case.
If you provide a string, either as the argument to new or as the second argument to raise, it be passed to initialize and will become the exception instance's .message.
For example:
class GoatException < StandardError
def initialize(message)
puts "initializing with message: #{message}"
super
end
end
begin
raise GoatException.new "Goats do not enjoy origami." #--|
# | Equivilents
raise GoatException, "Goats do not enjoy origami." #--|
rescue Exception => e
puts "Goat exception! The class is '#{e.class}'. Message is '#{e.message}'"
end
If you comment the first raise above, you'll see that:
In both cases, initialize is called.
In both cases, the exception class is GoatException, not class as it would be if we were rescuing the exception class itself.
I don't understand why this code is not working properly:
def test
raise Exception.new 'error'
end
def caller
begin
test
rescue =>e
puts e.message
end
end
caller
I want to catch the test exception in the caller method, but it seems that the caller method is not catching anything.
The reason your code doesn't work is because rescue without an explicitly stated exception type only catches StandardError, which is a subclass of Exception. Since you're raising Exception, which is higher than StandardError, your rescue doesn't catch it.
Generally you want to use more specific exceptions, and you almost never need to use Exception over StandardError.
This, for example, works fine:
begin
raise StandardError.new 'Uh-oh!'
rescue => e
p e.message
end
#=> Uh-oh!
You should specify what kind of exceptions you'd like to rescue. Try
rescue Exception => e
Jan beat me to it, but...
When you use the => var syntax with exception, you must specify what kind of exception you want to rescue. The base class for all exceptions is Exception, so it will work if you change that to rescue Exception => e. Also, when what you're rescuing from is the entire body of a method, you don't need an explicit begin...end block...
def test
raise Exception.new 'error'
end
def caller
test
rescue Exception =>e
puts e.message
end
caller()
There seems to be an odd discrepancy between the messages contained in Ruby Exceptions raised directly and raised from within evals. For instance, the following code:
def foo
raise "Help!"
end
puts "\nRescue foo"
begin
foo
rescue RuntimeError => e
puts e.message
end
puts "\nRescue eval 'foo'"
begin
eval "foo"
rescue RuntimeError => e
puts e.message
end
Produces the following output:
Rescue foo
Help!
Rescue eval 'foo'
./temp.rb:2:in `foo': Help!
Short of using regexps to sub it out, is there any way I can raise the exception without the context in the second case?
Thanks. I was defining my own error anyway, so that's an easy fix.
I've made a slight change, so that the superclass is initialized as well:
class MyException < RuntimeError
attr_accessor :my_message
def initialize(m)
#my_message = String.new(m)
super
end
end
(the String.new call seems to be needed to avoid getting the old behaviour again; presumably Exception.new modifies the message in-place.)
That is unusual, I've not come across that before. I can't see a way of persuading eval not to add that information, so either you do the regexp munging you mentioned, or you can define your own error type:
class MyError < RuntimeError
attr_accessor :my_message
def initialize(m)
#my_message = m.dup
super
end
end
def foo
raise MyError.new("Help!")
end
puts "\nRescue eval 'foo'"
begin
eval "foo"
rescue RuntimeError => e
puts e.my_message
end
With output:
Rescue eval 'foo'
Help!
In anything larger than a simple script, defining your own error types is good practice anyway.
(updated to fix code in line with what Chris said in his answer)