简体   繁体   中英

How to catch all errors in a class?

Here's what I'm trying to do:

class Foo
  def foo
    raise "lol noob"
  end

  # ... many other methods here ...

rescue StandardError => e
  puts "error caught: #{e}"
end


Foo.new.foo
RuntimeError: lol noob
from (pry):45:in `foo'

As you can see, this does not work.

What I'm trying to avoid is to put a rescue block into every single method, given that they're many. Is it possible? If not, what's the best practice?

It would make no sense to have a common rescue block for a class, each method does different things right? How would you handle the variety of errors all in the same way? Error handling is just as much part of the logic as the "main" part of the method(s).

There's no silver bullet here, you rescue where you need to, and do what is needed when it is needed.

The Exceptional Ruby book may be of interest if you want to learn some common best practices.

TL;DR

In Ruby, you generally have to wrap the caller with rescue, rather than the receiver.

Explanation

It's likely that you're finding this behavior surprising because you're not thinking of classes as executable code . A rescue clause in a class definition will capture exceptions raised while the class is being interpreted. Consider the following example:

class Foo
  raise 'bar'
rescue
  'Rescued!'
end
#=> "Rescued!"

Here, the rescue clause works because the exception is raised while the class code is executing. However, some Foo#bar method wouldn't normally get rescued by this clause (except possibly while being defined) because Foo is no longer the caller, it's the receiver.

A rescue clause on the class will catch exceptions raised when the class itself is being defined. However, to rescue within a method at run-time, you need to rescue within the caller (in this case, the #bar method) as follows:

class Foo
  def bar
    raise 'method exception'
  rescue
    'Rescued method exception.'
  end
end

Foo.new.bar
#=> "Rescued method exception."

If you're absolutely sure that the only error handling you need to do is log out errors, you can abstract away some of the repetitive error logging by defining a helper method that takes in a block and do your error handling there.

For example,

class Foo
  def foo
    handle_exception do
      raise "lol noob"
    end
  end

  def bar
    handle_exception do
      raise "rofl"
    end
  end

  def handle_exception
    yield
  rescue => e
    puts "error caught: #{e}"
  end
end

Foo.new.foo # error caught: lol noob
Foo.new.bar # error caught: rofl

This has the benefit that later on you decide that want some alternate behavior, ie adding in a backtrace, you only have to touch one line of code:

def handle_exception
  yield
rescue => e
  puts "error caught: #{e}\n#{e.backtrace.join("\n")}"
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM