how to properly rescue this statement in ruby? - ruby

I have a chef resource and I just added linting with cookstyle to the cookbook. I have this line in the cookbook:
existing_value = key.to_s.split('.').inject(node.elasticsearch) { |result, attr| result[attr] } rescue nil if existing_value.nil?
It's causing the linter to error. I tried a couple of things, but I'm not sure how to say the same thing and pass the lint test.
Style/RescueModifier: Avoid using rescue in its modifier form
If I remove the rescue statement entirely, it causes this exception:
Chef::Mixin::Template::TemplateError (undefined method `[]' for nil:NilClass) on line #52:
...
52: <%= print_value 'action.destructive_requires_name' -%>
...

So we actually have a method for this if you're on a mildly recent Chef version. You can do this instead:
existing_value ||= node.read("elasticsearch.#{key}")
And we'll take care of the rest.

Although #coderanger's solution is better, the linter wants you to use the long rescue form
begin
existing_value = key.to_s.split('.').inject(node.elasticsearch) { |result, attr| result[attr] }
rescue
nil
end if existing_value.nil?
or usually considered better
if existing_value.nil?
begin
existing_value = key.to_s.split('.').inject(node.elasticsearch) { |result, attr| result[attr] }
rescue
nil
end
end
I would also change rescue to the more specific rescue Chef::Mixin::Template::TemplateError, rescuing all exceptions is probably not what you want (i.e. you wouldn't want to hide a real exception)

Related

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.

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 rescue all exceptions under a certain namespace?

Is there a way to rescue all exceptions under a certain namespace?
For example, I want to rescue all of the Errno::* exceptions (Errno::ECONNRESET, Errno::ETIMEDOUT). I can go ahead and list them all out on my exception line, but I was wondering if I can do something like.
begin
# my code
rescue Errno
# handle exception
end
The above idea doesn't seem to work, thus is there something similar that can work?
All the Errno exceptions subclass SystemCallError:
Module Errno is created dynamically to map these operating system errors to Ruby classes, with each error number generating its own subclass of SystemCallError. As the subclass is created in module Errno, its name will start Errno::.
So you could trap SystemCallError and then do a simple name check:
rescue SystemCallError => e
raise e if(e.class.name.start_with?('Errno::'))
# do your thing...
end
Here is another interesting alternative. Can be adapted to what you want.
Pasting most interesting part:
def match_message(regexp)
lambda{ |error| regexp === error.message }
end
begin
raise StandardError, "Error message about a socket."
rescue match_message(/socket/) => error
puts "Error #{error} matches /socket/; ignored."
end
See the original site for ruby 1.8.7 solution.
It turns out lambda not accepted my more recent ruby versions. It seems the option is to use what worked in 1.8.7 but that's IM slower (to create a new class in all comparisons. So I don't recommend using it and have not even tried it:
def exceptions_matching(&block)
Class.new do
def self.===(other)
#block.call(other)
end
end.tap do |c|
c.instance_variable_set(:#block, block)
end
end
begin
raise "FOOBAR: We're all doomed!"
rescue exceptions_matching { |e| e.message =~ /^FOOBAR/ }
puts "rescued!"
end
If somebody knows when ruby removed lambda support in rescue please comment.
All classes under Errno are subclasses of SystemCallError. And all subclasses of SystemCallError are classes under Errno. The 2 sets are identical, so just rescue SystemCallError. This assumes that you're not using an external lib that adds to one and not the other.
Verify the identity of the 2 sets (using active_support):
Errno.constants.map {|name|
Errno.const_get(name)
}.select{|const|
Class === const
}.uniq.map(&:to_s).sort ==
SystemCallError.subclasses.map(&:to_s).sort
This returns true for me.
So, applied to your example:
begin
# my code
rescue SystemCallError
# handle exception
end
Here is a more generic solution, in the case you wanted to rescue some Errno types and not others.
Create a custom module to be included by all the error classes we want to rescue
module MyErrnoModule; end
Customize this array to your liking, up to the "each" call.
Errno.constants.map {|name|
Errno.const_get(name)
}.select{|const|
Class === const
}.uniq.each {|klass|
klass.class_eval {
include MyErrnoModule
}
}
Test:
begin
raise Errno::EPERM
rescue MyErrnoModule
p "rescued #{$!.inspect}"
end
Test result:
"rescued #<Errno::EPERM: Operation not permitted>"
I would guess this performs slightly better than a solution that needs to check the name of the exception.

How do you rescue I18n::MissingTranslationData?

I want to be able to rescue I18n::MissingTranslationData like so:
begin
value = I18n.t('some.key.that.does.not.exist')
puts value
return value if value
rescue I18n::MissingTranslationData
puts "Kaboom!"
end
I tried the above, but it doesn't seem to go into the rescue block. I just see, on my console (because of puts): translation missing: some.key.that.does.not.exist. I never see Kaboom!.
How do I get this to work?
IMO, it's pretty strange but in the current version of i18n (0.5.0) you should pass an exception that you want to rescue:
require 'i18n'
begin
value = I18n.translate('some.key.that.does.not.exist', :raise => I18n::MissingTranslationData)
puts value
return value if value
rescue I18n::MissingTranslationData
puts "Kaboom!"
end
and it will be fixed in the future 0.6 release (you can test it - https://github.com/svenfuchs/i18n)
Same as above but nicer.
v = "doesnt_exist"
begin
puts I18n.t "langs.#{v}", raise: true
rescue
puts "Nooo #{v} has no Translation!"
end
or
puts I18n.t("langs.#{v}", default: "No Translation!")
or
a = I18n.t "langs.#{v}", raise: true rescue false
unless a
puts "Update your YAML!"
end
In the current version of I18n, the exception you're looking for is actually called MissingTranslation. The default exception handler for I18n rescues it silently, and just passes it up to ArgumentError to print an error message and not much else. If you actually want the error thrown, you'll need to override the handler.
See the source code for i18n exceptions, and section 6.2 of RailsGuides guide to I18n for how to write a custom handler
Note that now you simply pass in :raise => true
assert_raise(I18n::MissingTranslationData) { I18n.t(:missing, :raise => true) }
...which will raise I18n::MissingTranslationData.
See https://github.com/svenfuchs/i18n/blob/master/lib/i18n/tests/lookup.rb

DRY way of re-raising same set of exceptions in multiple places

short:
Is there a way in Ruby to DRY-ify this:
def entry_point_one
begin
do_something
rescue MySyntaxErrorOne, MySyntaxErrorTwo, MySyntaxErrorEtc => syn_err
raise syn_err.exception(syn_err.message)
end
end
def entry_point_two
begin
do_something_else
rescue MySyntaxErrorOne, MySyntaxErrorTwo, MySyntaxErrorEtc => syn_err
raise syn_err.exception(syn_err.message)
end
end
longer:
I'm building an interpreter. This interpreter can be called using different entry points. If I feed this interpreter a 'dirty' string, I expect it to raise an error. However, it would be nice if I don't get spammed by the by the entire back trace of every method called directly or indirectly by do_something, especially since the interpreter makes use of recursion.
As you can see in the above snippet, I already know a way to re raise an error and thereby removing the back trace. What I would like do is remove the duplication in the above example. The closest I have come thus far is this:
def entry_point_one
re_raise_known_exceptions {do_something}
end
def entry_point_two
re_raise_known_exceptions {do_something_else}
end
def re_raise_known_exceptions
yield
rescue MySyntaxErrorOne, MySyntaxErrorTwo, MySyntaxErrorEtc => syn_err
raise syn_err.exception(syn_err.message)
end
But that makes the method re-raise-known-exceptions show up in the back trace.
edit: I guess what I want would be something like a C pre-processing macro
You can just use the splat on an array.
Straight from IRB:
COMMON_ERRORS = [ArgumentError, RuntimeError] # add your own
def f
yield
rescue *COMMON_ERRORS => err
puts "Got an error of type #{err.class}"
end
f{ raise ArgumentError.new }
Got an error of type ArgumentError
f{ raise 'abc' }
Got an error of type RuntimeError
while thinking about it a bit more, I came up with this:
interpreter_block {do_something}
def interpreter_block
yield
rescue ExceptionOne, ExceptionTwo, ExceptionEtc => exc
raise exc.exception(exc.message)
end
Although it's still not quiet what I would like to have, at least now the extra entry in the back trace has become somewhat better looking.
It might be slightly evil, but I think you can simply remove the line from the backtrace ;-)
COMMON_ERRORS = [ArgumentError, RuntimeError]
def interpreter_block
yield
rescue *COMMON_ERRORS => err
err.backtrace.delete_if{ |line| line=~/interpreter_block/ }
raise err
end
I'm not sure it's such a good idea though. You'll have a hell of a lot of fun debugging your interpreter afterward ;-)
Side note: Treetop may be of interest to you.
This is a touch hackish, but as far as cleaning up the backtrace goes, something like this works nicely:
class Interpreter
def method1
error_catcher{ puts 1 / 0 }
end
def error_catcher
yield
rescue => err
err.set_backtrace(err.backtrace - err.backtrace[1..2])
raise err
end
end
The main trick is this line err.set_backtrace(err.backtrace - err.backtrace[1..2]). Without it, we get the following (from IRB):
ZeroDivisionError: divided by 0
from (irb):43:in `/'
from (irb):43:in `block in method1'
from (irb):47:in `error_catcher'
from (irb):43:in `method1'
from (irb):54
from /Users/peterwagenet/.ruby_versions/ruby-1.9.1-p129/bin/irb:12:in `<main>'
What we don't want in there are the second and third lines. So we remove them, ending up with:
ZeroDivisionError: divided by 0
from (irb):73:in `/'
from (irb):73:in `method1'
from (irb):84
from /Users/peterwagenet/.ruby_versions/ruby-1.9.1-p129/bin/irb:12:in `<main>'
If you have all of the information you need in the exceptions, and you do not need the backtrace at all, you can just define your own error and raise that, instead of reraising the existing exception. This will give it a fresh backtrace. (Of course, presumably your sample code is incomplete and there is other processing happening in the rescue block -- otherwise your best bet is to just let the error bubble up naturally.)
class MyError < StandardError; end
def interpreter_block
yield
rescue ExceptionOne, ExceptionTwo, ExceptionEtc => exc
raise MyError
end

Resources