Стендап Сьогодні

Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті

Підписатись на RSS · 📢 Канал в Telegram @stendap_sogodni

01.09.2024

Спостереження за змінами на сайтах

Купа задач зводиться до спостереження за сайтами. Оновлення, розпродажі, оголошення — не завжди нам повідомлять про них напряму. Перевіряти вручну — непрактично.

Тобто, гарно, коли є підписка на RSS, або поштою, або принаймні надійна стрічка в соціальній мережі. Зокрема, я підписаний на RSS декількох сторінок “Releases” на GitHub. (Для того до адреси сторінки просто дописуємо .atom.)

Але на жаль, не у всіх воно є та працює надійно. У нас нещодавно був випадок, що офіційна розсилка деякого продукту про нову версію “забула”, а сторінка змін оновилася. На щастя, було налаштоване спостереження за сторінкою через Zapier, тому версію не проґавили.

Спостереження за змінами — на перший погляд, найпростіше, що може бути. От тільки проблема в тому, що більшість вебсторінок змінюються постійно — можливо, при кожному перезавантаженні — тож доведеться розумно обмежувати область порівняння. Якщо вже робити перевірку власноруч, то я б обмежувався змістом конкретного селектора, або присутністю регулярного виразу в тексті, а не в коді.

Є гарний продукт з відкритим кодом changedetection.io. Він вміє відстежувати частину сторінки, а ще аналізувати інформацію про товари. Що, до речі, дуже потужний спосіб заощадити на не терміново потрібних речах. Закинув вудку та чекаєш на приємний сюрприз. Є в них і платний SaaS.


31.08.2024

Fish Shell

🐠 Є багато текстових оболонок, гарних та цікавих. В мене - Fish. Це, певно, третя чи четверта за популярністю оболонка після Bash та Zsh.

Девіз Fish - “оболонка для 90-х” - непогано підкреслює її особливості. Тобто порівняно з Bash чи Zsh, вона має приємніший синтаксис та деякі цікаві можливості як вебінтерфейс для налаштувань. Але також тут немає нічого революційного — оболонка як оболонка, більшість команд та підходів збігаються.

Як одна з все ж популярних оболонок, Fish користується підтримкою у більшості доповнень (як direnv) та терміналів (як VSCode).

Також для Fish є декілька менеджерів плагінів, в мене встановлений fisher. (Плагін — то просто набір конфігураційних файлів.) До речі, подобається в fish зручний та зрозумілий каталог конфігурації. Як завжди, багато є різних плагінів для красивого рядка команди, але в мене він спрощений максимально, бо я користуюся терміналом через VSCode.

Мова скриптів Fish досить зручна, я час від часу дописую щось своє. Хоча скрипти для проєктів все одно всі на Bash, щоб були переносними. Так що доводиться памʼятати ще одну рідко використану мову.

Взагалі, чого мені не вистачає в терміналі, це повноцінного редактора командного рядка, де можна виділяти, копіювати, і таке інше. Не знаю, де його таке шукати — може, ви знаєте?


30.08.2024

Попередній перегляд постів в Telegram

Почав з реалізації попереднього перегляду для Telegram, бо тут вже більше коду готово. Виявилося, базовий тестовий скрипт не так вже й складно зробити. Чим воно відразу відрізняється від звичайної функціональності Hugo - замість всього сайту перегляд працює тільки з одним постом. Звісно, того ми й хочемо, але: тоді ще й потрібно щоб користувач обрав пост, над яким працює. (Або ні… є ідеї.)

В основі лежить спостереження за змінами в файлах; випробував новий для мене модуль fsnotify для Go. Він робить приблизно те, що від нього й очікуєш; хіба що не вміє стежити за деревом каталогів — але поки мені взагалі достатньо й одного файлу.

Далі, в тестовому каналі в Telegram створюю порожній пост. (Можна було б навіть брати останній, але технічно простіше створювати новий на кожний запуск скрипту.) Пост можна скільки завгодно редагувати, причому він оновлюється в клієнті наживу, навіть з прокруткою до нового змісту.

