Catching a lot of errors and putting all the errors being caught into a constant - ruby

Is there a way, within a rescue clause, to put all the errors into an array and call them from there, if the error is in the array?
For example:
FATAL_ERRORS = %w(Mechanize::ResponseCodeError RestClient::ServiceUnavailable OpenSSL::SSL::SSLError RestClient::BadGateway)
begin
# Do some cool stuff
rescue FATAL_ERRORS => e
puts "Exiting #{e}"
What I've tried:
I've tried grabbing the error from the current thread:
FATAL_ERRORS = Thread.current[:errors] ||= %w(Mechanize::ResponseCodeError RestClient::ServiceUnavailable OpenSSL::SSL::SSLError RestClient::BadGateway)
begin
# Do some cool stuff
rescue FATAL_ERRORS => e
puts "Exiting #{e}"
Also I've tried the splat operator:
FATAL_ERRORS = %w(Mechanize::ResponseCodeError RestClient::ServiceUnavailable OpenSSL::SSL::SSLError RestClient::BadGateway)
begin
# Do some cool stuff
rescue *FATAL_ERRORS => e
puts "Exiting #{e}"
Both the splat and the thread produce the following exception:
rescue in <main>': class or module required for rescue clause (TypeError)
How can I successfully rescue multiple errors without putting them all on the rescue line and making it look bad?

splat does work. Problem is in the way you are making FATAL_ERRORS constant. With %w notation, it converts value as string:
%w(Mechanize::ResponseCodeError)
=> ["Mechanize::ResponseCodeError"] # Note the string value instead of class constant.
Try
FATAL_ERRORS = [Mechanize::ResponseCodeError, RestClient::ServiceUnavailable, OpenSSL::SSL::SSLError, RestClient::BadGateway]

Related

My Rubocop doesn't like => e in Ruby error handling. Isn't => e standard?

Rubocop doesn't seem to like how I am handling my error. What should I do?
Lint/UselessAssignment: Useless assignment to variable - e.
rescue *exceptions_list => e
It is for the piece of code below:
def get_request(url_args = {})
http_connection(url_builder(url_args[:url], url_args[:page]))
rescue *exceptions_list => e
raise "#{e.message}"
end
The *exceptions_list that you see there is defined in a private method:
private
def exceptions_list
[
Net::HTTPBadResponse,
Net::HTTPHeaderSyntaxError,
Net::ProtocolError,
Errno::ECONNRESET,
Errno::EINVAL,
Timeout::Error,
EOFError,
SocketError
]
end
To avoid the rescue line being too long.
Question:
Having => e is pretty standard for error handling no? What should I do?
Thank you very much in advance! =)
Your example does not give Lint/UselessAssignment from rubocop, not at least with version 0.55.0
The UselessAssignment usually comes from something like this:
def do_things
..
rescue *exception_list => e # Useless assignment, e is never used in the block below.
puts "it failed :("
end
To fix this, you can remove the assignment if you're not using the raised exception for anything:
def do_things
..
rescue *exception_list
puts "it failed and i don't care why :D"
end

Rescue from an error that may not be defined

