Accessing Error Messages within a Rescue Block - ruby

Is there any way to get access to an error message in a rescue block as a string? For example:
def foo
raise RuntimeError, "This is an error"
end
def bar
begin
foo
rescue RuntimeError
puts "Rescued"
end
end
bar
Is there any way to gain access to "This is an error" from with-in the rescue block? Something like this:
...
rescue RuntimeError
puts <error-message>
end
...

You need to specify a variable to store the error in
def foo
raise RuntimeError, "This is an error"
end
def bar
begin
foo
rescue RuntimeError => ex
puts "Rescued #{ex.message}"
end
end

Related

Rspec testing raising and rescue of method

Is there a way to rspec test if an error was raised and rescued? If I have a rescue, my rspec test does not see the raised error, only results in the rescue?
module MyApp
def some_method(msg)
raise StandardError.new(msg)
end
def second_method(msg)
begin
count = 0
some_method(msg)
rescue StandardError=> e
puts e
count = 1
end
end
end
RSpec.describe Myapp do
describe "#some_method" do
it "should raise error" do
expect {
some_method("this is an error")
}.to raise_error(StandardError) {|e|
expect(e.message).to eql "this is an error"
}
end
end
# this fails, as the error is not raised
describe "#second_method" do
it should raise error and rescue do
expect {
a = second_method("this is an error and rescue")
}.to raise_error(StandardError) {|e|
expect(e.message).to eql "this is an error and rescue"
expect(a) = 1
}
end
end
end
You generally don't want to raise or rescue StandardError directly because it's pretty uninformative, and won't catch errors outside of the StandardError hierarchy. Instead, you generally want to test that a specific exception was raised, or that a specific error class or error message was raised.
If you know the custom or built-in exception class that you want, or the specific error message, then test for that explicitly. For example:
it 'should raise an ArgumentError exception' do
expect { MyApp.new.foo }.to raise_error(ArgumentError)
end
it 'should raise MyCustomError' do
expect { MyApp.new.foo }.to raise_error(MyCustomError)
end
it 'should raise StandardError with a custom message' do
msg = 'this is a custom error and rescue'
expect { MyApp.new.foo }.to raise_error(msg)
end
If you don't know (or care about) the specific exception or message that should be raised, but you expect some exception to interrupt the execution flow, then you should use a bare raise_error matcher. For example:
it "should raise an exception" do
expect { MyApp.new.foo }.to raise_error
end

Rescue NameError but not NoMethodError

I need to catch a NameError in a special case. But I don't want to catch all SubClasses of NameError. Is there a way to achieve this?
# This shall be catched
begin
String::NotExistend.new
rescue NameError
puts 'Will do something with this error'
end
# This shall not be catched
begin
# Will raise a NoMethodError but I don't want this Error to be catched
String.myattribute = 'value'
rescue NameError
puts 'Should never be called'
end
You can also do it in a more traditional way
begin
# your code goes here
rescue NoMethodError
raise
rescue NameError
puts 'Will do something with this error'
end
You can re-raise exception if its class is different than a given:
begin
# your code goes here
rescue NameError => exception
# note that `exception.kind_of?` will not work as expected here
raise unless exception.class.eql?(NameError)
# handle `NameError` exception here
end
You can also check the exception message and decide what to do.
Here is an example using the code you provided.
# This shall be catched
begin
String::NotExistend.new
rescue NameError => e
if e.message['String::NotExistend']
puts 'Will do something with this error'
else
raise
end
end
# This shall not be catched
begin
# Will raise a NoMethodError but I don't want this Error to be catched
String.myattribute = 'value'
rescue NameError => e
if e.message['String::NotExistend']
puts 'Should never be called'
else
raise
end
end

how to access instant variable with rspec

