在Ruby中使用define_method覆盖类的所有实例方法

时间:2018-10-16 09:55:06

标签: ruby

为了集中处理我的应用程序的错误,我创建了类ErrorHandling。此类应该记录我所有的错误,通过Slack通知我,等等。

class ErrorHandler
    def self.handle_error
        yield
    rescue => e
        log_error(e.message)
        notify_via_slack(e.message)
        # etc.
    end
end

我需要手动将MyClass的所有方法包装在ErrorHandler.handle_error中,以利用中央错误处理。

class MyClass
    def method1
        ErrorHandler.handle_error do
            raise StandardError, 'my error msg'
        end
    end

    def method2
        ErrorHandler.handle_error do
            raise StandardError, 'my error msg'
        end
    end
end

这将不必要地污染我的代码。 我考虑过动态覆盖我的方法,因此不必手动包装它们。

class MyClass
    def method1
        raise StandardError, 'my error msg'
    end

    def method2
        raise StandardError, 'my error msg'
    end

    instance_methods(false).each do |method_name| # instance_methods(false) => [:method1, :method2]
        define_method(method_name) do
            ErrorHandler.handle_error do
                super()
            end
        end
    end
end

不幸的是,此代码无法正常工作。是否可以动态覆盖MyClass的方法?

我的目标是动态重写类的所有实例方法以利用中央错误处理,因此我不必手动修改每个方法。

感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

如果您要致电super,则应使用Module#prepend

ims = instance_methods(false)
prepend(Module.new do
  ims.each do |method_name|
    define_method(method_name) do
      ErrorHandler.handle_error { super() }
    end
  end
end)

答案 1 :(得分:2)

我建议您使用代理模式,该模式允许您将MyClass的任何实例注入代理,然后在代理中定义所需的任何额外行为。

这里有一个练习,教您如何创建代理:

https://github.com/edgecase/ruby_koans/blob/master/src/about_proxy_object_project.rb

最后,您会得到类似的东西

class Proxy
  def initialize(target_object)
    @target_object = target_object
  end

  def missing_method(method_symbol, *args, &block)
    begin
      @target_object.__send__(method_symbol, *args, &block)
    rescue Exception => ex
      SlackNotifier.notify(ex)
      raise ex
    end
  end
end