RubyDev Ruby Tutorial #7 >Исключительные ситуации в Ruby и их обработка

мая 23, 2011  |  Published in Ruby, Основы  |  3 Comments

Исключительные ситуации (exceptions) — это ошибки возникшие в вашем коде и которые также представлены в виде специальных объектов. Лично у меня самой популярной ошибкой, благодаря тому, что яне владею слепым набором, является NoMethodError или NameError:

Array.hello
#NoMethodError: undefined method `hello' for Array:Class
#from (irb):3
#from /home/vladimir/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'

hello
#NameError: undefined local variable or method `hello' for main:Object
#from (irb):4
#from /home/vladimir/.rvm/rubies/ruby-1.9.2-p0/bin/irb:17:in `<main>'

Ниже приведен иерархический список всех стандартных исключительных ситуаций в Ruby:

Exception
 NoMemoryError
 ScriptError
   LoadError
   NotImplementedError
   SyntaxError
 SignalException
   Interrupt
 StandardError
   ArgumentError
   IOError
     EOFError
   IndexError
   LocalJumpError
   NameError
     NoMethodError
   RangeError
     FloatDomainError
   RegexpError
   RuntimeError
   SecurityError
   SystemCallError
   SystemStackError
   ThreadError
   TypeError
   ZeroDivisionError
 SystemExit
 fatal

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

def my_method()
  raise "SomeError message ..."
end

my_method
#exceptions.rb:2:in `my_method': SomeError message ... (RuntimeError)
#from exceptions.rb:5:in `<main>'

Давайте разберем сообщение об ошибке. Оно содержит весьма полезную информацию, которая необходима вам для исправления ошибки: где находится ошибка (exceptions.rb:2:in `my_method’), сообщение описывающее ошибку (SomeError message …), тип ошибки (RuntimeError), и место где возникла ошибка (#from exceptions.rb:5:in `<main>’).

Обработка ошибок
Реальная польза от всех этих типов ошибок заключается в возможности их обработки. Обработка ошибок — это код, который выполняется только при условии возникновения ошибок. Код, ошибки в котором следует обрабатывать необходимо заключить в блок begin — end, а отлавливание ошибок следует производить при помощи ключевого слова rescue. Пример:

begin
  100 / 0
rescue
  puts "Fuck! Divider is zero!"
end
#=> Fuck! Divider is zero!

Код после rescue выполнится только после возникновения исключительной ситуации, любой исключительной ситуации! rescue может принимать параметр — тип исключительной ситуации для того, чтобы обрабатывать лишь один определенный тип ошибок, таким образом можно выполнять различный код для различных ошибок. Пример:

begin
  some_undefined_method_call
rescue NameError
  puts "Fuck! Undefined method!"
end
#=>Fuck! Undefined method!

Иногда бывает необходимость выполнить кусок кода независимо от того была ошибка или небыло. Для этого существует ensure. Пример:

begin
  some_undefined_method_call
rescue NameError
  p "Fuck! Undefined method!"
ensure
  p "RubyDev.ru"
end
#=>"Fuck! Undefined method!"
#=>"RubyDev.ru"

Выможете использовать обработчик ошибок rescue и ensure не только в контексте begin — end, но и в контексте любого блока кода, например в контексте метода или класса. Пример:

def hello(msg = "")
  raise "Empty message!" if msg == ""
  puts(msg)
rescue
  puts "Some Error!"
end

hello("Rubydev.ru") #Rubydev.ru
hello #Some Error!

«Кошэрная» обработка ошибок

Чтобы в обработчике ошибок иметь доступ к различной информации об ошибке, необходимо использовать следующий синтаксис:

def hello(msg = "")
  raise "Empty message!" if msg == ""
  puts(msg)
rescue RuntimeError => error
  puts error.inspect
end

hello #<RuntimeError: Empty message!>

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

def hello(msg = "")
  raise "Empty message!" if msg == ""
  puts(msg)
rescue RuntimeError => error
  puts error.message
  puts error.backtrace
end

hello
#=>Empty message!
#exceptions.rb:2:in `hello'
#exceptions.rb:9:in `<main>'

Создание собственных типов ошибок

Глядя на иерархию исключительных ситуаций можно увидить, что все исключительные ситуации происходят от класса Exception. Доказательство:

puts RuntimeError.superclass #StandardError
puts RuntimeError.superclass.superclass #Exception

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

class SomeError < StandardError
  def message
    "Some Error!"
  end
end

raise SomeError #exceptions.rb:7:in `<main>': Some Error! (SomeError)

Лучшая благодарнорность автору — ваши комментарии!

Tags: ,

Responses

  1. cyberf0x says:

    мая 26, 2011 at 11:25 (#)

    Еще нет случая, который очень удобен:

    begin
    # …
    rescue => e
    puts e.message
    end

    И еще одной штуки:

    def method
    10 / 0
    rescue
    puts «Fuck! Divider is zero!»
    end

  2. c0va23 says:

    мая 27, 2011 at 09:12 (#)

    Спасибо, не знал что rescue/ensure можно использовать не только в блоках begin…end.

    PS. Исправьте «Выможете».

  3. Alexander says:

    июня 6, 2011 at 14:05 (#)

    Еще пару мелких трюков:

    У raise есть алиас fail, лично мне он нравится больше :)
    [language]
    fail «Приплыли»
    [/language]

    Хорошо бы указать, что блоки begin / rescue / ensure возвращают значение, как и остальные конструкции языка.

    Например, такой «полухачный» однострочник с rescue:
    [language]
    result = do_smth rescue nil
    [/language]

Leave a Response

Для подсветки кода используйте BB - коды: [language]...[/language], где language может быть: ruby, javascript, css, html.