Залишається отримувати події про зміни з fsnotify, перемальовувати пост (тобто конвертувати з Markdown в Telegram HTML), та надсилати через API. Обмеження на частоту запитів значення не мають. Єдине, що Telegram не дозволяє надсилати новий зміст такий самий, як наявний. Але то дрібниці.

Певно, найскладнішою частиною було видерти з того скрипту, що вже є, саме промальовку. Бо, уявляєте, там водночас відбувається і промальовка, і валідація, і таке інше. Поки рішення на рівні “накопіпастив та повидаляв зайве”. Взагалі з публікацією в декілька сервісів буде цікаво, бо наразі читання метаданих та конвертація Markdown в HTML логічно спарені.

Поки дуже задоволений, прототип вже спрощує життя.


29.08.2024

Публікація в Mastodon: попередній перегляд

Публікація в Mastodon в мене трохи загальмувала, хоч технічно рішення ніби готове. Тобто перетворення поста на HTML для Mastodon та публікація готова.

Мій сервер у Федіверсі - GoToSocial - поки не вміє редагувати пости. Взагалі у Федіверсі редагування — не така вже й проста дія, бо потребує реплікації на інші сервери. Втім, в Mastodon для редагування є API.

Отже. Так чи інакше, перед публікацією пости варто переглянути (та відкоригувати.) В Telegram я робив це “наживу”, бо мій скрипт вміє редагувати вже дуже давно — тож я можу опублікувати, а потім зробити декілька правок. Щоб це помітити, треба прямо уважно дивитися на свіжий пост. (Хоча, сповіщення отримують початковий текст. Також міст в RSS може зберегти старий текст, якщо йому пощастить.)

А з Mastodon такої можливості не буде. Навіть якщо редагування є, я боюся що по децентралізованій системі розповзуться недороблені варіанти поста. Тому, зробити кроспостинг заважає відсутність попереднього перегляду. Особливо якщо винести публікацію на CI, щоб поєднати з публікацією вебсайта.

До речі, рушій мого сайту - Hugo - містить чудовий локальний сервер з живим перезавантаженням. От щось схоже хочу зробити для Telegram та для Mastodon.


28.08.2024

Структурне мислення та мапи думок

Приблизно відтоді, як відкрив для себе Obsidian Canvas, в моєму житті відбулася просто революція когнітивних мап. Це прям помітно розширює здібності.

Я взагалі звик до текстових документів чи списків-нарисів. Мапа мені зручніше, бо вона не є лінійною. Пробував після мап робити нариси — вони скоріше втрачають ясність та стають стіною тексту, який потрібно читати, щоб знайти місце. А в мапі місце часто запамʼятовується візуально. Так би мовити, складність O(1) замість O(N).

Це важливо, бо наші думки лінійні — наступна заміняє попередню. А предметна область ніколи не лінійна. Коли ми думаємо про будь-який предмет, то неминуче стрибаємо від одного до іншого аспекту. Це робити незрівнянно легше, коли фіксуєш думки в мапу.

По-перше, не доводиться тримати в голові весь “стек” того, про що можна подумати. Так голова вільніша думати “вперед”, а не “назад”. По-друге, ще й нічого не забудеш. Мапа залишається як нагадування про всі можливі напрямки міркувань.

Ефект такий, що коли я помічаю, що ментально “буксую”, то простий процес побудови мапи думок приносить ясність. Причому доходиш до розуміння, на яке до того навіть натяку не було, ось що найцікавіше.

Я зазвичай бачу приклади мап думок як щось завершене, як конспект. Треба розуміти, що найбільше користі від них в процесі; тому всім раджу спробувати власноруч. Софт шукати не обовʼязково — вистачить аркушу A4.


27.08.2024

Сповіщення про закінчення задачі

Навздогін до попереднього посту — запуск тривалих тестів завжди полегшується, якщо закінчення легко помітити. Інакше ризикуємо відвертати увагу та згадувати про тести надто пізно (тобто рідко.) Так само і з іншими задачами. Навіть герої канонічного коміксу XKCD ніяк не здогадаються, що пора йти працювати.

