В Ruby винятки представлені класами. Клас Exception є батьківським класом для всіх винятків, що виникають в Ruby. Клас StandardError є підкласом Exception та є батьківським класом для більшості винятків, що виникають в звичайній роботі програми. Існує також клас RuntimeError, який є підкласом StandardError та використовується для позначення винятків, що виникають під час виконання програми.
Для обробки винятків в Ruby використовується конструкція begin-rescue-ensure. Блок begin містить код, який може призвести до виникнення винятку. Блок rescue містить код, який обробляє виняток, якщо він виникає. Блок ensure містить код, який виконується незалежно від того, чи виник виняток чи ні.
Наприклад, уявімо, що ми маємо метод, який намагається отримати зв'язок з віддаленим сервером:
У цьому методі можуть виникати винятки, які вказують на невдалу спробу підключення до сервера. Щоб обробити ці винятки, ми можемо використати конструкцію rescue.
У прикладі використано конструкцію begin/rescue, де код, який може викликати винятки, поміщений в блок begin, а код обробки помилок - в блок rescue. Кожен блок rescue відповідає за обробку конкретного типу винятку.
У прикладі розглянуто такі винятки:
Крім того, ми можемо використовувати ключове слово ensure для виконання певних дій після обробки винятків. Наприклад, ми можемо закрити сокет незалежно від того, чи виникла помилка, за допомогою блоку ensure:
У цьому прикладі, якщо підключення до сервера не вдалося через помилку ETIMEDOUT, ми виводимо повідомлення про помилку. Якщо підключення відхилене сервером через помилку ECONNREFUSED, ми також виводимо повідомлення про помилку. Незалежно від того, чи виникла помилка, ми закриваємо сокет за допомогою блоку ensure.
Окрім ключового слова rescue, Ruby надає також ключове слово retry, яке дозволяє повторно виконати блок коду, що породив виняток, після відповідної обробки. Наприклад:
У цьому прикладі метод read_data_from_socket читає дані з сокета за допомогою методу read_nonblock, який може породжувати виняток IO::WaitReadable або IO::WaitWritable, якщо дані не готові для читання або запису. У випадку винятку ми спочатку збільшуємо лічильник спроб, а потім за допомогою конструкції rescue виконуємо обробку винятку. У випадку IO::WaitReadable або IO::WaitWritable ми просто збільшуємо лічильник спроб і повертаємось до початку циклу. У випадку будь-якого іншого винятку ми виконуємо необхідні дії, такі як запис інформації про виняток у журнал логу або відправку повідомлення про помилку користувачеві, і виходимо з циклу.
У даному прикладі ми використовуємо конструкцію rescue для обробки винятків IO::WaitReadable, IO::WaitWritable і будь-якого іншого винятку. Крім того, ми використовуємо ключове слово ensure для закриття сокета незалежно від того, чи виникла помилка, а також для виконання будь-яких інших дій, які мають бути виконані після обробки винятків.
Обробка винятків є важливою частиною будь-якої програми, яка може виникнути помилка. В Ruby існує багато вбудованих винятків, які дозволяють програмісту точно вказати, що саме сталося неправильно, і виконати необхідні дії для відновлення роботи програми. Використання конструкції begin-rescue-ensure дозволяє елегантно обробляти винятки і забезпечувати безперебійну роботу програми. Винятки можуть виникати з будь-яких причин, таких як помилки вводу-виводу, проблеми з мережею, помилки в обчисленнях, і так далі. Від обробки винятків залежить поведінка програми під час виникнення помилок.
Один з підходів до обробки винятків в Ruby полягає в тому, щоб використовувати багато різних видів винятків для вказівки на різні види помилок. Ruby має вбудований набір винятків, таких як StandardError, ArgumentError, TypeError, ZeroDivisionError, NameError та багато інших.
Ось приклад коду, який демонструє використання конструкції begin-rescue-ensure:
У цьому прикладі, якщо код, який знаходиться у блоках begin-rescue, породжує виняток ArgumentError, виконується блок коду, який знаходиться у першому rescue-блоці. Якщо виняток відповідає ZeroDivisionError, виконується другий rescue-блок. Код, який знаходиться у блоку ensure, виконується незалежно від того, чи виникла помилка.
Для кращого керування винятками, можна використовувати ключове слово raise для виклику винятку вручну:
У цьому прикладі, якщо y дорівнює нулю, викликається виняток ArgumentError з повідомленням про те, що y не може дорівнювати нулю.
Крім того, можна використовувати спеціальну форму rescue, яка виконується лише для певного типу винятку:
У цьому прикладі код відловлює два типи винятків - TypeError та ArgumentError, і для кожного з них передбачена окрема обробка. Це дозволяє більш детально контролювати обробку винятків і забезпечити більш точне визначення проблеми, яка виникла в програмі.
Крім того, можна використовувати спеціальний блок else, який виконується, якщо виняток не було відловлено в жодному зі зазначених блоків rescue:
У цьому прикладі код в блоку else виконається, якщо виняток не було відловлено в жодному з блоків rescue.
Нарешті, в Ruby є можливість власного визначення типів винятків. Для цього можна створити власний клас винятку, який наслідується від стандартного класу Exception або одного з його підкласів. Для визначення власних типів винятків зазвичай використовуються класи StandardError або RuntimeError.
Після визначення класу винятку його можна використовувати в блоках rescue:
В цьому прикладі обробляється власний виняток типу MyCustomError.
Загалом, в Ruby існує багато способів обробки винятків, і вибір конкретного способу залежить від потреб вашої програми. Зокрема, в Ruby є декілька вбудованих класів винятків, які можна використовувати для обробки різних видів помилок. Наприклад, клас StandardError є базовим класом для більшості винятків, які можуть виникнути в Ruby, і він може бути використаний як перехоплювач для більшості винятків.
Крім того, можна визначати власні класи винятків для обробки специфічних помилок, які можуть виникнути в вашій програмі. Наприклад, якщо ваша програма працює з базою даних, ви можете визначити власний клас винятку, щоб обробляти помилки, пов'язані зі з'єднанням з базою даних.
Крім того, в Ruby є декілька інших способів обробки винятків, таких як використання методу catch і throw для обробки неконтрольованих винятків або використання методу rescue_from в Rails для обробки винятків, які виникають у веб-додатку.
У будь-якому випадку, правильна обробка винятків є важливим аспектом написання стійких і надійних програм. Використання конструкції begin-rescue-ensure дозволяє елегантно обробляти винятки і забезпечувати безперебійну роботу програми, а визначення власних класів винятків дозволяє більш точно і специфічно обробляти помилки, які виникають у вашій програмі.
Найважливіше - не ігнорувати винятки і завжди обробляти їх у вашому коді.
Додаткові джерела на вивчення матеріалу з сьогоднішнього уроку:
Exception (винятки) додатковий матеріал
Це завдання дозволить вам навчитись використовувати конструкцію begin-rescue-end для обробки винятків, а також зрозуміти, які помилки можуть виникати при роботі з файлами в Ruby.
Сьогоднішній урок файлом - лінк.
Чат для спілкування - лінк.
Група в телеграмі: https://t.me/ruby4you
Автор курсу: Шкоропад Даниїл