I have a class like this.
require 'net/http'
class Foo
def initialize
#error_count = 0
end
def run
result = Net::HTTP.start("google.com")
#error_count = 0 if result
rescue
#error_count += 1
end
end
And I want to count up #error_count if connection fails, so I wrote like this.
require_relative 'foo'
describe Foo do
before(:each){#foo = Foo.new}
describe "#run" do
context "when connection fails" do
before(:each){ Net::HTTP.stub(:start).and_raise }
it "should count up #error_count" do
expect{ #foo.run }.to change{ #foo.error_count }.from(0).to(1)
end
end
end
end
Then I got this error.
NoMethodError:
undefined method `error_count' for #<Foo:0x007fc8e20dcbd8 #error_count=0
How can I access instance variable with Rspec?
Edit
describe Foo do
let(:foo){ Foo.new}
describe "#run" do
context "when connection fails" do
before(:each){ Net::HTTP.stub(:start).and_raise }
it "should count up #error_count" do
expect{ foo.run }.to change{foo.send(:error_count)}.from(0).to(1)
end
end
end
end
Try #foo.send(:error_count) i guess it should work.
Update: found in docs
expect{ foo.run }.to change{foo.instance_variable_get(:#error_count)}.from(0).to(1)

Error handling: How to throw/catch errors correctly

I have a method that calls two other methods:
def first_method
second_method
# Don´t call this method when something went wrong before
third_method
end
The second_method calls other methods:
def second_method
fourth_method
fifth_method
end
Let´s say the fifth_method has a begin/rescue statement:
def fifth_method
begin
# do_something
rescue Error => e
#
end
end
Now I want to avoid third_method to be called when fifth_method throws an error. How would I/you solve this most elegantly in Ruby.
It seems to me so obvious but anyway
def first_method
begin
second_method
rescue
return
end
third_method
end
This construction (without explicit type of exception) will catch StandartError exception.
To avoid intersection with another exceptions you can create your own exception class:
class MyError < StandardError; end
and then use it
begin
second_method
rescue MyError => e
return
end
Note that you should not inherit exception from Exception because this type of exceptions are from environment level, where the exceptions of StandardError are meant to deal with application level errors.
I think the simplest way is removing error catching from fifth_method and move it to the first_method
def first_method
begin
second_method
third_method
rescue Error => e
end
end
def fifth_method
# do_something
end
If you don't want to use exceptions, you can just return a status:
def fifth_method
# do_something
true
rescue Error => e
false
end
def first_method
if second_method
third_method
end
end

Is SystemExit a special kind of Exception?

How does SystemExit behave differently from other Exceptions? I think I understand some of the reasoning about why it wouldn't be good to raise a proper Exception. For example, you wouldn't want something strange like this to happen:
begin
exit
rescue => e
# Silently swallow up the exception and don't exit
end
But how does the rescue ignore SystemExit? (What criteria does it use?)
When you write rescue without one or more classes, it is the same as writing:
begin
...
rescue StandardError => e
...
end
There are Exceptions that do not inherit from StandardError, however. SystemExit is one of these, and so it is not captured. Here is a subset of the hierarchy in Ruby 1.9.2, which you can find out yourself:
BasicObject
Exception
NoMemoryError
ScriptError
LoadError
Gem::LoadError
NotImplementedError
SyntaxError
SecurityError
SignalException
Interrupt
StandardError
ArgumentError
EncodingError
Encoding::CompatibilityError
Encoding::ConverterNotFoundError
Encoding::InvalidByteSequenceError
Encoding::UndefinedConversionError
FiberError
IOError
EOFError
IndexError
KeyError
StopIteration
LocalJumpError
NameError
NoMethodError
RangeError
FloatDomainError
RegexpError
RuntimeError
SystemCallError
ThreadError
TypeError
ZeroDivisionError
SystemExit
SystemStackError
fatal
You can thus capture just SystemExit with:
begin
...
rescue SystemExit => e
...
end
...or you can choose to capture every exception, including SystemExit with:
begin
...
rescue Exception => e
...
end
Try it yourself:
begin
exit 42
puts "No no no!"
rescue Exception => e
puts "Nice try, buddy."
end
puts "And on we run..."
#=> "Nice try, buddy."
#=> "And on we run..."
Note that this example will not work in (some versions of?) IRB, which supplies its own exit method that masks the normal Object#exit.
In 1.8.7:
method :exit
#=> #<Method: Object(IRB::ExtendCommandBundle)#exit>
In 1.9.3:
method :exit
#=> #<Method: main.irb_exit>
Simple example:
begin
exit
puts "never get here"
rescue SystemExit
puts "rescued a SystemExit exception"
end
puts "after begin block"
The exit status / success?, etc. can be read too:
begin
exit 1
rescue SystemExit => e
puts "Success? #{e.success?}" # Success? false
end
begin
exit
rescue SystemExit => e
puts "Success? #{e.success?}" # Success? true
end
Full list of methods: [:status, :success?, :exception, :message, :backtrace, :backtrace_locations, :set_backtrace, :cause]

Resources