拯救类中所有方法的异常

Toà*_*oàn 0 ruby

基本上,这是正常的代码:

class Foo
   def hi
      # your code here....
   rescue => e
      # Raise error here
   end

   def hello
      # your code here...
   rescue => e
      # Raise error here
   end
end
Run Code Online (Sandbox Code Playgroud)

但在 PHP 中,我可以使用__call魔术方法创建抽象类,如下所示:

class FooAbstract {
    public function __call($name, $args) {
       # Try catch in here...
    }
}

class Foo extends FooAbstract {
   public function hi() {
     # Code with try catch...
   }
}
Run Code Online (Sandbox Code Playgroud)

我如何在 Ruby 类中使​​用 __call 方法???

Kim*_*hto 5

您可以定义一个模块,该模块在包含时定义一个method_added将所有新方法包装在begin..rescue块内的钩子:

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
Run Code Online (Sandbox Code Playgroud)

这将像这样使用:

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
Run Code Online (Sandbox Code Playgroud)

这将输出:

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..
Run Code Online (Sandbox Code Playgroud)

我不确定这是否是一个好主意。

你可以把这个部分拿出来method_added,它看起来像这样:

class Foo
  with_rescue def foofoo(arg)
    puts arg.inspect
  end
end
Run Code Online (Sandbox Code Playgroud)

(您只需将其重命名handle_method_exceptionswith_rescue并删除所有@__ignoring_added_methods技巧和method_added方法,它应该按描述的方式工作)。