Учебники

Ruby — исключения

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

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

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

Синтаксис

begin  
# -  
rescue OneTypeOfException  
# -  
rescue AnotherTypeOfException  
# -  
else  
# Other exceptions
ensure
# Always will be executed
end

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

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

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

пример

Live Demo

#!/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 , устанавливая его сообщение в заданную строку. Это исключение затем поднимается вверх по стеку вызовов.

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

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

пример

Live Demo

#!/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.  

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

Live Demo

#!/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

пример

Live Demo

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

пример

Live Demo

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, причём причина в том, что конкретное исключение вызвало сбой записи данных.