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

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

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

08.04.2024

Бібліотека conc в Golang

Вже другий пост задоволення бібліотекою github.com/sourcegraph/conc. Такі в ній гарні обгортки для типових задач рівночасності.

Цього разу — конкретно про модуль conc/pool. Він реалізує абстракцію “запустити декілька задач рівночасно та почекати на результат”. Ніби для того достатньо стандартного sync.WaitGroup, але то буде примітивне рішення. Доведеться вручну лічити задачі, збирати результати, обробляти помилки…

З conc/pool достатньо запустити необхідні задачі викликом pool.Go(), а потім зачекати — викликом pool.Wait(). Є декілька модифікацій пула залежно від змісту задач: чи повертають вони результат, або помилку; чи потребують вони контекст (з особливою можливістю скасувати контекст після першої помилки.)

conc/pool підходить для ситуацій, коли ми запускаємо різнорідні задачі — приблизно як Promise.all в JavaScript. Та головне, що він робить використання рівночасності легше та простіше. Наприклад, якщо нам потрібно завантажити дві неповʼязані сторінки. Зазвичай така задача надто маленька, щоб розкладати її на горутіни, канали, і все інше. Але з conc/pool майже нічого зайвого писати не доведеться. Просто чудова маленька абстракція.


07.04.2024

Документація

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

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

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

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

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


06.04.2024

Office key у Windows

Колись базовий пакет Windows містив Сапера. У Windows 11 його немає, зате є більш дорослі ігри — в одну з них грав сьогодні, називається “Не увійти в Office 365”.

…Я граю у Windows через Parsec, та мене досить давно дратує, що час від часу в браузері відкривається сторінка “Увійти в Office 365”. Я знав, що вона привʼязана до деякої звичної мені з macOS комбінації клавіш, проте розібрався тільки сьогодні.

Все виявилося ще гірше, ніж я очікував. На клавіатурах для Windows зʼявилася нова клавіша - “Office Key”. Але… чомусь вона не має нового коду клавіші, а емулює натиск Win+Ctrl+Alt+Shift, тобто комбінації, відомої як Hyper key. Але: Hyper key був обраний саме через те, що таких комбінації не зустрічаються у звичайних програмах та можуть бути призначені за власною потребою користувача.

В мене натиск на Hyper (а насправді Caps Lock) перемикає мову на клавіатурі, тобто натискаю я його регулярно, та часто несвідомо. Що, через Parsec, призводить до появи дратівної сторінки.

А тут виходить, що Microsoft вирішили пригребти Hyper key на власні потреби. Та ще й так, що його неможливо перепризначити або вимкнути. (Для порівняння, macOS дає змінити поведінку кожної клавиші-модифікатора окремо.)

Ну як — неможливо… Коли є AutoHotKey, то можливо. Хоча й не очевидно, бо комбінацію обробляє процес Explorer, та просто перехопити її не вийде. Знайшов тред з розвʼязком — я б сам не придумав.


05.04.2024

Як гарантувати імпорт в базу без дублікатів

Є така задача — завантажувати пакети даних в базу. Пакет складається з декількох частин. Інколи програма буде ламатися посередині (а яка не буде?); нам потрібно гарантувати, що жодний пакет не буде пропущений та в базі не зʼявляться дублікати.


04.04.2024

Чому Go вміє форматувати дати "як Ruby?"

Натрапив випадково на те, що в стандартній бібліотеці Go є, проміж різних стандартних форматів, формат дати time.RubyDate. Тільки Ruby - немає всяких PythonDate, JavaDate, або HaskellDate. Хоч це мене, як рубіста, переповнює гордістю, доведеться все ж поставити питання — навіщо?

Щоб відповісти, знаходимо той файл на GitHub та закопуємося в його blame. (Це типова процедура розшуку причини, яку я роблю щоразу, коли бібліотека поводиться незрозуміло.) Корисніше за все тут функція “open blame prior to this change”. Знаходимо коміт, в якому рядок зʼявився, та з нього - PR, в якому той коміт зробили.

