How to do something upon error in Ruby? - ruby

Say I do .operator on an object and .operator produces an error, how can I not have the program terminate and instead do something else.

Ruby provides exception handling via begin , rescue blocks. you may do the following
exception_array = []
begin
array.operator
rescue Exception => e
exception_array << e
ensure
# this line will execute irrespective of an
# error happened or not
end
Lets breakdown the commands and the program flow
First your program try to execute the command in begin block. So if it raise any error, then it goes to rescue section
Exception is there to catch any type of exception. But you can specify the exception type you want too. Following is the ruby exception hierarchy from nicksiegers blog
Exception
NoMemoryError
ScriptError
LoadError
NotImplementedError
SyntaxError
SignalException
Interrupt
StandardError
ArgumentError
IOError
EOFError
IndexError
StopIteration
LocalJumpError
NameError
NoMethodError
RangeError
FloatDomainError
RegexpError
RuntimeError
SecurityError
SystemCallError
SystemStackError
ThreadError
TypeError
ZeroDivisionError
SystemExit
fatal
So depending on the error type you can catch a specific error type if you want to
=> e part is assigning your error to a variable called e. You can rename this to anything you want.
The last part is ensure and this is optional, Ensure will execute no matter you get an error or not. So if you have something to get done, if an error happens or not, you can use ensure
This is a good read on ruby error handling

You can do this by catching exceptions in rescue block.
begin
array.operator
# Change the class according to the Exception you are getting
rescue StandardError => e
exceptions ||= []
exceptions << e
end

Related

How do I prevent two exception stacks when catching and re-raising Ruby exceptions?

In a larger project, I am catching specific exceptions, adding some details to the message and then re-raising the same exception. Here is the basic idea of what I'm doing.
#!/usr/bin/env ruby
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.exception("new message #{e.message}")
end
When I execute temp.rb from the command line, two exceptions are printed:
% ./temp.rb
./temp.rb:4:in `/': new message divided by 0 (ZeroDivisionError)
from ./temp.rb:4:in `<main>'
./temp.rb:4:in `/': divided by 0 (ZeroDivisionError)
from ./temp.rb:4:in `<main>'
I'm assuming that there is a stack or a list of exceptions somewhere and the exit process of Ruby prints out the entire list or stack of exceptions. I can see great benefits to this. But in my case, I'd like to get rid of the original exception from the list or stack. Is there a way to do this?
The "second" (original) exception is the cause of the rescued exception which is references by your new exception as it is created with Exception#exception.
Starting with Ruby 3.1, irb prints the details of the cause in addition to the actual exception to aid in debugging. Previous Ruby versions have ignored the cause here.
Thus, you can use other means to handle your exceptions rather than using Ruby's default handler of printing the details of an otherwise unhandled exception, e.g. by adding an explicit exception handler. Alternatively, you can also explicitly set the cause as you are creating your new exception:
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.class, "new message #{e.message}", cause: nil
end
May be something like this to extend Exception with custom method
Exception.class_eval do
def add_message(msg)
mod =
Module.new do
define_method :to_s do
"#{super()} #{msg}"
end
end
extend mod
end
end
And then
begin
puts 12 / 0
rescue ZeroDivisionError => e
raise e.add_message("(even don't try it)")
end

Can you rescue from specific errors with messages in ruby?

I'm trying to understand how errors propogate between classes in Ruby. I have this so far:
class User
def charge
puts "charging order soon"
raise RuntimeError.new("This is a runtime error")
rescue ArgumentError
puts "should never gets here"
end
end
class Runner
def run
begin
User.new.charge
rescue RuntimeError => e
puts e.message
end
end
end
Runner.new.run
When I run this, I get this which seems right:
$ ruby errors.rb
charging order soon
This is a runtime error
Inside runner, can I rescue from the RuntimeError with a specific message? If I have multiple RuntimeErrors being raised around my application, is there any way for the Runner's rescue clause to be raised only for RuntimeErrors with specific messages?
See https://stackoverflow.com/a/23771227/2981429
If you call raise inside a rescue block, the last raised exception will be re-raised.
In your exception block, you can check the message and choose to re-raise or not:
begin
User.new.charge
rescue RuntimeError => e
case e.message
when "This is a runtime error"
# put your handler code here
else
raise # re-raise the last exception
end
end
However if it's your goal to solely rescue errors that you yourself raise manually, then it's probably easier to define a custom error class instead:
class MyError < StandardError; end
Then instead of raise RuntimeError.new("message") use raise MyError.new("message"), and rescue it normally:
begin
User.new.charge
rescue MyError => e
# handler
end
This way you don't have to worry about your rescues interfering with the built-in exceptions.

Exit method in case of API error and continue with other methods

Is it possible to raise an exception if any, when calling an API, then exit the method in which exception happened and continue normally calling other methods? For example in case like this:
Class1.first_method
Class2.second_method -> failed because of an API error
Clas3.third_method - I want this one to continue
Class1.first_method
begin
Class2.second_method
rescue StandardError => e
$stderr << "An error occurred: #{e.message}"
end
Clas3.third_method
This is an example of a rescue block, which is part of the way ruby permits Exception handling

Reraise (same exception) after catching an exception in Ruby

