You could define a module that when included defines a method_added
hook that wraps all new methods inside a begin..rescue
block:
require 'set'
module ExceptionHandler
def self.included(klass)
super
klass.send(:extend, ClassMethods)
end
module ClassMethods
def exception_handler(&block)
@__exception_handler = block.to_proc
end
def handle_exception(exception)
defined?(@__exception_handler) ? @__exception_handler.call(exception) : super
end
def handle_method_exceptions(method_name)
old_method = instance_method(method_name)
return if (@__hooked_methods ||= Set.new).include?(method_name)
@__ignoring_added_methods = true # avoid infinite define_method/method_added loop
define_method method_name do |*args, &block|
begin
old_method.bind(self).(*args, &block)
rescue => ex
self.class.handle_exception(ex)
end
end
@__ignoring_added_methods = false
@__hooked_methods << method_name
end
def method_added(method_name)
super
unless @__ignoring_added_methods
handle_method_exceptions(method_name)
end
end
end
end
This would be used like:
class Foo
include ExceptionHandler
exception_handler do |exception|
puts "Catched an exception:"
puts "---------------------"
puts "Exception class: #{exception.class}"
puts "Message: #{exception.message}"
puts "Backtrace:"
puts exception.backtrace.join("\n ")
puts
puts "reraising.."
puts
raise exception
end
def this_raises
raise "somebody set up us the bomb"
end
end
Foo.new.this_raises
This would output:
Catched an exception:
---------------------
Exception class: RuntimeError
Message: somebody set up us the bomb
Backtrace:
errorhandler.rb:62:in `this_raises'
errorhandler.rb:26:in `call'
errorhandler.rb:26:in `block in handle_exceptions'
errorhandler.rb:67:in `<main>'
reraising..
I'm not sure if it is a good idea.
You could take out the method_added
part and it would look something like:
class Foo
with_rescue def foofoo(arg)
puts arg.inspect
end
end
(You can just rename the handle_method_exceptions
to with_rescue
and remove all the @__ignoring_added_methods
trickery and the method_added
method and it should work as described).