Виходить, в далекому 2010 році, за два місяці після випуску мови на публіку, команда Go виявила розбіжність форматування Ruby та стандартного “Unix date”. Як вони її виявили та чому та розбіжність була варта включення в стандартну бібліотеку? Бо в тому форматі повертав дати тогочасний API Twitter. На той час, певно, цього було достатньо, щоб в мові Go зʼявився формат дат “як у Ruby”.

Наразі Twitter API (наскільки він живий?) повертає дати в стандарті ISO 8601. Якого, до речі, в Go немає! Є тільки формат time.RFC3339, який, як я сьогодні дізнався, тільки перетинається з ISO 8601. Бо обидва стандарти пропонують набагато більше єдиного вірного формату. Наскільки більше? Пропоную роздивитись ось цю жахливу діаграму. Форматування часу — це складно!


03.04.2024

Дизайн та юзабіліті скрипту для експорту рецептів

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

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


02.04.2024

Федеративна авторизація з AWS в GCP

Довелося мати справу з проєктом, де є компоненти в AWS та GCP. Наївне рішення в такому випадку — просто поділитися ключами доступу. Проте ключі можна вкрасти, тому всередині хмари ми користуємося ролями, що призначені з оточення. Дізнався, що роль AWS можна використати для авторизації в GCP - називається це Workload Identity Federation.

Яким чином Google може перевірити нашу роль? Трохи криво, але дієво: викликом AWS API GetCallerIdentity. Причому щоб Google міг зробити той виклик, ми формуємо параметри, підписуємо їх власним ключем AWS, та передаємо всі параметри в сервіс STS Google (зазначу, що ключі залишаються в нас - Google отримує тільки підпис). Той власноруч робить цей виклик, отримує у відповідь автентичну сутність AWS та, якщо їй дозволено вхід, видає нам ключ Google.

Окрему складність ставить бізнес-модель авторизації: є workload identity pool, який є “дверми в Google”; пул отримує доступ до конкретного service account, який буде нашим представником по всіх інших справах в Google Cloud. Але за авторизацію відповідає workload identity provider; в одного пула може бути декілька провайдерів. Як на мене, то дуже заплутано.

На щастя, більшість тих нюансів (в тому числі весь процес авторизації!) приховані в SDK. Тобто пул видає нам стандартний для GCP credentials.json, в якому міститься повна інструкція для клієнта, який буде запущений на AWS. (Нічого секретного: тільки інформація про засіб авторизації, та координати цільового service account.) Оскільки в межах AWS клієнт може отримати (тимчасові) ключі самостійно з оточення, то нам нічого більше робити не треба.

Тобто, коли вже розібрався, то все “просто”. До речі, так само Google підтримує авторизацію з OpenID Connect та іншими технологіями. Гарно!


01.04.2024

Декодування ненормального JSON у Go

Кастомизація розбору та генерації JSON у Go - один з підходів “вище середнього”, який робить цю типізовану мову зручнішою або взагалі, відкриває шлях до задач, які здаються неможливими.

Вчора зіткнувся з проблемкою: в JSON-LD в одних рецептах світлини передаються переліком об’єктів (в об’єктах - посилання, розмір, і т.д.), а в інших - просто рядком з посиланням.

В мовах з динамічним типізуванням це навіть не варто уваги. А в Go ми повинні задати конкретний тип JSON, який хочемо отримати. Та написати в ньому “рядок або масив обʼєктів” неможливо, бо в Go немає алгебраїчних типів.

Можна, як я колись писав, встановити тип interface{}. Результат можна розібрати або рефлексією, або перевіркою типів; в будь-якому випадку код буде багатослівний. Особливо якщо дані мають складну структуру, а не просто “рядок або число”.