I am trying to improve my Ruby skills by catching exceptions. I want to know if it is common to reraise the same kind of exception when you have several method calls. So, would the following code make sense? Is it ok to reraise the same kind of exception, or should I not catch it on the process method?
class Logo
def process
begin
#processed_logo = LogoProcessor::create_image(self.src)
rescue CustomException
raise CustomException
end
end
end
module LogoProcessor
def self.create_image
raise CustomException if some_condition
end
end
Sometimes we just want to know an error happened, without having to actually handle the error.
It is often the case that the one responsible for handling errors is user of the object: the caller. What if we are interested in the error, but don't want to assume that responsibility? We rescue the error, do whatever we need to do and then propagate the signal up the stack as if nothing had happened.
For example, what if we wanted to log the error message and then let the caller deal with it?
begin
this_will_fail!
rescue Failure => error
log.error error.message
raise
end
Calling raise without any arguments will raise the last error. In our case, we are re-raising error.
In the example you presented in your question, re-raising the error is simply not necessary. You could simply let it propagate up the stack naturally. The only difference in your example is you're creating a new error object and raising it instead of re-raising the last one.
This will raise the same type of error as the original, but you can customize the message.
rescue StandardError => e
raise e.class, "Message: #{e.message}"
I had the same question as in the comment thread here, i.e. What if the line before (re)raise fails?
My understanding was limited by the missing knowledge that the global variable of $! is "kinda garbage collected" // "scoped to its functional context", which the below example demonstrates:
def func
begin
raise StandardError, 'func!'
rescue StandardError => err
puts "$! = #{$!.inspect}"
end
end
begin
raise StandardError, 'oh no!'
rescue StandardError => err
func
puts "$! = #{$!.inspect}"
raise
end
The output of the above is:
$! = #<StandardError: func!>
$! = #<StandardError: oh no!>
StandardError: oh no!
from (pry):47:in `__pry__'
This behavior is different than how Python's (re)raise works.
The documentation for Exception states:
When an exception has been raised but not yet handled (in rescue,
ensure, at_exit and END blocks), two global variables are set:
$! contains the current exception.
$# contains its backtrace.
So these variables aren't true global variables, they are only defined inside the block that's handling the error.
begin
raise
rescue
p $! # StandardError
end
p $! # nil
A slightly better way to do the same thing as FreePender is to use the exception method from the Exception class, which is the ancestor class to any error classes, like StandardError, so that the method is available to any error classes.
Here the method's documentation that you can find on ApiDock:
With no argument, or if the argument is the same as the receiver, return the receiver. Otherwise, create a new exception object of the same class as the receiver, but with a message equal to string.to_str.
Now let's see how it works:
begin
this_will_fail!
rescue Failure => error
raise error.exception("Message: #{error.message}")
end
Adding to above answers here:
In some applications you may need to log the error twice.
For example exception need to be notified to monitoring tools like
Nagios/Newrelic/Cloudwatch.
At the same time you may have your own kibana backed summary logging
tool, internally for your reference.
In those cases you might want to log & handle the errors multiple times.
Example:
begin
begin
nil.to_sym
rescue => e
puts "inner block error message: #{e.message}"
puts "inner block backtrace: #{e.backtrace.join("\n")}"
raise e
end
rescue => e
puts "outer block error message: #{e.message}"
puts "outer block backtrace: #{e.backtrace.join("\n")}"
end
I am using puts here, for your the ease of verifying this code in
rails console, in actual production you may need to use rails logger

How to suppress and not to print the backtrace of the exception on the terminal in ruby using Thor?

Following is my method that might raise the exception.
Its a method of the CLI too that I am building.
Whenever the exception occurs, I want to catch that and just print my custom message on the terminal.
# variation 1
def self.validate(yaml_path)
begin
....
....
rescue
puts "Error"
end
end
# variation 2
def self.validate(yaml_path)
begin
....
....
rescue Exceptino => e
puts "Error: #{e.message}"
end
end
But the backtrace gets printed on the terminal.
How to avoid the backtrace to get printed?
± ../../bin/cf site create
ruby-1.8.7-p352
Error during processing: syntax error on line 52, col 10: ` - label: Price'
/Users/millisami/.rvm/rubies/ruby-1.8.7-p352/lib/ruby/1.8/yaml.rb:133:in `load': syntax error on line 52, col 10: ` - label: Price' (ArgumentError)
.... backtrace .....
.............
The answer was to rescue it on the executable file at bin/<exe>.
Thanks for suggesting
begin
Cf::CLI.start
rescue Psych::SyntaxError
$stderr.puts "\n\tError during processing: #{$!.message}\n\n"
end
The following code doesn't output the backtrace.
class CLS
def hi
begin
raise "X"
rescue
puts $!.message
end
end
end
CLS.new.hi
Have you checked to see if there is another point in the stack where another method is rescuing the exception, outputting the stack trace and then re-raising the exception?
The reason you're not rescuing the exception is because Psych::SyntaxError is not descended from StandardError, so a simple rescue won't catch it. You need to specify a descendant of Psych::SyntaxError:
>> require 'psych'
=> true
>> begin; raise Psych::SyntaxError; rescue; puts "GOT IT"; end
# Psych::SyntaxError: Psych::SyntaxError
# from (irb):8
# from /Users/donovan/.rvm/rubies/ruby-1.9.2-p180/bin/irb:16:in `<main>'
>> Psych::SyntaxError.ancestors
=> [Psych::SyntaxError, SyntaxError, ScriptError, Exception, Object, PP::ObjectMixin, Kernel, BasicObject]
>> begin; raise Psych::SyntaxError; rescue Exception; puts "GOT IT"; end
GOT IT
Notice that in my example rescue Exception does catch it. You should generally be as specific as you can when rescuing unless you really need to rescue all Exceptions. Be aware that suppressing backtraces is good when the exception is something you expect, but if you don't expect it in general it makes debugging much harder.

Resources