Ruby - wyjątki
Wykonanie i wyjątek zawsze idą w parze. Jeśli otwierasz plik, który nie istnieje, to jeśli nie poradziłeś sobie z tą sytuacją poprawnie, Twój program zostanie uznany za złej jakości.
Program zatrzymuje się, jeśli wystąpi wyjątek. Dlatego wyjątki są używane do obsługi różnego rodzaju błędów, które mogą wystąpić podczas wykonywania programu i podjęcia odpowiednich działań zamiast całkowitego zatrzymania programu.
Ruby zapewnia niezły mechanizm obsługi wyjątków. Umieszczamy kod, który może zgłosić wyjątek w bloku początku / końca i używamy klauzul rescue , aby poinformować Ruby o typach wyjątków, które chcemy obsłużyć.
Składnia
begin
# -
rescue OneTypeOfException
# -
rescue AnotherTypeOfException
# -
else
# Other exceptions
ensure
# Always will be executed
end
Wszystko od początku do ratowania jest chronione. Jeśli podczas wykonywania tego bloku kodu wystąpi wyjątek, kontrola jest przekazywana do bloku między ratowaniem a zakończeniem .
Dla każdej klauzuli rescue w bloku begin Ruby porównuje podniesiony wyjątek z każdym z parametrów po kolei. Dopasowanie powiedzie się, jeśli wyjątek nazwany w klauzuli rescue jest taki sam jak typ aktualnie zgłaszanego wyjątku lub jest nadklasą tego wyjątku.
W przypadku, gdy wyjątek nie pasuje do żadnego z określonych typów błędów, możemy użyć klauzuli else po wszystkich klauzulach rescue .
Przykład
#!/usr/bin/ruby
begin
file = open("/unexistant_file")
if file
puts "File opened successfully"
end
rescue
file = STDIN
end
print file, "==", STDIN, "\n"
Spowoduje to następujący wynik. Możesz zobaczyć, że STDIN jest podstawiany do pliku, ponieważ otwarcie nie powiodło się.
#<IO:0xb7d16f84>==#<IO:0xb7d16f84>
Korzystanie z instrukcji retry
Możesz przechwycić wyjątek za pomocą bloku ratunkowego , a następnie użyć instrukcji retry , aby wykonać blok begin od początku.
Składnia
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
Przykład
#!/usr/bin/ruby
begin
file = open("/unexistant_file")
if file
puts "File opened successfully"
end
rescue
fname = "existant_file"
retry
end
Poniżej przedstawiono przebieg procesu -
- Wystąpił wyjątek podczas otwarcia.
- Poszedł na ratunek. nazwa fname została przeniesiona.
- Ponowna próba przeszła na początek początku.
- Tym razem plik otwiera się pomyślnie.
- Kontynuacja zasadniczego procesu.
NOTE- Zauważ, że jeśli plik o zmienionej nazwie nie istnieje, ten przykładowy kod ponawia nieskończoną liczbę powtórzeń. Zachowaj ostrożność, jeśli używasz ponownej próby dla procesu wyjątku.
Korzystanie z instrukcji raise
Możesz użyć instrukcji raise , aby zgłosić wyjątek. Poniższa metoda wywołuje wyjątek za każdym razem, gdy jest wywoływana. Druga wiadomość zostanie wydrukowana.
Składnia
raise
OR
raise "Error Message"
OR
raise ExceptionType, "Error Message"
OR
raise ExceptionType, "Error Message" condition
Pierwsza forma po prostu ponownie zgłasza bieżący wyjątek (lub błąd RuntimeError, jeśli nie ma bieżącego wyjątku). Jest to używane w programach obsługi wyjątków, które muszą przechwycić wyjątek przed przekazaniem go dalej.
Druga forma tworzy nowy wyjątek RuntimeError , ustawiając jego komunikat na podany ciąg. Ten wyjątek jest następnie podnoszony na stosie wywołań.
Trzecia forma używa pierwszego argumentu do utworzenia wyjątku, a następnie ustawia skojarzony komunikat na drugi argument.
Czwarta forma jest podobna do trzeciej, ale możesz dodać dowolną instrukcję warunkową, na przykład chyba, że chcesz zgłosić wyjątek.
Przykład
#!/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.'
To da następujący wynik -
I am before the raise.
I am rescued.
I am after the begin block.
Jeszcze jeden przykład pokazujący użycie podbicia -
#!/usr/bin/ruby
begin
raise 'A test exception.'
rescue Exception => e
puts e.message
puts e.backtrace.inspect
end
To da następujący wynik -
A test exception.
["main.rb:4"]
Korzystanie z instrukcji zapewnienia
Czasami trzeba zagwarantować, że część przetwarzania zostanie wykonana na końcu bloku kodu, niezależnie od tego, czy został zgłoszony wyjątek. Na przykład, możesz mieć otwarty plik przy wejściu do bloku i musisz się upewnić, że zostanie zamknięty, gdy blok zostanie zamknięty.
Zapewnić klauzula robi tylko to. zapewniają, że następuje po ostatniej klauzuli ratunkowej i zawiera fragment kodu, który zawsze będzie wykonywany po zakończeniu bloku. Nie ma znaczenia, czy blok wychodzi normalnie, czy zgłasza i ratuje wyjątek, czy też jest zakończony przez nieprzechwycony wyjątek, blok zapewniający zostanie uruchomiony.
Składnia
begin
#.. process
#..raise exception
rescue
#.. handle error
ensure
#.. finally ensure execution
#.. This will always execute.
end
Przykład
begin
raise 'A test exception.'
rescue Exception => e
puts e.message
puts e.backtrace.inspect
ensure
puts "Ensuring execution"
end
To da następujący wynik -
A test exception.
["main.rb:4"]
Ensuring execution
Korzystanie z instrukcji else
Jeśli klauzula else jest obecna, następuje po klauzulach ratunkowych i przed jakimkolwiek zapewnieniem .
Treść klauzuli else jest wykonywana tylko wtedy, gdy żaden wyjątek nie został zgłoszony przez główną część kodu.
Składnia
begin
#.. process
#..raise exception
rescue
# .. handle error
else
#.. executes if there is no exception
ensure
#.. finally ensure execution
#.. This will always execute.
end
Przykład
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
To da następujący wynik -
I'm not raising exception
Congratulations-- no errors!
Ensuring execution
Podniesiony komunikat o błędzie można przechwycić za pomocą $! zmienna.
Złap i rzuć
Chociaż wyjątkowy mechanizm podnoszenia i ratowania świetnie nadaje się do przerywania wykonywania, gdy coś pójdzie nie tak, czasami miło jest móc wyskoczyć z jakiejś głęboko zagnieżdżonej konstrukcji podczas normalnego przetwarzania. Tutaj przydaje się łapanie i rzucanie.
Haczyk definiuje blok, który jest oznaczony daną nazwą (który może być symbolem lub String). Blok jest wykonywany normalnie, dopóki nie zostanie napotkany rzut.
Składnia
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
Przykład
W poniższym przykładzie zastosowano rzut do zakończenia interakcji z użytkownikiem, jeśli „!” jest wpisywany w odpowiedzi na dowolny monit.
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:")
Powinieneś wypróbować powyższy program na swoim komputerze, ponieważ wymaga on ręcznej interakcji. To da następujący wynik -
Name: Ruby on Rails
Age: 3
Sex: !
Name:Just Ruby
Wyjątek klasy
Standardowe klasy i moduły Rubiego wywołują wyjątki. Wszystkie klasy wyjątków tworzą hierarchię, z klasą Exception na górze. Następny poziom zawiera siedem różnych typów -
- Interrupt
- NoMemoryError
- SignalException
- ScriptError
- StandardError
- SystemExit
Na tym poziomie jest jeszcze jeden wyjątek, Fatal, ale interpreter języka Ruby używa tego tylko wewnętrznie.
Zarówno ScriptError, jak i StandardError mają wiele podklas, ale nie musimy tutaj wdawać się w szczegóły. Ważne jest to, że jeśli tworzymy własne klasy wyjątków, muszą one być podklasami klasy Exception lub jednej z jej potomków.
Spójrzmy na przykład -
class FileSaveError < StandardError
attr_reader :reason
def initialize(reason)
@reason = reason
end
end
Spójrzmy teraz na poniższy przykład, który użyje tego wyjątku -
File.open(path, "w") do |file|
begin
# Write out the data ...
rescue
# Something went wrong!
raise FileSaveError.new($!)
end
end
Ważną tutaj linią jest podniesienie FileSaveError.new ($!) . Wywołujemy raise, aby zasygnalizować, że wystąpił wyjątek, przekazując mu nowe wystąpienie FileSaveError, z tego powodu, że określony wyjątek spowodował niepowodzenie zapisu danych.