My Rails website (this problem is purely Ruby based though) uses the AWS-SES (Action mailer using AWS) gem in test/development environment, and I am catching possible errors from email deliveries like this
def try_delivering_email(options = {})
begin
yield
return false
rescue EOFError,
...
AWS::SES::ResponseError,
... => e
log_exception(e, options)
return e
end
end
Now the problem is that this gem is only defined for specific environments, in other words AWS does not exist in development, and the error checking code will therefore throw an error (haha) for undefined constant.
I have tried substuting that line for (AWS::SES::ResponseError if defined?(AWS) but then the next error I get is
class or module required for rescue clause
How can I get around this in the nicest way possible ?
The exception list of a rescue-clause doesn't have to be a literal/static list:
excs = [EOFError]
defined?(AWS) && excs << AWS::SES::Response
# ...
rescue *excs => e
The splat operator * is used here to convert an array into a list.
You can't include a conditional in a rescue clause, but you can blind rescue and then get picky about how to deal with it using conventional Ruby code:
rescue EOFError => e
log_exception(e)
e
rescue => e
if (defined?(AWS) and e.is_a?(AWS::SES::Response))
# ...
else
raise e
end
end
It's not the nicest way, but it does the job. You could always encapsulate a lot of that into some module that tests more neatly:
def loggable_exception?(e)
case (e)
when EOFError, AnotherError, EtcError
true
else
if (defined?(AWS) and e.is_a?(AWS::SES::Response))
true
else
false
end
end
end
Then you can do this as that method name should be self-explanatory:
rescue => e
if (loggable_exception?(e))
log_exception(e)
e
else
raise e
end
end
You could make this a little neater if log_exception returned the exception it was given. Don't forget Ruby is "return by default" and it doesn't need to be explicit unless you're doing it early.

Variables not recognized within Rescue in Ruby

I have the following code:
rescue Timeout::Error, StandardError => e
puts "Caught exception: #{e.message}".red
log.puts("#{e.backtrace}")
email_ids_all.each do |email_delete|
call= "/api/v2/emails/#{email_delete}/"
......
Before this rescue piece I have defined log and email_ids_all. However, neither of these are recognized within the ruby script. If i do this:
rescue Timeout::Error, StandardError => e
File.open(rescuelogfile, 'w') do |log| #setup log to write response codes.
puts "Caught exception: #{e.message}".red
log.puts("#{e.backtrace}")
email_ids_all.each do |email_delete|
call= "/api/v2/emails/#{email_delete}/"
....
log works fine, which makes sense. It would take a lot of writing to redefine the email_ids_all array and other variables contained inside my rescue block.
Is there anyway to allow variables to be recognized inside the rescue? Basically my code is laid out like this:
begin
#some code
rescue
#above code
end
I am using ruby 1.9.3.
EDIT----
log starts right after my begin statement :
begin
File.open(logfile, 'w') do |log| #setup log to write response codes.
log.puts works throughout the entire code except when an error is thrown, and then it runs the rescue script where log is not available.
The same goes for email_ids_all. There is an API call that generates about 10,000 emails and each of them is added to the array email_ids_all. The script is receiving an error about halfway through generating these emails, and so I need the rescue script to delete all email ids in the email_ids_all array. But for whatever reason, I get the following error:
FS_Test_Env.rb:762:in `block in <main>': undefined local variable or method `email_ids_all' for main:Object (NameError)
from FS_Test_Env.rb:759:in `open'
from FS_Test_Env.rb:759:in `rescue in <main>'
from FS_Test_Env.rb:7:in `<main>'
Any thoughts?
The way you put it, it should work, for example:
irb(main):001:0> begin
irb(main):002:1* x = 1
irb(main):003:1> x / 0
irb(main):004:1> rescue Exception => e
irb(main):005:1> p x
irb(main):006:1> end
1
=> 1
So it looks like the exception is thrown before your variables are defined.
The scope of the block parameter log is limited to that block. This is the whole point of the open with block.
Maybe you want to do:
begin
log = File.open('logfile', 'w')
...
rescue
...
ensure
log.close
end
Note that this does not cover errors when opening the logfile.
Regarding email_ids_all, I guess (!) you have the exception in a statement like:
email_ids_all = ... a long and complex calculation which raises an exception
If yes, the problem is that the assignment happens only after the whole right-hand side is calculated. The var email_ids_all is not yet created when the exception happens.
In order to access the elements created before the exception, you have to keep track of them, e.g.
begin
email_ids = []
10000.times do
email_ids << ... # create element eventually raising an exception
end
rescue
... # treat the already created elements
end

How to catch all exceptions as soon as they occur by just one rescue statement

I know how to catch the exceptions but what we do is to put "rescue" after a suspicious section of a code. what if you had a lot functions sending a query to mysql through mysql2 gem and you want to catch their exceptions. One thing you can do is to put a "rescue" statement in each of them. but i want to do that just by one rescue statement. So I put a "rescue" in end of code and put all of code in a "begin" and "end" but it didn't work.
Here is my code and as you see there is a problem in mysql query and just because of "rescue" being end of file, it doesn't catch the exception but when I put it after that query it works.
require 'mysql2'
require 'colored'
begin
def log(string)
p "["+string.cyan+"]"
end
def err
p "["+"FAIL".red+"]"
end
def done
p "["+"DONE".red+"]"
end
class SqlClient
def initialize()
log "SqlClient/initialize"
puts "Host: \n"
#host = gets.strip
puts "User: \n"
#user = gets.strip
puts "Pass: \n"
#pass = gets.strip
#client = Mysql2::Client.new(host: #host , username: #user , password: #pass)
end
def list_databases()
puts "We are listing your databases(not just projects) \n \n \n "
#client.query("ELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA").each do |row|
p row["SCHEMA_NAME"]
end
puts "\n \n \n"
end
end
rescue Mysql2::Error
err
abort
end
`query': You have an error in your SQL syntax; check the manual that corresponds to your
MySQL server version for the right syntax to use near 'ELECT SCHEMA_NAME FROM
INFORMATION_SCHEMA.SCHEMATA' at line 1 (Mysql2::Error)
I'm not looking for something like:
begin
# my code
rescue # this line is right after the code which is going to have problem and we catch it.
end
I'm looking for something like this:
begin
# first method
# second method
# thrid method
# rest of code and etc ...
# now this is end of file:
rescue
end
but as you saw in my code, it didn't work.
UPDATE: I found a similar question here and it seems there will be no answer :| maybe this is a sort of ruby Weakness.
if you want to see ANY error just use e for example
begin
# your call to a method of Mysql2 gem. for example:
client = Mysql2::Client.new(:host => "localhost", :username => "root", etc...)
rescue => e
puts e.message
puts e.backtrace.inspect
end
In order to catch every exception you'd need to wrap each method call with a begin rescue end. When an exception is raised it bails out of the execution so it wouldn't hit the next methods.
To catch all errors I guess I'd do something like this. Keep in mind, this is ugly and I'd recommend for you NOT to do this, but... if you want to try, maybe try something like this:
all_errors = []
# first method you call
begin
# some error might happen here
first_response = Mysql2::Client.new(:host => "localhost", :username => "root", etc...)
rescue => e
all_errors << e
end
# second method you call
begin
# some error might happen here
second_response = Mysql2::Client.new(:host => "localhost", :username => "root", etc...)
rescue => e
all_errors << e
end
puts all_errors.inspect
After a quick search I've found: (http://coderrr.wordpress.com/2008/11/07/the-simple-way-to-print-exceptions-in-ruby/)
# catch all exceptions (anything that derives from Exception)
begin
...
rescue Exception
puts $!, $#
end
You could use an at_exit handler, which has access to the last exception in $!
like
at_exit {
puts "the exception that killed us is", $!
}
If you want to catch Exceptions "as soon as they occur" (not after they're caught) you could use ruby's "debug mode" (which outputs messages when they occur to the console) or ruby-debug see Is there any way to start the Ruby debugger on exception?
Just wrap all your code in:
begin
#yourcode
#as much as you want
rescue
end
Nobody seemed to notice it but using rescue without a class will catch all StandardError and there are so much more.
If you want to catch ALL exceptions you need to do
begin
# your code where you call SqlClient.new etc
rescue Exception => e
puts "error raised"
puts [e, e.backtrace].flatten.join("\n")
end
List of all error classes:
Exception
NoMemoryError
ScriptError
LoadError
NotImplementedError
SyntaxError
SignalException
Interrupt
StandardError
ArgumentError
IOError
EOFError
IndexError
LocalJumpError
NameError
NoMethodError
RangeError
FloatDomainError
RegexpError
RuntimeError
SecurityError
SystemCallError
SystemStackError
ThreadError
TypeError
ZeroDivisionError
SystemExit
fatal
have you tried adding an at_exit method in your class? This would allow you to do something when ruby is exiting. like in this article.
Ruby at_exit
or
From Ruby 2.0 API Docs
However beware of cleverly rescuing from an exception!
You'll start pulling your hair out down the road (or another dev will) when you try to figure out why your code isn't failing when it should be failing. I prefer to fail massively with a bright shiny sign saying the code failed here! hehe.
Good luck!

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