0

我在某些应用程序中有一个收集敏感信息的自定义库。

这些信息来自各种网站,例如:Twitter、Facebook、政府博客、大学网站和普通博客。

我为每个源创建了一个解析器,因为其中一些写得不好,其中一些需要一些硬编码解析(正则表达式和其他东西)。

一切正常。几天前,我在我的监控工具中看到了客户端的一些异常。当我去寻找它时,我发现我的一个“观看”网站已经更改了它们的 HTML+CSS,所以我不得不重新实现该网站的解析器。

所以这让我想,如果其他网站也改变了他们的标记会发生什么?

所以我让我所有的解析器都为我的监视器提出了自定义异常来记录。但是当谈到红宝石时,我知道我可以做一些更精细的事情。

我的问题是:有没有办法在我的应用程序中捕获所有异常,添加自定义行为,如前置过滤器,并让其他人触发他们的默认操作?

为了显示:

如果我遇到解析器错误,我的 twitter 解析器中有这个异常:raise Twitter::ParserError

在某个地方,在初始化程序或其他东西中,我有这个寄存器(这就是我想要的):

# after 5 exceptions of Twitter::ParserError i want to act
registerException(Twitter::ParserError, 5.times) do
  # Send me an email warning me about this error
  # or do something with my environment
  # Parser.find_by_website(website: :twitter).report_offline
end
4

1 回答 1

0

我不会评论我是否认为这是解决这个问题的正确方法……但这是一个有趣的元编程小挑战。

要意识到的是raise,就像 ruby​​ 中的大多数东西一样,它只是一种方法。因此,您可以为方法链起别名,并在调用原始方法之前或之后做任何您想做的事情,从而有效地为您提供方法提升的“之前”和“之后”挂钩。

这是上面伪代码的实现。你没有得到的一件事是处理引发的异常的实际实例,因此你不能,例如,在发送给你的电子邮件中包含堆栈跟踪。我不确定是否有办法让您处理该异常,因为它是在 C 代码中创建的。

但是给你:

module Kernel
  @@registered_exceptions ||= {}
  @@raises ||= {}

  def register_exception(exception_class, times = 5, &block)
    @@registered_exceptions[exception_class] = {
      times: times,
      block: block
    }
  end

  alias_method :original_raise, :raise
  def hacked_raise(*args)
    # figure out what class we are about to `raise`
    # see http://www.ruby-doc.org/core-2.0/Kernel.html#method-i-raise
    # about why this is the correct logic.
    exception_class = if args.empty?
                        if $!
                          $!.class
                        else
                          RuntimeError
                        end
                      elsif args[0].is_a? String
                        RuntimeError
                      else
                        args[0]
                      end

    # this exception is not registered so forget it and act normally
    (original_raise(*args) and return) unless @@registered_exceptions.keys.include?(exception_class)



    @@raises[exception_class] ||= 0
    @@raises[exception_class] += 1

    if @@raises[exception_class] == @@registered_exceptions[exception_class][:times]
      @@raises[exception_class] = 0 # reset counter for this exception class
      @@registered_exceptions[exception_class][:block].call
    end
  end
  alias_method :raise, :hacked_raise
end

register_exception RuntimeError, 3 do
  puts "hey, there were 3 runtime exceptions raised!"
end

raise
raise
raise
raise
raise
raise
raise
于 2013-06-16T18:00:41.837 回答