在多个位置重新引发同一组异常的DRY方法

时间:2020-03-06 14:45:11  来源:igfitidea点击:

短的:

Ruby中是否有一种方法可以对此进行DRY化:

def entry_point_one
  begin
    do_something
  rescue MySyntaxErrorOne, MySyntaxErrorTwo, MySyntaxErrorEtc => syn_err
    raise syn_err.exception(syn_err.message)
  end
end

def entry_point_two
  begin
    do_something_else
  rescue MySyntaxErrorOne, MySyntaxErrorTwo, MySyntaxErrorEtc => syn_err
    raise syn_err.exception(syn_err.message)
  end
end

更长:

我正在建立一个口译员。可以使用不同的入口点来调用此解释器。如果我向此解释器提供"脏"字符串,则期望它引发错误。但是,如果我不被do_something直接或者间接调用的每个方法的整个反向跟踪所困扰,那就太好了,特别是因为解释器使用了递归。

如我们在上面的代码片段中所看到的,我已经知道一种重新引发错误并由此消除回溯的方法。我想做的是删除上面示例中的重复项。到目前为止,我最接近的是:

def entry_point_one
  re_raise_known_exceptions {do_something}
end

def entry_point_two
  re_raise_known_exceptions {do_something_else}
end

def re_raise_known_exceptions
  yield
rescue MySyntaxErrorOne, MySyntaxErrorTwo, MySyntaxErrorEtc => syn_err
    raise syn_err.exception(syn_err.message)
end

但这使方法重新引发已知异常显示在后跟踪中。

编辑:我想我想要的将是类似于C预处理宏

解决方案

在想一想的同时,我想到了:

interpreter_block {do_something}

def interpreter_block
  yield
rescue ExceptionOne, ExceptionTwo, ExceptionEtc => exc
  raise exc.exception(exc.message)
end

尽管我仍然不想要什么,但至少现在回溯轨迹中的额外条目看起来有些好看了。

如果我们拥有异常中所需的所有信息,并且根本不需要回溯,则可以定义自己的错误并提出该错误,而不必重新引发现有的异常。这将给它带来新的回溯。 (当然,大概示例代码是不完整的,并且救援块中正在发生其他处理-否则,我们最好的选择就是让错误自然冒出来。)

class MyError < StandardError; end

def interpreter_block
  yield
rescue ExceptionOne, ExceptionTwo, ExceptionEtc => exc
  raise MyError
end

我们可以仅在阵列上使用splat。

直接来自IRB:

COMMON_ERRORS = [ArgumentError, RuntimeError] # add your own 

def f
  yield
rescue *COMMON_ERRORS => err
  puts "Got an error of type #{err.class}"
end

f{ raise ArgumentError.new }
Got an error of type ArgumentError

f{ raise 'abc' }
Got an error of type RuntimeError

可能有点邪恶,但我认为我们可以简单地从回溯中删除该行;-)

COMMON_ERRORS = [ArgumentError, RuntimeError]

def interpreter_block
  yield
rescue *COMMON_ERRORS => err
  err.backtrace.delete_if{ |line| line=~/interpreter_block/ }
  raise err
end

我不确定这是一个好主意。之后,调试调试器会带来很多乐趣;-)

旁注:Treetop可能会让我们感兴趣。

这是一个小技巧,但是就清理回溯而言,类似这样的方法很有效:

class Interpreter

  def method1
    error_catcher{ puts 1 / 0 }
  end

  def error_catcher
    yield
  rescue => err
    err.set_backtrace(err.backtrace - err.backtrace[1..2])
    raise err
  end

end

主要技巧是" err.set_backtrace(err.backtrace err.backtrace [1..2])"这一行。没有它,我们将获得以下内容(来自IRB):

ZeroDivisionError: divided by 0
  from (irb):43:in `/'
  from (irb):43:in `block in method1'
  from (irb):47:in `error_catcher'
  from (irb):43:in `method1'
  from (irb):54
  from /Users/peterwagenet/.ruby_versions/ruby-1.9.1-p129/bin/irb:12:in `<main>'

我们不需要的是第二行和第三行。因此,我们将其删除,最后得到:

ZeroDivisionError: divided by 0
  from (irb):73:in `/'
  from (irb):73:in `method1'
  from (irb):84
  from /Users/peterwagenet/.ruby_versions/ruby-1.9.1-p129/bin/irb:12:in `<main>'