Исполнение и исключение всегда идут вместе. Если вы открываете файл, который не существует, то если вы не справились с этой ситуацией должным образом, значит, ваша программа плохого качества.
Программа останавливается, если возникает исключение. Таким образом, исключения используются для обработки ошибок различного типа, которые могут возникнуть во время выполнения программы и предпринять соответствующие действия вместо полной остановки программы.
Ruby предоставляет хороший механизм для обработки исключений. Мы заключаем код, который может вызвать исключение, в начало / конец блока и используем спасательные предложения, чтобы сообщить Ruby типы исключений, которые мы хотим обработать.
Синтаксис
begin # - rescue OneTypeOfException # - rescue AnotherTypeOfException # - else # Other exceptions ensure # Always will be executed end
Все от начала до спасения защищено. Если во время выполнения этого блока кода возникает исключение, управление передается в блок между спасением и завершением .
Для каждого пункта спасения в блоке начала Ruby сравнивает возбужденное исключение с каждым из параметров по очереди. Сопоставление будет успешным, если исключение, указанное в предложении восстановления, совпадает с типом создаваемого в данный момент исключения или является суперклассом этого исключения.
В случае, если исключение не соответствует ни одному из указанных типов ошибок, мы можем использовать предложение else после всех предложений спасения .
пример
#!/usr/bin/ruby begin file = open("/unexistant_file") if file puts "File opened successfully" end rescue file = STDIN end print file, "==", STDIN, "\n"
Это даст следующий результат. Вы можете видеть, что STDIN заменяется на файл, потому что открыть не удалось.
#<IO:0xb7d16f84>==#<IO:0xb7d16f84>
Использование оператора повтора
Вы можете перехватить исключение, используя блок восстановления, а затем использовать оператор retry для запуска блока begin с самого начала.
Синтаксис
begin # Exceptions raised by this code will # be caught by the following rescue clause rescue # This block will capture all types of exceptions retry # This will move control to the beginning of begin end
пример
#!/usr/bin/ruby begin file = open("/unexistant_file") if file puts "File opened successfully" end rescue fname = "existant_file" retry end
Ниже приведен поток процесса —
- Исключение произошло при открытии.
- Пошел спасать. Fname был переназначен.
- По повторным попыткам пошли к началу начала.
- На этот раз файл открывается успешно.
- Продолжение необходимого процесса.
ПРИМЕЧАНИЕ. — Обратите внимание, что если файл с замененным именем не существует, этот пример кода повторяется бесконечно. Будьте осторожны, если вы используете повтор для процесса исключения.
Использование рейза Statement
Вы можете использовать оператор поднятия, чтобы вызвать исключение. Следующий метод вызывает исключение всякий раз, когда он вызывается. Это второе сообщение будет напечатано.
Синтаксис
raise OR raise "Error Message" OR raise ExceptionType, "Error Message" OR raise ExceptionType, "Error Message" condition
Первая форма просто повторно вызывает текущее исключение (или RuntimeError, если нет текущего исключения). Это используется в обработчиках исключений, которым необходимо перехватить исключение перед его передачей.
Вторая форма создает новое исключение RuntimeError , устанавливая его сообщение в заданную строку. Это исключение затем поднимается вверх по стеку вызовов.
Третья форма использует первый аргумент для создания исключения, а затем устанавливает связанное сообщение со вторым аргументом.
Четвертая форма похожа на третью форму, но вы можете добавить любое условное утверждение, например, если только не вызвать исключение.
пример
#!/usr/bin/ruby begin puts 'I am before the raise.' raise 'An error has occurred.' puts 'I am after the raise.' rescue puts 'I am rescued.' end puts 'I am after the begin block.'
Это даст следующий результат —
I am before the raise. I am rescued. I am after the begin block.
Еще один пример, показывающий использование рейза —
#!/usr/bin/ruby begin raise 'A test exception.' rescue Exception => e puts e.message puts e.backtrace.inspect end
Это даст следующий результат —
A test exception. ["main.rb:4"]
Используя обеспечить заявление
Иногда вам нужно гарантировать, что некоторая обработка выполняется в конце блока кода, независимо от того, было ли вызвано исключение. Например, у вас может быть файл, открытый при входе в блок, и вам нужно убедиться, что он закрывается при выходе из блока.
Пункт гарантии делает именно это. гарантированно идет после последнего предложения спасения и содержит кусок кода, который всегда будет выполняться после завершения блока. Не имеет значения, если блок завершается нормально, если он вызывает и спасает исключение, или если он завершается неперехваченным исключением, будет запущен блок обеспечения .
Синтаксис
begin #.. process #..raise exception rescue #.. handle error ensure #.. finally ensure execution #.. This will always execute. end
пример
begin raise 'A test exception.' rescue Exception => e puts e.message puts e.backtrace.inspect ensure puts "Ensuring execution" end
Это даст следующий результат —
A test exception. ["main.rb:4"] Ensuring execution
Использование else Statement
Если присутствует условие else , оно идет после пунктов спасения и перед любой гарантией .
Тело предложения else выполняется только в том случае, если основная часть кода не вызывает никаких исключений.
Синтаксис
begin #.. process #..raise exception rescue # .. handle error else #.. executes if there is no exception ensure #.. finally ensure execution #.. This will always execute. end
пример
begin # raise 'A test exception.' puts "I'm not raising exception" rescue Exception => e puts e.message puts e.backtrace.inspect else puts "Congratulations-- no errors!" ensure puts "Ensuring execution" end
Это даст следующий результат —
I'm not raising exception Congratulations-- no errors! Ensuring execution
Поднятое сообщение об ошибке может быть записано с помощью $! переменная.
Поймать и бросить
Несмотря на то, что механизм исключений при поднятии и спасении отлично подходит для отказа от выполнения, когда что-то идет не так, иногда приятно иметь возможность выпрыгнуть из глубоко вложенной конструкции во время обычной обработки. Это где ловить и бросать пригодится.
Функция catch определяет блок, помеченный данным именем (это может быть Symbol или String). Блок выполняется нормально, пока не встретится бросок.
Синтаксис
throw :lablename #.. this will not be executed catch :lablename do #.. matching catch will be executed after a throw is encountered. end OR throw :lablename condition #.. this will not be executed catch :lablename do #.. matching catch will be executed after a throw is encountered. end
пример
В следующем примере команда throw завершает взаимодействие с пользователем, если ‘!’ набирается в ответ на любое приглашение.
def promptAndGet(prompt) print prompt res = readline.chomp throw :quitRequested if res == "!" return res end catch :quitRequested do name = promptAndGet("Name: ") age = promptAndGet("Age: ") sex = promptAndGet("Sex: ") # .. # process information end promptAndGet("Name:")
Вы должны попробовать вышеуказанную программу на вашем компьютере, потому что она требует ручного взаимодействия. Это даст следующий результат —
Name: Ruby on Rails Age: 3 Sex: ! Name:Just Ruby
Исключение класса
Стандартные классы и модули Ruby порождают исключения. Все классы исключений образуют иерархию с классом Exception вверху. Следующий уровень содержит семь различных типов —
- Прерывание
- NoMemoryError
- SignalException
- Ошибка скрипта
- Стандартная ошибка
- 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 # Write out the data ... rescue # Something went wrong! raise FileSaveError.new($!) end end
Важной строкой здесь является повышение FileSaveError.new ($!) . Мы вызываем функцию указывает, что произошло исключение, передавая ему новый экземпляр FileSaveError, причём причина в том, что конкретное исключение вызвало сбой записи данных.