25

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?

4
  • Did you try rescuing everything, checking for the namespace, and re-raising if it's not? Commented Jul 12, 2012 at 18:10
  • @dave I was mainly wondering if there's an easier/cleaner way to catch exceptions based on namespace. Commented Jul 12, 2012 at 18:38
  • Nope, unless there's something common, as indicated in the answers. Commented Jul 12, 2012 at 18:52
  • 3
    Your question is more general than what the accepted answer answers. I suggest you either: 1) revise your question to say you only care about Errno namespace specifically, or 2) unaccept the answer. Commented Aug 12, 2014 at 18:14

4 Answers 4

30

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 
Sign up to request clarification or add additional context in comments.

Comments

4

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.

4 Comments

+1 I never knew you could pass a lambda as an exception matcher
class or module required for rescue clause (TypeError) looks like it doesn't work?
Interesting because it has worked for me at the time of writing. If you check source I referred to, you can see that it was not my invention and worked for other people. Presently I can only get it working under ruby 1.9.x. Also my upvotes show it has worked for some people. Also I see one person claiming it works on 2.1.2 (gist.github.com/panthomakos/1230515#gistcomment-1298427). I have hard time now finding where I used something similar as I'm wondering if it silently stopped working.
It is also very strange for me I can't google anything about lambdas and rescue related ruby change anywhere. Because lambda === something calls lambda.call(something) under the hood I assume that type checking was implemented at some point without regard to this use case. If anybody knows when this changed, please let me know.
3

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 

4 Comments

"all subclasses of SystemCallError are classes under Errno" does not necessarily hold. All Errno exceptions are documented to be SystemCallErrors but there's no guarantee that all SystemCallErrors will be Errnos.
@muistooshort does my verification code not work then? I assume searching ObjectSpace is sufficient for discovering undocumented classes. Unless some built-in ruby lib has adds more subclasses.
The problem is the "This returns true for me" part and in particular, the for me. A gem (or even the application itself) could provide a subclass of SystemCallError that isn't an Errno.
@mu I'll grant you that a lib can add a new subclass, but it can also add a new error class under Errno that isn't a subclass of SystemCallError. Also, I think the n=1 problem with my assertion is not so serious since the users run the verification code to see what they get. In any case, it's good to make all the points plain so people know the benefits/risks. Sigh. This kind of caveat-upon-caveat is a frequent reality in a dynamic language world.
2

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.