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

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

Ruby обеспечивает хороший механизм обработки исключений. Мы прилагаем код, который может вызвать исключение в блоке begin/end и использовать предложения rescue, чтобы сообщить Ruby о типах исключений, которые мы хотим обработать.

Синтаксис

begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
else
# другие исключения
ensure
# Всегда будет выполняться
end

 

Все от begin до rescue защищено. Если во время выполнения этого блока кода возникает исключение, управление передается блоку между rescue и end.

Для каждого предложения rescue в begin Ruby сравнивает поднятое исключение с каждым из параметров по очереди. Совпадение завершится успешно, если исключение, указанное в предложении rescue, совпадает с типом создаваемого исключения или является суперклассом этого исключения.

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

Пример

#!/usr/bin/ruby
begin file = open("/unexistant_file") if file puts "Файл успешно открыт " end
rescue file = STDIN
end
print file, "==", STDIN, "n"

 

Это приведет к следующему результату. Вы можете видеть, что STDIN заменяется file, потому что произошла ошибка открытия.

#<IO:0xb7d16f84>==#<IO:0xb7d16f84>

Использование утверждения Retry

Вы можете захватить исключение, используя rescue, а затем использовать заявление retry для выполнения блока begin с самого начала.

Синтаксис

begin #Исключения в этом коде #поймал следующий пункт rescue
rescue # Этот блок будет захватывать Все типы исключений retry # Это переместит управление в начало <i>begin</i>
end

Пример

#!/usr/bin/ruby
begin file = open("/unexistant_file") if file puts "Файл успешно открыт" end
rescue fname = "existant_file" retry
end

 

Ниже приводится поток процесса:

  • Исключение произошло при открытии.
  • Вызвано rescue. fname было переназначено.
  • retry указал на начало begin.
  • Этот файл открывается успешно.
  • Продолжал необходимый процесс.

ПримечаниеОбратите внимание, что если файл повторно замещенного имени не существует, то этот примерный код будет повторятся бесконечно. Будьте осторожны, если вы используете повтор для процесса исключения.

Использование выражения raise

Вы можете использовать заявление raise чтобы сгенерировать исключение. Следующий метод вызывает исключение всякий раз, когда он вызывается. Будет напечатано второе сообщение.

Синтаксис

raise
OR
raise "Error Message"
OR
raise ExceptionType, "Error Message"
OR
raise ExceptionType, "Error Message" condition

 

Первая форма просто повторно вызывает текущее исключение (или RuntimeError, если нет текущего исключения). Используется в обработчиках исключений, которые должны перехватывать исключение, прежде чем передавать его.

Вторая форма создает новое исключение RuntimeError, устанавливая его сообщение для данной строки. Это исключение затем поднимает стек вызовов.

Третья форма использует первый аргумент для создания исключения, а затем устанавливает связанное сообщение во второй аргумент.

Четвертая форма похожа на третью форму, но вы можете добавить любой условный оператор, например, если не возбуждать исключение.

Пример

#!/usr/bin/ruby
begin puts 'Код перед raise.' raise 'Произошла ошибка.' puts 'Код после raise.'
rescue puts 'Код в rescued.'
end
puts 'Код после блока begin.'

 

Это приведет к следующему результату:

Код после raise.
Код в rescued.
Код после блока begin. 

 

Еще один пример, показывающий использование raise:

#!/usr/bin/ruby
begin raise 'Тестовое исключение.'
rescue Exception => e puts e.message puts e.backtrace.inspect
end

 

Это приведет к следующему результату:

Тестовое исключение.
["main.rb:4"]

Использование инструкции ensure

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

Предложение ensure делает именно это. обеспечивается после последнего предложения rescue и содержит кусок кода, который всегда будет выполняться по завершении блока. Не имеет значения, нормально ли завершиться блок, если есть исключения raises и rescues, или если он завершается исключением, блок ensure будет запущен.

Синтаксис

