Стендап Сьогодні
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
Пости з тегом #Кодогенерація
09.10.2022
Бібліотека react-native-health та кодогенерація
⚛️🍎❤️ Продовжив розбиратись з react-native-health. Дуже швидко натрапив на проблему: в обгортці не вистачає функції preferredUnitsForQuantityTypes ; як, до речі, і в обгортках, що з нею конкурують. Тобто не можна отримати налаштування одиниць вимірювання, і доведеться питати користувача окремо.
Само собою що проблема це незначна, і можна було відкласти її рішення принаймні до появи у додатка другого користувача. Бо для мене одного можна просто вказати одиниці константами. Але яка в тому втіха?
Додати в обгортку для React Native один метод на Objective C справа цілком реальна і багато часу на це не пішло, хоча на Objective C я пишу “зі словником” (а насправді з гуглом, авжеж). Навіть якщо обгортку треба писати з нуля, то можна впоратись за пару годин. Objective C - одна з найменш зрозумілих для мене мов, синтаксис ні на що не схожий.
Але тут стала інша проблема. Категорії значень — вхідне значення методу — треба передати у вигляді типу HKObjectType
. В JavaScript, відповідно, треба передати рядок, який потім перекласти у цей самий тип. Простого методу для цього немає, бо в Objective C так не роблять — автори бібліотеки цілком раціонально очікують, що ми будемо вживати готові константи, а не довільні рядки.
Тобто треба написати словник. Можна побудувати його вручну. Але ж є проблема — на цей час в HealthKit існує 182 різних типи значень, а в майбутньому зʼявляться ще. (Зауважу, що для розв’язання моєї задачі вистачило б одного типу. Але яка в тому втіха?)
На допомогу знову приходить кодогенерація. Я знайшов файл, де перелічені всі ці типи - HKTypeIdentifiers.h (Він вже схожий на згенерований, до речі.) Далі рядки файлу можна розібрати регулярним виразом, побудувати перелік типів, і згенерувати для них як словник для Objective C, так і декларації для JavaScript/TypeScript.
Кодогенерація це, можливо, не дуже “чистий” підхід програмування, але для деяких задач підходить ідеально.
29.11.2023
Кодогенерація для доступу в базу на Go з sqlc
Ті проєкти, де я працюю з Go, мають певний мікросервісний характер. В тому сенсі, що від бази їм потрібний вузький набір даних — ніяких там адмінок, редакторів і так далі немає.
Отже, для доступу в базу використовуються стислі, вручну написані запити SQL, а також функції Go, які їх виконують та повертають результати. Це відносно непогано працює, та тягнути в проєкт цілий ORM точно поки не хотілося.
Але як тільки в такому проєкті зʼявляється трохи складніший сценарій взаємодії з базою — ну такий, що в Ruby on Rails став би запитом з декількома розгалуженими includes()
- то рентабельність ручного написання стрімко зникає.
…Втім, ORM заради пʼятьох запитів все одно буде зайвим. Знайшов натомість чудове, ідіоматичне рішення для мілкої роботи з базою — генератор коду sqlc. Тут з запитів SQL генерується Go - причому й структури записів, й код взаємодії з базою. Результат генерації близький до того, що я пишу руками. Це просто ідеальний розвиток підходу.
Мені подобається, що sqlc залишає за мною можливість написати складний SQL, та не витрачати час на моделювання бази засобами Go. Бо на то є Ruby on Rails - там міграції повноцінні та взагалі з базою зручно працювати.
09.08.2024
Дженерики та кодогенерація в Golang
Я люблю генерацію коду як аналог метапрограмування на Go. Навіть більше, ніж метапрограмування як у Ruby, оскільки згенерований код можна переглянути, а не тільки уявити.
Дженерики частково прибрали потребу в кодогенерації: а саме, більше не потрібно генерувати код “з підставлянням типів”. Це чудово та, звісно, в нашому коді є місце дженерикам. (Хоча я поки не брався за впровадження суто функціональних підходів, як, наприклад робить модуль github.com/samber/lo.)
Але все ж навіть з дженериками залишається багато шаблонного коду, який неможливо узагальнити. Уявимо, що у нас є структура Database
, в якій 10 різних полів Table[Key, Record]
. Всі ці поля доведеться оголосити, ініціалізувати, та виконати різне обслуговування. Щоб не копіювати десять разів однакові рядки для кожної таблиці, нам і допоможе кодогенерація.
Раніше я передавав параметри генератора в рядку //go:generate
, але нещодавно знайшов інший підхід. Можна почати з конфігурації у JSON чи TOML, а у генераторі зчитувати ту конфігурацію та генерувати весь код. Наприклад, в конфігурації може бути список таблиць, за яким ми генеруємо і структуру Database
, і функцію Database.SaveTables()
, і все інше.
Можливості практично необмежені! Але все ж я б радив зводити генерований код до мінімуму, бо код в шаблоні точно важче сприймати та редагувати. Наприклад, замість генерації всієї Database
можна згенерувати структуру tables
та вбудувати її: type Database struct { tables }
. Тоді цей тип можна розширювати без вдавання до шаблону.