Ререйз (той самий виняток) після того, як зловив виняток у Ruby


84

Я намагаюся вдосконалити свої навички 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

Відповіді:


168

Іноді ми просто хочемо знати про помилку сталося , не маючи насправді обробляти помилку.

Часто буває так, що відповідальним за обробку помилок є користувач об’єкта: абонент. Що робити, якщо нас цікавить помилка, але ми не хочемо брати на себе цю відповідальність? Ми виправляємо помилку, робимо все, що нам потрібно, а потім розповсюджуємо сигнал по стеку так, ніби нічого не сталося.

Наприклад, що, якщо ми хочемо записати повідомлення про помилку, а потім дозволити абоненту розібратися з ним?

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

Виклик raiseбез будь-яких аргументів призведе до останньої помилки. У нашому випадку ми піднімаємо error.

У прикладі, який ви подали у своєму запитанні, повторне підняття помилки просто не потрібно. Ви можете просто дозволити йому поширюватися в стеці природним шляхом. Єдина відмінність у вашому прикладі полягає в тому, що ви створюєте новий об’єкт помилки та піднімаєте його замість того, щоб повторно піднімати останній.


Цікаво. Моє питання полягає в тому, що якщо я не вловлюю помилку у визначенні процесу, то мені потрібно буде її вловити, коли я викликаю метод процесу, наприклад:, begin @logo.process; rescue...але тоді я б не ловив виняток, запущений самим процесом, але про те, що було викликано зсередини процесу. Чи правильно це робити?
Хоммер Сміт,

2
Це stacktracecause
втратило

4
@bjhaid Calling raiseу формі цієї відповіді повністю зберігає оригінальний виняток, включаючи backtrace. causeне поширюється на цю справу. Швидше, він заповнюється автоматично, коли rescueблок викликає новий виняток.
повторення

@HommerSmith: Що робити, якщо рядок перед рейзом (log.error у цьому випадку, але це може бути що завгодно) не вдається? Я думаю про те, щоб "забезпечити" його, але, всередині забезпечення, мені потрібно було б використовувати посилання на помилку як аргумент "підняти". Що ти думаєш про це?
jgomo3

1
@ RafałCieślak Кожного разу, коли виникає помилка, вона присвоюється $!глобальній змінній. Виклик raiseбез аргументів викликає помилку, що міститься в $!, фактично викликаючи останню помилку. Однак це raise errorпризведе до помилки, яка міститься в errorлокальній змінній, яка може бути або не бути тим самим об'єктом, що міститься в $!. У моєму прикладі $!це те саме, що error. Однак це також можливо зробити:error = Exception.new; raise error
Матеус Морейра

3

Це спричинить той самий тип помилки, що й оригінал, але ви можете налаштувати повідомлення.

rescue StandardError => e
  raise e.class, "Message: #{e.message}"

Я вважаю, що це погана ідея ловити StandardError, оскільки це включає всі види функцій нижчого рівня і може повісити вашу програму.
Пол Уайтхед,

1
Я вважаю, що це "виняток" із правила, оскільки ми негайно знову піднімаємо виняток.
FreePender
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.