Я колись писав про використання системних сповіщень та це гарний варіант. Проте не єдиний — є й такі, що не потребують спеціальних утиліт для створення сповіщень.

Команда say в MacOS вміє промовити голосом буквально будь-який текст. Хоч статичне, хоч say "$?", щоб дізнатися код результату, хоч взагалі промовити весь вихід з нашої задачі: make test | say.

Якщо копнути ще глибше, то стандартні термінали “посигналять”, якщо їм зустрінеться символ BEL. Його можна отримати різним чином, наприклад, tput bel або echo \7. Хоча сьогодні помітив, що VSCode так зробить тільки якщо увімкнути відповідну опцію accessibility.signals.terminalBell. Ну, та й сигнал менш помітний, ніж say.

Також є цікаве рішення на той випадок, якщо команда вже запущена, займає більше часу, ніж очікувалось, та ти хочеш відійти, але дізнатись про завершення. Тут треба відправити задачу в фон традиційною комбінацією Ctrl+Z, а потім повернути з фону командою fg з додаванням потрібного “хвоста”: fg; say "all done".


26.08.2024

Випадково невдалі тести

Що ви робите з випадково невдалими тестами? Роки йдуть, а ця неприємність залишається поруч. Хоча причина, принаймні в мене, змінилася. Колись головною проблемою були побічні ефекти та взаємовплив тестів. Через те досі в Ruby запускаємо тести у випадковому порядку. Але, сучасні тести дають хибу через асинхронні події в самому тесті. Головним чином це тільки інтеграційні тести. З одного боку, їх мало, що гарно; з іншого — вони довгі. Що погано.

Раніше я був тієї думки що випадково невдалі тести потрібно збирати, відстежувати частоту та виправляти за критичністю. Зараз бачу, що такі списки застарівають. Натомість можна просто відкрити перелік останніх невдалих запусків CI, та виправляти просто по черзі. (В GitHub Actions, наприклад, можна відфільтрувати запуски за статусом та гілкою.)

Далі беру тест та запускаю його по колу локально, скриптом на кшталт такого:

for i in (seq 0 100000); echo $i; runtest; or break; end

(Це мова оболонки fish, якщо що.) Так набагато зручніше ніж запускати повторно вручну. Можна поставити та займатись чимось іншим. А якщо тест не падає 100, 1000 разів, можна зробити висновок, що він не такий вже й невдалий. (Тому немає великого сенсу заздалегідь вести статистику.)

Коли зрозуміло, де саме тест падає, можна розсипати там максимум логів. Бо якщо причиною є випадковий збіг обставин, побачити його пізніше буде важко. Навіть коли пізніше — це через десять секунд.

Залишається знайти причину та виправити… Хоча це й нелегко, але все ж чомусь найскладніше це досягти відтворення тесту з помилкою.


25.08.2024

Функціональні структури та Redux

Може я вчора був надто критичним до функціональних структур. Насправді їх сила точно не в одноразовій обробці величезного файлу. Бо така обробка все одно перетворює всі дані, тобто немає нагоди нічого заощадити на незмінності.

А насправді функціональні структури сяють там, де змінюються поступово та частково. З моїх задач в таке означення вписується Redux - бібліотека функціонального стану для React (та не тільки). Тут дійсно є і велика структура даних, і поступові та дуже ізольовані її зміни.

Ще кориснішою незмінність буде при застосуванні Reselect, бо там ефективність спирається саме на рівність вхідних даних за значенням, а бібліотека Immutable як раз реалізує таку рівність для власних структур. (Бо без неї треба чимало робити самотужки, я про це колись писав статтю). Тобто тут ми вже заощаджуємо на тому, що робимо менше обчислень завдяки мемоїзації.

Залишається питання — чи пришвидшить Immutable роботу застосунку з Redux? Треба буде перевірити — це вже дійсно практична ситуація. (Та колись давно я навіть працював на проєкті де був Immutable+Redux, так що це рішення не нове.)

