3

short:

Is there a way in Ruby to DRY-ify this:

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

longer:

I'm building an interpreter. This interpreter can be called using different entry points. If I feed this interpreter a 'dirty' string, I expect it to raise an error. However, it would be nice if I don't get spammed by the by the entire back trace of every method called directly or indirectly by do_something, especially since the interpreter makes use of recursion.

As you can see in the above snippet, I already know a way to re raise an error and thereby removing the back trace. What I would like do is remove the duplication in the above example. The closest I have come thus far is this:

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

But that makes the method re-raise-known-exceptions show up in the back trace.

edit: I guess what I want would be something like a C pre-processing macro

4

5 回答 5

3

您可以在阵列上使用 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
于 2008-09-26T02:22:01.023 回答
2

在考虑更多的同时,我想出了这个:

interpreter_block {do_something}

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

虽然我想要的还不是很安静,但至少现在后面的痕迹中的额外条目已经变得更好看了一些。

于 2008-09-25T22:16:17.447 回答
1

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

COMMON_ERRORS = [ArgumentError, RuntimeError]

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

不过,我不确定这是否是个好主意。之后调试解释器会非常有趣;-)

旁注:Treetop可能会引起您的兴趣。

于 2008-09-26T11:56:55.310 回答
1

这有点骇人听闻,但就清理回溯而言,这样的事情效果很好:

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>'
于 2009-10-04T23:22:14.967 回答
0

如果您在异常中拥有所需的所有信息,并且根本不需要回溯,则可以定义自己的错误并引发它,而不是重新引发现有的异常。这将给它一个新的回溯。(当然,可能您的示例代码不完整,并且在救援块中发生了其他处理 - 否则您最好的选择是让错误自然冒泡。)

class MyError < StandardError; end

def interpreter_block
  yield
rescue ExceptionOne, ExceptionTwo, ExceptionEtc => exc
  raise MyError
end
于 2008-09-25T23:13:05.470 回答