How to rescue all exceptions under a certain namespace?
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.
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
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 ofSystemCallError
. As the subclass is created in moduleErrno
, its name will startErrno::
.
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