begin #.. процесс #..поднять исключение
rescue #.. ошибка обработки
ensure #.. наконец, убедитесь в выполнении #.. Это всегда будет выполняться.
end

Пример

begin raise 'Тестовое исключение.'
rescue Exception => e puts e.message puts e.backtrace.inspect
ensure puts "Обеспечение выполнения"
end

 

Это приведет к следующему результату:

Тестовое исключение.
["main.rb:4"]
Обеспечение выполнения

Использование инструкции else

Если предложение else присутствует, оно выполняется после предложений rescue и до того, как оно будет выполнено.

Тело предложения else выполняется только в том случае, если в основной части кода не возникают исключения.

Синтаксис

begin #.. процесс #.. вызывается исключение
rescue # .. ошибка обработки
else #.. выполняется, если нет исключения
ensure #.. наконец, убедитесь в выполнении #.. Это всегда будет выполняться.
end

 

Пример

begin # raise 'Тестовое исключение.' puts "Я не поднимаю исключение"
rescue Exception => e puts e.message puts e.backtrace.inspect
else puts "Поздравляем - ошибок нет!"
ensure puts "Обеспечение выполнения"
end

 

Это приведет к следующему результату:

Я не поднимаю исключение
Поздравляем - ошибок нет!
Обеспечение выполнения

 

Сообщение “Поднятая ошибка” можно записать с помощью переменной $!.

Catch и Throw

В то время как механизм исключения и rescue отлично подходит для отказа от выполнения, когда что-то идет не так, иногда бывает приятно выпрыгнуть из какой-то глубоко вложенной конструкции во время нормальной обработки. Это – то, где может пригодится catch и throw.

catch определяет блок, который помечен с данным именем (которое может быть символ или строка). Блок выполняется нормально до тех пор, пока не будет обнаружен throw.

Синтаксис

throw :lablename
#.. это не будет выполнено
catch :lablename do
#.. соответствующий catch будет выполняться после обнаружения throw.
end
OR
throw :lablename condition
#.. это не будет выполнено
catch :lablename do
#.. соответствующий catch будет выполняться после обнаружения throw.
end

Пример

В следующем примере используется throw для прекращения взаимодействия с пользователем, если ‘!’ набирается в ответ на любое приглашение.

def promptAndGet(prompt) print prompt res = readline.chomp throw :quitRequested if res == "!" return res
end
catch :quitRequested do name = promptAndGet("Имя: ") age = promptAndGet("Возраст: ") sex = promptAndGet("Пол: ") # .. # process information
end
promptAndGet("Имя:")

 

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

Имя: Ruby on Rails
Возраст: 18
Пол: !
Имя: AndreyEx

Класс Exception

Стандартные классы и модули Ruby вызывают исключения. Все классы исключений образуют иерархию с классом Exception в верхней части. Следующий уровень содержит семь разных типов:

  • Interrupt
  • NoMemoryError
  • SignalException
  • ScriptError
  • StandardError
  • SystemExit

На этом уровне есть еще одно исключение, Fatal, но интерпретатор Ruby использует это только внутренне.

И ScriptError, и StandardError имеют ряд подклассов, но здесь нам не нужно вдаваться в подробности. Важно то, что если мы создаем собственные классы исключений, они должны быть подклассами любого класса Exception или одного из его потомков.

Давайте посмотрим на пример:

class FileSaveError < StandardError attr_reader :reason def initialize(reason) @reason = reason end
end

 

Теперь рассмотрим следующий пример, который будет использовать это исключение:

File.open(path, "w") do |file|
begin # Запишите данные ...
rescue # Что-то пошло не так! raise FileSaveError.new($!)
end
end

 

Важной линией здесь является поднять FileSaveError.new ($!). Мы вызываем raise, чтобы сигнализировать о том, что произошло исключение, передав ему новый экземпляр FileSaveError, по причине того, что конкретное исключение вызвало сбой записи данных.

 

Источник: AndreyEx.ru