Повторное повышение (то же исключение) после перехвата исключения в Ruby

86

Я пытаюсь улучшить свои навыки Ruby, перехватывая исключения. Я хочу знать, распространено ли повторное создание одного и того же исключения, когда у вас есть несколько вызовов методов. Итак, имеет ли смысл следующий код? Можно ли повторно вызвать такое же исключение или я не должен ловить его в методе процесса?

class Logo
  def process
    begin
      @processed_logo = LogoProcessor::create_image(self.src)
    rescue CustomException
      raise CustomException
    end
  end
end

module LogoProcessor
  def self.create_image
    raise CustomException if some_condition
  end
end
Хоммер Смит
источник

Ответы:

175

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

Часто за обработку ошибок отвечает пользователь объекта: вызывающий объект. Что делать, если мы заинтересованы в ошибке, но не хотим брать на себя эту ответственность? Мы спасаем ошибку, делаем все, что нам нужно, а затем передаем сигнал вверх по стеку, как будто ничего не произошло.

Например, что, если мы хотим зарегистрировать сообщение об ошибке, а затем позволить вызывающему абоненту разобраться с этим?

begin
  this_will_fail!
rescue Failure => error
  log.error error.message
  raise
end

Вызов raiseбез аргументов вызовет последнюю ошибку. В нашем случае мы делаем ререйз error.

В примере, который вы представили в своем вопросе, повторное создание ошибки просто не нужно. Вы можете просто позволить ему естественным образом распространяться вверх по стеку. Единственная разница в вашем примере заключается в том, что вы создаете новый объект ошибки и поднимаете его, а не повторно поднимаете последний.

Матеус Морейра
источник
Интересно. Мой вопрос в том, что если я не поймаю ошибку в определении процесса, тогда мне нужно будет поймать ее, когда я вызываю метод процесса, например:, begin @logo.process; rescue...но тогда я бы не перехватил исключение, запущенное самим процессом, но чего-то, что было вызвано изнутри процесса. Это правильно?
Hommer Smith
2
Это приведет к потере stacktraceисходного исключения, вы, вероятно, захотите включить исключение, causeкоторое доступно в ruby> 2.1
bjhaid
4
@bjhaid Вызов raiseв манере этого ответа полностью сохраняет исходное исключение, включая backtrace. causeне относится к этому случаю. Напротив, он заполняется автоматически, когда rescueблок вызывает новое исключение.
Rep
@HommerSmith: Что делать, если строка до повышения (в данном случае log.error, но это может быть что угодно) не работает? Я думаю об «обеспечении» этого, но внутри гарантии мне нужно будет использовать ссылку на ошибку в качестве аргумента «поднять». Что ты об этом думаешь?
jgomo3 07
1
@ RafałCieślak Каждый раз, когда возникает ошибка, она присваивается $!глобальной переменной. Вызов raiseбез аргументов вызывает ошибку, содержащуюся в $!, фактически вызывая последнюю ошибку. Однако raise errorвызовет ошибку, содержащуюся в errorлокальной переменной, которая может быть, а может и не быть тем же объектом, который содержится в $!. В моем примере $!это то же самое, что и error. Тем не менее, это также возможно:error = Exception.new; raise error
Матеус Морейра
4

Это вызовет ошибку того же типа, что и исходное, но вы можете настроить сообщение.

rescue StandardError => e
  raise e.class, "Message: #{e.message}"
FreePender
источник
Я бы посоветовал ловить StandardError - плохая идея, поскольку он включает в себя все виды функций нижнего уровня и может повесить вашу программу.
Пол Уайтхед
3
Я считаю, что это «исключение» из правила, поскольку мы немедленно снова вызываем исключение.
FreePender
Я вижу, что ты там делал, @FreePender;)
ilasno
1
Я знаю, что прошел год, но можно безопасно поймать StandardError, @PaulWhitehead - это исключение, которое вы никогда не должны ловить. (Быстрый источник: thinkbot.com/blog/rescue-standarderror-not-exception )
RonLugge