Стендап Сьогодні
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
Пости з тегом #RubyOnRails
15.02.2023
Історія сьогоднішнього багу
Тільки закінчив випускати хотфікс. Випускати щось в продакшн вночі — завжди цікаво, та завжди з історією. Що ж трапилось на цей раз?
Баг зʼявився у надсиланні пошти. Його помітили ще на стейджингу — чомусь листи не відправлялись з помилкою SMTP 503 5.5.1 Error: authentication not enabled
. Але на стейджингу принаймні я багато уваги цьому не приділив, бо було більше схоже на проблеми з налаштуваннями середовища, ніж на реальну проблему. Але ж на продакшні баг відтворився, та почав перешкоджати надсиланню чесних користувацьких листів. Довелося терміново розвʼязувати.
Перше питання — що взагалі значить ця помилка? З мого досвіду, аутентифікація SMTP потребує шифрування, тобто STARTTLS. Бо як я вже писав, інакше пароль зможуть підгледіти по дорозі. Тож інтуїція каже, що насправді проблема у тому, що клієнт не надсилає команду STARTTLS
. Шукаю параметри підключення та знаходжу: enable_starttls_auto: false
. Так, це дуже схоже на причину. Але цей рядок зʼявився в проєкті роки тому. Як трапилось, що поведінка змінилась зараз?
З нещодавніх змін до проєкту підозрою є оновлення Ruby on Rails. Оновлення мінімальне та стосується тільки патч-версії, але все ж таки це глобальна зміна, яка теоретично може вплинути на будь-що. А конкретно, при перегляді різниці файлу Gemfile.lock
бачу, що помінялася також версія гему mail. Його має сенс перевірити в першу чергу.
І нарешті, в переліку змін гему mail бачу цікавий рядок “Bug Fixes: Regression: accept enable_starttls_auto: false”. Знайшов! (Окреме питання, що написано, що вона unreleased, хоча вже 2 тижні як released. Але буває.)
Тож послідовність подій така: в нас були поламані налаштування. Але через баг у гемі вони не застосовувались. Далі гем виправив баг, ми оновились на виправлену версію, налаштування набули чинності та проявили вже наш баг.
Що робити, щоб такого не було? Напевно, головне, це підтримувати стейджинг в повністю робочому стані, як пріоритет №1. Щоб впевнено шукати помилки, навіть якщо вони не повʼязані очевидно з поточними змінами.
13.11.2024
Рушії для блогу
Взагалі я, звісно, переписував колись блог на новий рушій. Коли я починав щось публікувати, здається, ще взагалі “рушіїв” ніяких безплатних не було, а платити тоді не було чим. Перший сайт був на рукописному HTML.
Втім, я все ж був програмістом, тому знайшов таку цікаву технологію як SSI (включення на стороні сервера), якими можна замінити повторюваний початок та кінець сторінки. Хоч SSI не давав справжніх шаблонів, бо не було ані параметрів, ані “обгорток” - тільки пряме включення.
На початку 2008 в мене зʼявився перший віртуальний сервер, а також перша версія блогу на WordPress. Не памʼятаю, як обрав саме його. Я на той час працював на PHP та й фрілансив для WordPress, тож, мабуть, тому. Здається, в мене була власна тема та чомусь багато всього складного… Хоча найбільший мінус WordPress як я бачу зараз це його вразливість. Для найпопулярнішої платформи в інтернеті, в ній надто багато дірок, чим без кінця користуються спамери. Сайт на WordPress потребує постійної уваги та оновлень. Або просто будеш інколи помічати, що вже пару місяців хтось розсилає пошту з твого VPS/
Іронія в тому, що в тому самому 2008 я почав дивитись на RubyOnRails, а невдовзі й працювати з ним професійно… тож підтримка WordPress подобалася ще менше. Я не знайшов нічого кращого, ніж у 2011 переписати все на Rails.
З плюсів — ламати припинили, код став приємніше (HAML/SASS, між іншим). Блог перетворився на справжній проєкт-хобі. З мінусів - Ruby on Rails значно ненажерливіше. Особливо коли не розробляти сайт з розрахунком на кешування повних сторінок. Бо, якщо чесно, якщо кешувати, то недалеко від наступного рішення…
У 2016 мені набридло підтримувати Rails, та я переписав блог на Hugo - щоб він знову став статичним сайтом. Бо, дійсно, від блогу одного автора не потрібно багато динамічності. До речі, статичність не означає, що в сайті відсутня логіка та програмування; їх тут повно — але весь код виконується в момент компіляції. Головне, від чого довелося позбавитися — це коментарі — а точніше, замінити на зовнішнє рішення. Спочатку на Disqus, а зараз на власний скрипт. Втім, коментарі на сайтах все одно відійшли в минуле.
(Також, очевидно, зникла адміністративна панель та вебредактор. Тут я не бачу проблем, бо на розробку гідної адміністративної панелі часу не вистачало — а у статичного сайту замість неї звичний IDE та Git.)
Виходить, блог я переписував двічі: спочатку на Rails, а потім — з них. За цим трендом у 2023 пора було переписувати наново. Але, я переконаний, що статичний сайт — це найкраще рішення для мене, тому нікуди не поспішаю.
15.01.2025
Чому в Rails крута модель
Вчорашній пост змусив мене знову подумати, чому ж в Ruby on Rails з моделлю працювати зручніше, ніж будь-де. ActiveRecord залишається неперевершеним в простоті використання, навіть для побудови складних та потужних бізнес-процесів.
Отже. В Ruby немає різниці між методами та атрибутами, а точніше, весь публічний інтерфейс обʼєктів є виключно методами. Це приховує силу-силенну складності — аспекти реалізації не обтяжують споживача. Наприклад, User.all.map(&:name)
елегантно та непомітно залізе в базу. (Але краще User.pluck(:name)
, що відразу витягне масив імен.) Фактично в Rails база виглядає, як “внутрішня”, а не “зовнішня” структура даних. Інколи це підштовхує писати неоптимальний код — але, як каже Роб Пайк, не варто оптимізувати код наперед.
Ніякі класи не потребують явно вказаного підключення до бази чи інших параметрів. Практично в будь-якому іншому середовищі використовується Dependency Injection - принаймні для того, щоб класи можна було тестувати без справжньої бази тощо. В Rails всі залежності приховані, а нам ніколи не доводиться про них думати.
Можна подумати, що це робить код Rails неможливим для перевірки. Але, в Ruby весь код живе у спільному просторі імен та доступний для змін під час виконання. Тому практично кожний метод, змінну чи клас можна замінити на тестову копію. Це насправді дуже потужно, та ми постійно цим користуємося. Для прикладу, можна навіть змінювати константи.
Якщо підсумувати, то мова Ruby - королева абстракції. Тут, як ніде, розробник може сам вирішити, які деталі приховати, а які продемонструвати. Навіть зчитати під час виконання схему бази та утворити на основі цієї схеми повну модель даних, яка нічим не відрізняється від коду, написаного вручну - цілком буденне явище.
31.03.2025
Очікування в інтеграційних тестах
Хто що використовує для фіча-тестів? Я вже бозна скільки років на Capybara, та про інші фреймворки знаю тільки поверхнево. Але це показник того, що Capybara така хороша, що для Ruby on Rails нічого більше не потрібно.
Але гадаю, тема сьогодні незалежна від інструмента. Фіча-тести ніколи не є на 100% стабільними. Графічний інтерфейс це вже складно, а коли його автоматизувати, то складність тільки помножується. Тому багато фіча-тестів дають хибу, але не кожного разу — а випадково.
Виправляти хиби важко, бо це ще й доводиться робити пізніше, коли контекст втрачений. (Ідея: якось зробити, щоб тільки що додані тести запускалися багато разів, для виявлення випадкових хиб.) Та що люди першим роблять: додають очікування. У випадку з Capybara це зажди помилка, оскільки Capybara чекає результатів автоматично.
Але то якщо правильно писати тести. Наприклад, Capybara чекатиме тільки всередині перевірок. Якщо просто звертатися до елементів, то невідомо, чого саме чекати. Ще є класична помилка expect(page).not_to have_content
: тут Capybara не знає, що потрібно чекати, поки зміст зникне. Правильна форма - expect(page).to have_no_content
.
До того ж очікування не розвʼязують питання послідовності дій. От виправляв тільки-но помилку, де сценарій інколи стартував ще до того, як компонент React встигне перемалюватися з даними. В такому разі перший крок сценарію затирався. Я собі стільки морочив голову над поведінкою та можливими проблемами з кнопкою SVG, а виявилося, що лише треба було на початку почекати правильного стану.
05.05.2025
Середовища для тестів та для розробки
От вже не знаю, наскільки це поширено, але в Rails в тебе локально завжди є два екземпляри середовища. Одне — для розробки - development
- містить сталі дані. Друге — для тестів - test
- витирається начисто на кожний запуск тестів. В “середовище” завжди входить база даних, але також може бути кеш, набір файлів тощо. Ну, та ще й належна конфігурація.
Це взагалі дуже корисна ідея, та якщо у вас такого нема, раджу спробувати. Хоча не завжди легко й одне середовище зробити, не те що копію. Багато залежить від того, наскільки у вас параметризований код.
Але я навіть не про те хотів сказати. Я, з подачі колеги, ось що згадав: інструменти для налаштування тестового середовища значно зручніше, ніж робочого. Причина проста — тестове середовище ми відтворюємо багато-багато разів, а робоче… ну може коли новий працівник приходить, чи після якоїсь катастрофи. Гарно, коли для розробки є скрипт (в Rails це seeds.rb
), але за досвідом він майже завжди застарілий, бо його майже не використовують. Та й локальне оточення буде застарілим, якщо єдиний спосіб його оновити — це все перестворити наново.
Тому я вже давно, якщо потрібно зробити ілюстрацію якогось стану в застосунку, відтворюю той стан в тестах. Або просто роблю знімок екрана прямо з тесту, який вже написав. Це швидко, а головне, я можу відтворити стан скільки завгодно разів.
А якщо піти далі (що й пропонував колега), то можна запустити інтеграційні тести з відкритим браузером, а посередині поставити на паузу та далі взаємодіяти із застосунком в потрібному стані.
Все це наводить на думку… може до середовища для розробки треба ставитись схоже? Не підтримувати повністю готове оточення, а мати інструменти для його створення — ті ж фабрики, як в тестах. Або додумати підхід із використанням тестів як трампліну для ручної перевірки. Цікаво.
07.05.2025
Опорні конструкції Ruby on Rails
🛤️ Якщо ти ніколи не працював з Ruby on Rails, а тільки дивився, то я гадаю, головні переваги просто залишаться непоміченими. Зокрема, тому, що в Rails безліч зручностей, яких немає в інших середовищах. Ну це як коли несподівано дізнаєшся, що вода з-під крана питна та смачна, та більше не треба думати, де її взяти — цілий шар проблем відпадає.
До мови Ruby можна ставитися по-всякому. Я, як 4 роки тому любив, так досі й люблю. Як і Ruby on Rails. На своєму місці, звісно. Але тоді я писав тільки про якості мови та архітектури застосунку. Які, дійсно, мають власну нішу.
Проте що я залишив неоціненим, це як багато Ruby on Rails допомагає в розробці.
Міграції для бази, рідною мовою та з вбудованою можливістю відкотити, зберегти схему, відтворити будь-де. Конфігурація оточення для розробки, тестів, продакшна. Інтерактивна консоль, де відразу доступні всі можливості застосунку. Генератори коду під все що завгодно. Це все — вже в базовому комплекті.
Також філософія Rails в тому, щоб надати надійну основу для будь-якої задачі. Тому поступово зʼявлялися модулі для: секретів, відкладених задач, завантаження файлів, вебсокетів, тексту з форматуванням. А такі речі, як компіляція JS/CSS, надсилання пошти чи інтернаціоналізація, були “з коробки” ще 15 років тому. Нещодавно зʼявилося офіційне рішення для розгортування через Docker.
Думаю, найпростіше з усім цим можна ознайомитися з Rails Guides.
Якщо ви знаєте фреймворки, де все так само гладко, як у Rails, дуже прошу поділитися, бо я б залюбки мав вибір. Поки доводиться тільки помічати, як всього цього не вистачає, коли його немає.
30.05.2025
Я втомився від Capybara
В Ruby on Rails надто зручна система для інтеграційних тестів — з Capybara. Вона дозволяє керувати браузером з Ruby. Та це чудово. Можна поєднувати в одному файлі будь-які налаштування застосунку та бізнес-логіки та всі можливості браузера.
Бо знаєте, не всякі побічні ефекти можна прочитати не те що з інтерфейсу, а навіть з API. А у нас, оскільки інтеграційні тести повністю на Ruby, то можна все що завгодно перевіряти. Стан бази та інших систем — звісно. Але також легко замокати все що завгодно та перевіряти, що відбулися очікувані виклики.
А ще в Capybara приємний DSL, абстрагований майже від всіх нюансів вебсторінок. (Хоча за потребою можна й спуститися на низький рівень чи навіть виконувати JavaScript.)
Такі зручності формують стиль тестів, де браузер для нас — лише ще один спосіб перевірки. Тобто головний фокус тестів залишається на застосунку, єдина різниця що для цих тестів ми ним керуємо з браузера — на відміну від юніт-тестів, де код викликається напряму, та тестів запитів, де ми робимо прямий запит до API застосунку.
Але браузер — річ складна та хаотична. Абстракція тече, та замість простих “перевірок браузером” ми починаємо латати в ній дірки. Хоч є багато системних покращень (як-от Capybara вже давно вміє автоматично чекати, доки на сторінці зʼявиться зміст, який ми перевіряємо), але код тестів вкривається латками все одно.
Минає час, та ми отримуємо незграбну, незрозумілу масу коду, який ще й все одно регулярно дає хибу.
Який я можу зробити висновок… Capybara була чудовим інструментом на той час, коли JavaScript був рідкістю (так, колись взагалі окремо виділяли тести, яким він потрібний — решта навіть браузер не запускала!) Але зараз фокусом інтеграційних тестів є застосунок JavaScript, та набагато легше було б їх писати не на Ruby, а наближено до браузера — тобто в Cypress чи Playwright тощо. Тим паче, що для них вже давно зробили інтеграції, які вміють запускати фабрики, а то й довільний код на Ruby.