PS: знайшов цікавий проєкт The Benchmarks Game, який порівнює більш ніж 20 мов на декількох простих прикладах. Зокрема, там OCaml та Haskell виходять на такому самому рівні як Swift чи Node.js. А, до речі, Node.js виглядає як найшвидша з популярних “високорівневих” мов.


24.08.2024

Функціональні структури даних

Теоретична частина: поставив собі питання — яким чином функціональні мови здатні… функціонувати, коли вони ніби повинні без кінця копіювати значення для збереження незмінності? Це ж повинно бути надзвичайно неефективно, особливо коли структури даних більшають.

Коротка відповідь: функціональні мови використовують особливі структури даних, які зокрема дозволяють зберігати незмінену частину без копіювання. Як виявилося, існує величезна академічна та практична база за цим питанням.Направляю у вікіпедію за подробицями.

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

Виходить, що чисто функціональна мова (або чисто функціональна бібліотека) здатна хоча б наблизитись за швидкістю до імперативної. Що досить суттєвий аргумент за використання функціональної мови “від природи” замість ФП імперативною мовою, що я відразу вирішив перевірити на JavaScript.

Та тут практична частина: зробив невеличкий тестовий скрипт; задача — підрахувати кількістю ключів у SteamDB, довших за 5 символів. Така досить класична функціональна задача на поважному обсязі в 163 Мб даних. Що маємо:

  1. Функціональне рішення лише у 2 рази повільніше за імперативне (яке не приводжу заради стислості):
json.reduce((a, r) => a + Object.keys(r).filter((k) => k.length > 5).length, 0);
  1. Таке саме рішення, але з використанням відомої бібліотеки Immutable, в 10 разів повільніше за попереднє! Висновок: на практиці ціна функціональних структур даних перевищує витрати на копіювання традиційних.

  2. Та саме цікаве: думаю, ну добре, але ось ClojureScript точно повинен робити все правильно та ефективно. Але ні: код на ClojureScript був ще в 4 рази повільніше за Immutable, тобто у 80 разів повільніший за імперативне рішення!

(reduce + 0 (map
  (fn [r]
    (count (filter #(> (count %) 5) (keys r))))
  json))

Висновок: я вірю, що якщо взяти низькорівневу мову та реалізувати функціональні структури близько до заліза, можливо, з власним керуванням памʼяттю, то це може бути швидко (Тобто, наприклад, варіант Haskell, а можливо й Clojure без -script.) Але в імперативній мові вони навряд чи зроблять функціональний код швидше.


23.08.2024

Дизайн та естетики

Ділюся ще досі сирими, але впевненими міркуваннями про те, як зрозуміти дизайн, якщо пощастило бути програмістом.

Є таке модне сучасне слово: естетика. (Тобто слово старе, а значення надсучасне.) Естетика — це, моїми словами, сукупність відчуттів від якогось предмета. Є зміст, а є естетика. На відміну від інших схожих слів, естетика фіксує точку в просторі, навколо якої скупчуються схожі предмети. Є ціла енциклопедія естетик.

Естетики є в інтерʼєрі, одязі, вебсайтах… є вони й в коді. Наприклад “естетика Ruby on Rails” багато у свій час наробила хвиль. Естетику складно описати словами, вона невербальна, легше побачити. Тим важче формалізувати: правила на кшталт “роби функції не довше 10 рядків” корисні, але не навчать тебе програмувати зі стилем; це приходить з досвідом.

Так само й в дизайні, будь-який дизайн не створюється в вакуумі з голови, а існує в культурному просторі. Щоб навчитися робити щось гарне, треба знайти естетику, яка тобі до вподоби, збирати приклади та відтворювати їх. В запозиченні немає нічого ганебного: поки починаєш, твоя задача привʼязатися до свого орієнтира.

Я про це пишу, бо в мене була така хиба, що ніби дизайн утворюється з чистого листа, як то кажуть, “з першооснов”… якщо не виходить — значить немає хисту. Проте спостерігати, сприймати та відтворювати естетики здатний кожний успішний програміст — не треба цього боятися.