Ruby Исключения

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

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

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

Синтаксис

begin #begin raise.. #вызвать исключение rescue [ExceptionType = StandardException] #Перехватывать исключения указанного типа, по умолчанию — StandardError $! #Представляет информацию об исключении $@ #Представляет место в коде, где возникло исключение else #Остальные исключения .. ensure #Независимо от того, есть ли исключение, выполняется этот блок кода end #end

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

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

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

Примеры

#!/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 #Этот блок перехватит все типы исключений retry #Это переместит управление в начало begin end

Примеры

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

Ниже приведён поток обработки:

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

Примечание:Если переименованный файл не существует, этот пример кода будет пытаться бесконечно. Поэтому при обработке исключений используйте с осторожностьюretry。

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

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

Синтаксис

raiseилиraise "Сообщение об ошибке"илиraise ExceptionType, "Сообщение об ошибке"илиraise ExceptionType, "Сообщение об ошибке" условие

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

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

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

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

Примеры

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

Приведённый выше пример выводит:

I am before the raise.  
I am rescued.  
I am after the begin block.  

Ещё одна демонстрацияraiseпример использования:

Примеры

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

Приведённый выше пример выводит:

A test exception.
["main.rb:4"]

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

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

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

Синтаксис

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

Примеры

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

Приведённый выше пример выводит:

A test exception.
["main.rb:4"]
Ensuring execution

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

Если предусмотрено предложениеelse, оно обычно размещается после предложенияrescue, перед любымensureперед.

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

Синтаксис

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

Примеры

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

Приведённый выше пример выводит:

I'm not raising exception
Congratulations-- no errors!
Ensuring execution

Используя переменную $!, можно захватить сообщение о возбуждённом исключении.

Catch и Throw

Механизм исключений с помощью raise и rescue может прервать выполнение при возникновении ошибки. Иногда во время нормальной обработки нужно выйти из некоторых глубоко вложенных структур. Здесь пригодятся catch и throw.

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

Синтаксис

throw :lablename #.. это не будет выполнено catch :lablename do #.. catch, который будет выполнен после возникновения throw endorthrow :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("Пол: ") # .. #Обработка информации end promptAndGet("Name:")

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

Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby

Класс Exception

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

  • Прервать
  • 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

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

Другие расширения