Однак є й кращий спосіб. Це створити спеціальний тип поля та реалізувати в ньому метод UnmarshalJSON. Метод отримує на вхід []byte, та повинний призначити з них свій зміст. Не потрібно вручну парсити JSON; ми делегуємо це звичайному парсеру, коли зʼясуємо, який конкретний тип перед нами. Наприклад, в моєму випадку, я просто дивлюся, чи є перший символ лапкою ", та викликаю Unmarshal в рядок, а якщо ні — то в структуру.


31.03.2024

Організація рецептів з відкритими форматами даних

🌶️ NYT Cooking залишається моїм найнадійнішим джерелом рецептів. Так, New York Times за часи війни втратили мою довіру, проте розділ з рецептами це не стосується. Особливо подобається те, що кількість оцінок рецептів вимірюється тисячами, та коли рецепт на пʼять зірок — то можна бути впевненим, що вийде гарним. Мати надійні рецепти в одному місці, без прочісування інтернету — незрівнянно.

Але… є проблема. Всі інші рецепти я зберігаю в застосунок Paprika. На macOS та iOS це практично стандарт. Набридло мати (та шукати) одні рецепти в NYT Cooking, а інші — в Paprika. Захотілось перенести.

Крок перший. Як імпортувати рецепти в Paprika? Якби йшлося про один рецепт, то Paprika непогано імпортує з HTML. Але мені потрібний масовий імпорт. Спосіб є; з доступних форматів найпростіше підготувати YML. Тільки з чого?

Крок другий. Як отримати список збережених рецептів з NYT Cooking? Швидко помітив, що вебчастина це SPA. Гарна особливість SPA - можна знайти API, який його живить.Так і тут: є API recipe_box_search. От тільки як авторизуватися, щоб викликати його? Тут є хитрий спосіб: написати скрипт та виконати його в консолі браузера. В цьому та іншому випадках це простий шлях отримати дані з-під авторизації.

Крок третій. Як забрати дані рецепту? Авторизація для того не потрібна. Знайшов https://github.com/ianbrault/nyt_recipe, який конвертує рецепти в простий HTML. Проте він розбирає сторінку з рецептом — цей метод поламається як тільки зміниться дизайн сторінки. Чи є надійніші джерела? Спроби завантажити сторінку з розширенням .json та іншими не допомогли. Тоді помітив, що в сторінці є блок <script type="application/ld+json>. Такий блок містить дані сторінки в форматі JSON-LD для машин — пошукових рушіїв та іншого. У випадку NYT Cooking, він містить всі дані рецепту! Залишилось тільки конвертувати їх у формат YML, який очікує Paprika.

Який тут висновок: при вебскрейпінгу варто звернути перевірити, чи є альтернатива у машинному форматі - RSS, JSON, і так далі. Тоді не доведеться робити ненадійний розбір сторінок.


30.03.2024

Відстежування витрат електроенергії з Home Assistant

💡 Трохи актуального. Задався задачею стежити за власним електроспоживанням. Це виявилося цілком реально.

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

Проте дійсно найпростіший спосіб — це пристрій Shelly EM. Він має індукційну кліпсу, яка просто вдягається на вхідний кабель від лічильника. Мені подобається, що через сам датчик струм не йде, тож це безпечний та неінвазивний спосіб. Зазначу, що для підключення самого пристрою все ж доведеться його заживити від чогось в щитку, тому я доручив роботу професійному електрику.

Пристрій Shelly EM має розміри пачки сірників та під’єднується по Wi-Fi. На радість, цей Wi-Fi в мене витримав зʼєднання через металевий щиток та броньовані вхідні двері, але то був найбільш несподіваний та ризикований момент всього проєкту.

У пристрою є вебінтерфейс та навіть якась власна хмара, але мені таке не цікаво, я хочу у свій HomeAssistant. Так можна, причому без жодних хмар. У Shelly хмара опціональна; спочатку налаштовується підключення до Wi-Fi та локальний вебінтерфейс, а потім вже за бажанням обліковий запис; в HA пристрій зʼявляється автоматично, як буде підключений до спільного вайфаю.

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