СУХОЙ способ повторного вызова одного и того же набора исключений в нескольких местах - PullRequest
3 голосов
/ 26 сентября 2008

короткий:

Есть ли в Ruby способ DRY-ify:

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

Но это заставляет метод re-повысить известные-исключения обнаруживаться в обратной трассировке.

edit: Я думаю, что я хотел бы что-то вроде макроса предварительной обработки C

Ответы [ 5 ]

3 голосов
/ 26 сентября 2008

Вы можете просто использовать знак в массиве.

Прямо из 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
2 голосов
/ 26 сентября 2008

немного подумав, я придумал это:

interpreter_block {do_something}

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

Хотя все еще не так тихо, как мне хотелось бы, по крайней мере теперь дополнительная запись в обратной трассировке стала несколько лучше.

1 голос
/ 05 октября 2009

Это хакерское прикосновение, но что касается очистки следа, что-то вроде этого работает хорошо:

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>'
1 голос
/ 26 сентября 2008

Это может быть немного злым, но я думаю, что вы можете просто удалить линию из обратного следа;

COMMON_ERRORS = [ArgumentError, RuntimeError]

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

Я не уверен, что это хорошая идея. После этого вы получите массу удовольствия отладки вашего переводчика; -)

Примечание: Верх дерева может вас заинтересовать.

0 голосов
/ 26 сентября 2008

Если у вас есть вся информация, необходимая вам в исключениях, и вам не нужна обратная трассировка вообще, вы можете просто определить свою собственную ошибку и поднять ее, вместо того, чтобы пересмотреть существующее исключение. Это даст ему свежий след. (Конечно, предположительно, ваш пример кода неполон, и в блоке спасения происходит другая обработка - в противном случае лучше всего просто позволить ошибке всплыть естественным путем.)

class MyError < StandardError; end

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