shikhalev.*

Последние записи

ТехнологииПрограммированиеRubyПриродаБёрдвотчинг

2026.03.27 • Иван Шихалев

iNatGet v0.9.2 и его практическое применение

Версия 0.9.0.18

Итак, финальные отчеты по сво­им районным проектам на iNa­tu­ra­list я сфор­ми­ро­вал, пользуясь уже новой версией inat-get. Сразу выяснилось, что вер­сия 0.9.01, несмотря на то, что я старался для ба­зо­вых вещей писать тесты, никуда не го­дит­ся. Впрочем, это нормально на дан­ном этапе (ранней беты). В ре­зуль­та­те, отлаживая на ре­аль­ных данных, я дошел до вер­сии 0.9.0.18 — уже вполне рабочей.

Полученными отчетами я вполне доволен. Примеры:

В подвале скриптов можно разглядеть мелким шрифтом, что сгенерированы они версией 0.9.0.15, а не .18. Это потому, что самые последние исправления в вет­ке 0.9.0 касались исключительно оптимизаций, и на результат не влияли.

Средств для удоб­но­го формирования отчетов все еще нет, пишу текстом в фай­лы, так что скрипты отчетов получились довольно развесистые. Я их поместил в от­дель­ный репозиторий ing-sv-districts — можно полюбоваться, хотя стру­к­ту­ра там сильно так себе…

Версия 0.9.2

А здесь добавлена довольно мелкая фи­ча — поддержка ERB, как в ка­чес­т­ве шаблонов, вызываемых из поль­зо­ва­тель­с­ких скриптов, так и в ка­чес­т­ве пользовательских скриптов как таковых. Не то, чтобы это было существенное улучшение, но может, кому и пригодится.

Переводить районные скрипты на этот механизм я не стал, поскольку это ничего нового не да­ет. Впрочем, возможно, имело бы смысл для до­пол­ни­тель­ной отладки…

Промежуточные итоги

В общем и целом, текущая архитектура вполне годная, от глав­ных тормозов вер­сии 0.8.x удалось избавиться, основной упор по вре­ме­ни идет в ско­рость интернета, причем обновления, как и задумано, берутся ин­к­ре­мен­т­но — через параметр updated_since, что резко ускоряет повторные запросы.

Но работа с ло­каль­ной БД оставляет желать лучшего, груп­пи­ро­воч­ные запросы промахиваются мимо индексов, причем даже группировки по поль­зо­ва­те­лям, которые самые простые по сво­е­му внутреннему устройству… Что ж, значит следующая ите­ра­ция — 0.9.4, запланированная для ре­ше­ния именно этих про­б­лем — запланирована не зря. Заметные тормоза, впрочем, видны только на ге­не­ра­ции итоговой сводки — она сейчас занимает у ме­ня пару часов, что печально, конечно, но по срав­не­нию с 0.8.x — просто прекрасный результат.

Что по­ра­до­ва­ло — уже отлаженный на SQLite вариант на Post­greSQL заработал сра­зу — спасибо Sequel. Скорость работы на раз­ных СУБД практически не от­ли­ча­ет­ся. Нужны, конечно, аккуратные замеры, но в це­лом разницу можно игнорировать.

Что дальше?

Общий роадмап имеется на ви­ки проекта — Roadmap. Кстати, я тут «на­вайб­ко­дил» генератор ро­ад­ма­пов из Is­su­es и Mi­le­sto­nes — action-is-roadmap — довольно прикольно получилось, люблю наглядность. Даты майл­сто­у­нов проставлены от фо­на­ря — чисто для упо­ря­до­че­ния, как обещания их воспринимать точно не стоит.

А если не вдаваться в детали, то ключевые задачи такие:

  • Оптимизация запросов.

    Не знаю, насколько удастся их ускорить, но желательно выжать все возможное.

  • Доделать кэширование.

  • Удобный конструктор отчетов.

    Чтобы не нужно было заморачиваться на оформление в пользовательских скриптах. Писать их, думая только о ло­ги­ке, причем в тер­ми­нах множеств и выборок.

  • Доделать работу с про­чи­ми данными.

    Пока реализованы далеко не все возможные запросы и фильтры, есть куда развиваться. И здесь нужно будет не за­бы­вать об оп­ти­маль­нос­ти запросов.

Наверное, по ходу дела будут появляться новые задачи, и уж точ­но — находиться новые баги…

ERBPostgreSQLSQLiteSequeliNatGetiNaturalistinat-get

ТехнологииПрограммированиеRubyПриродаБёрдвотчинг

2026.03.04 • Иван Шихалев

iNatGet v0.9.0

Что ж… Вот я и за­ре­ли­зил 🌿 iNatGet v0.9. Через два с небольшим года после v0.8, и через примерно год после последней вер­сии — v0.8.0.16. Обо всех этих минорных корректирующих версиях я ничего не писал, главным образом потому, что там мучительно правились мелкие баги, а нормальная работа требовала глобального рефакторинга (т.е. переписать заново с нуля), на который никак не находилось времени.

Однако все это время я не прекращал пользоваться своей программой, регулярно формируя отчеты для своих проектов1 на iNa­tu­ra­list. Таким образом я постепенно уточнял свое понимание того, как это в принципе должно работать и где у меня провалы в архитектуре. Плюс развитие ней­ро­се­тей… В проекте ни строчки сгенерированного кода, но для консультаций и ревью я пользовался различными LLM очень активно2, что и позволило сравнительно быстро написать новую, полностью переделанную, версию.

Если версии v0.8.x я характеризовал как альфа-версии, то ветка v0.9.x пусть будет бетой, но пока — очень ранней бетой

Коротко о том, что это за проект вообще, можно прочитать в README-ru.md в репозитории.

  1. См. журналы проекта «Биоразнообразие районов Свердловской области» и его дочерних. 

  2. О своем отношении и подходе к использованию нейросетей я подробно писал в посте: «Заметки об LLM и нейросетях вообще»


Читать далее »

iNatGetiNaturalistinat-get

ТехнологииПрограммированиеRubyБД

2026.02.05 • Иван Шихалев

Автодокументирование моделей Sequel

КДПВ сгенерирована по запросу «(yard) is (sequel) in winter fantasy style, detailed eyes»

Набросал предварительную версию YARD-плагина для автодокументирования Sequel-моделей — yard-is-sequel. Су­щес­т­ву­ю­щий yard-sequel с современными версиями YARD/Ruby/Sequel не работает.

Мой вариант, конечно, не может пока похвастаться полнофункциональностью (версия 0.8.0 — это ранняя альфа), но кое-что самое важное умеет:

  • Генерирует список ассоциаций: many_to_many, many_to_one и one_to_many с корректными ссылками на типы.

  • Генерирует список полей. Также с типами, но тут есть нюансы…

    1. Маппинг типов полей требует доработки. Кроме того, ти­пы, не поддерживаемые SQLite, скорее всего, не будут нормально обрабатываться в принципе.

      Поля берутся из Database#schema на созданной in-memory SQLite базе данных. Было бы хорошо, безусловно, брать их непосредственно из миграций, но пока непонятно, как это сделать.

    2. Путь к миграциям следует указать через переменную окружения.

Чего нужно доделать?

  • Отрефакторить и упростить обработку ассоциаций.

  • Расширить обработку типов в полях и сделать ее менее хрупкой. Сейчас есть подозрение, что минорная смена версии Sequel может всё поломать…

  • Добавить возможность брать схему из отдельно сохраненного файла (в формате JSON, скорее всего).


Но надо понимать, что я буду что-то править и дорабатывать только постольку, поскольку мне это самому нужно… Однако, если кто-то предложит свои пулл-реквесты, или хотя бы подробные баг-репорты, отнесусь со вниманием.

SequelYARDyardocдокументирование

ТехнологииПрограммированиеАбстракцииRubyДыбр

2026.01.19 • Иван Шихалев

Об инвалидацию кэша

КДПВ сгенерирована нейросетью по запросу «cache invalidation»

Как известно, в программировании есть только две реально сложные задачи: именование переменных и инвалидация кэша1. С первой мы сделать ничего не можем, со второй, конечно, тоже, но что-то делать приходится…

И вот в процессе большого рефакторинга inat-get я в очередной раз задумался. Суть тут вот в чем: данные, которые требуется получать из API iNa­tu­ra­list — очень большие (зависит от це­лей, конечно, но они могут быть очень большими), и логично их за­кэ­ши­ро­вать в локальной базе данных. Естественно, кэшированные данные нужно обновлять.

Есть прекрасный параметр updated_since в запросах к API, т.е. мы храним у себя информацию о сделанных запросах, и когда нам нужно получить новые данные по тем же условиям, указываем дату/время предыдущего запроса в этом параметре. Но полностью проблему это не решает: updated_since не отменяет (и не должен отменять) все остальные параметры запроса, т.е. те наблюдения, которые выпали из запроса, в выборку не попадут. И останутся в локальной БД в старом состоянии.

Ну, то есть, выбрали мы, например, данные по проекту, который фильтрует наблюдения с quality_grade=research, а потом пришел добрый человек и заметил, что в наблюдении культурное растение. Наблюдение выпадает из проекта, но мы об этом не можем узнать через обновление…

Какие есть пути решения? Вижу несколько вариантов, ни один из которых меня не устраивает полностью…

  1. Автор этой фразы, предположительно, Фил Карлтон (Phil Karlton), ведущий инженер компании Netscape. 


Читать далее »

iNaturalistinat-get

ТехнологииПрограммированиеRubyАбстракции

2025.12.23 • Иван Шихалев

Паттерн «Фасад» и гем для DSL

Картинка для привлечения внимания

При написании inat-channel я столкнулся вот с какой проблемой: с одной стороны, более-менее сложные действия должны быть декомпозированы, то есть разбиты на модули и отдельные методы в них; с другой — глубокая декомпозиция заставляет писать длинные обращения к методам типа INat­Chan­nel::​Te­le­gram::​send_​ob­ser­va­ti­on, что неудобно, да и не эстетично. По хо­ро­ше­му вообще нужно верхний уровень методов ин­клю­дить и писать send_observation в основной программе, но если писать все как включаемые методы модулей, то во-первых, они все из всех модулей попадут в финале в одно пространство имен, а во-вторых, туда же попадут и приватные методы.

Для подобных случаев и предназначен паттерн «Фасад» — мы создаем отдельный программный модуль — в данном случае это модуль же в терминах Ruby — который содержит только нужные извне методы, делегируя их в основной нормально де­ком­по­зи­ро­ван­ный код. И затем его спокойно ин­клю­дим в ко­де основного скрипта.

Собственно, именно так я и сделал, определив модуль IC и заполняя его методами в тех же файлах, где они определены. Туда же отправились некоторые методы, не нужные вовне, а используемые слабо логически связанными модулями — здесь речь скорее не о логике и отделении фасада, а о сокращении (текстовом) кросс­мо­дуль­ных вызовов. Впрочем, по мере разрастания структуры вопрос, что считать внутренним, а что внешним, становится не очень однозначным.

Подумав немного на эту тему, я решил вынести абстракцию в код и написал is-dsl — гем, упрощающий, а главное — структурирующий делегирование методов и констант фасаду. Подробнее — в README репозитория (есть русская версия), а также в yard-документации. Здесь коротко обозначу основные особенности:

  • Помимо основного модуля фасада формируется теневой модуль — для использования внутри библиотеки. Все, что попадает в основной, попадает и в теневой, обратное неверно. См. shadow-методы.

  • Можно делегировать как статически сингл­тон-методы классов и модулей, так и лениво методы произвольных сингл­тон-объектов, где сам объект создается или получается через вызов блока. См. lazy-ме­то­ды. Предполагается применение с ме­то­дом класса instance в пер­вую очередь.


Планов менять или добавлять что-то в основную функциональность нет, думаю когда-нибудь сделать плагин для YARD, чтобы делегирование методов правильно автоматически документировалось.

DSLgemis-dslпаттерны проектирования

ТехнологииПрограммированиеRubyWebПриродаОбщество

2025.12.12 • Иван Шихалев

iNaturalist + Telegram

Анонс

Написал скрипт для автопостинга выборок из iNaturalist в tg-каналы. Скрипт делает выборку по произвольно сконфигурированным параметрам (которые, разумеется, должны поддерживаться iNaturalist API), затем берет случайное наблюдение, постит его, а остальные складывает в пул, который будет задействован, если свежие кончатся. Это если коротко.

Более подробно, как это все работает, а главное — как настраивается, я описал в README проекта inat-get/inat-channel. В том числе и на русском. Здесь пара моментов:

  • Наблюдения не дублируются.

  • Можно включить режим, когда и таксоны не будут дублироваться, с ограниченным, правда, сроком. Но его можно выставить произвольно большим.

  • Форматирование делается ERB-шаблоном, т.е. максимально гибко.

  • Скрипт прекрасно работает на GitHub Actions, запускаясь по расписанию. Для контроля неповторямости необходимо настроить обратный пуш, чтобы данные, которые хранятся в JSON-файлах, сохранялись в репозитории между сеансами.

  • Используется iNaturalist API v2, которое находится в ранней бете и может поломаться. Однако, на первой версии пришлось бы вытягивать в десятки, если не в сотни раз бо́льшие объемы данных, что малоприемлемо.

Примеры

На реальных примерах работу скрипта можно посмотреть на двух моих каналах:

Подписывайтесь, ставьте лайки, комментируйте… Отелеграмливайте свои проекты. В общем, велкам.


Читать далее »

TelegramiNaturalistinat-channelавтоматизация

ТехнологииПрограммированиеRubyБёрдвотчингПрирода

2023.11.13 • Иван Шихалев

INat::Get — ранняя альфа

Скриншот репозитория

— Я зделяль. ©

Итак, прошу любить и жаловать — INat::Get — софтина для по­лу­че­ния и обработки данных с iNaturalist. Основное изначальное пред­наз­на­че­ние — подбивать всякую статистику для про­ек­тов на том же iNaturalist’е, но варианты использования гораздо шире.

Первым делом хочу отметить, что текущее со­сто­я­ние — это ранняя альфа. Я не ре­ко­мен­дую никому этим пользоваться иначе как из любопытства и желания поучаствовать. Тем не ме­нее делаю пост уже сейчас в на­деж­де, что любопытные желающие найдутся. Со сво­ей стороны готов подробно отвечать на во­про­сы и учитывать пожелания.

Зачем?

iNaturalist предоставляет открытый доступ к ог­ром­но­му массиву наблюдений, а также по су­ти к по­сто­ян­но ак­ту­али­зи­ру­ему таксономическому справочнику (тут можно обсуждать нюансы, но для лю­би­тель­с­ких целей это очень хорошие данные). Интерфейс самого сайта не по­кры­ва­ет и, конечно, не мо­жет покрывать все возможные варианты запросов и выборок, но мы можем получить сами данные через механизм выгрузок или посредством открытого API, и второй вариант богаче, гибче и вообще интересней.


Читать далее »

iNatGetiNaturalistinat-get

ТехнологииПрограммированиеRubyПубликации«Системный администратор»Web

2020.01.11 • Иван Шихалев

Rack — основа веб-фреймворков в Ruby

Оригинал этой статьи опубликован в журнале «Системный администратор» №5 (150) за май 2015. Прошу обратить внимание на год — какие-то моменты могут расходиться с современными версиями языка и библиотек…


Библиотека Rack — простой объектный интерфейс для написания веб-приложений.

Слово «rack» в английском языке имеет множество значений, включая такие, как «пытка» и «разрушение»… Однако, надо полагать, название рассматриваемой библиотеки произошло от другой группы смыслов: «стойка», «штатив», «каркас» и т.д. Rack обеспечивает простой и в то же время удобный интерфейс, обеспечивающий взаимодействие между веб-сервером и приложением, позволяя программисту сосредоточиться исключительно на логике последнего.

Этот интерфейс достаточно низкоуровневый и не ограничивает разработчика каким-либо заранее заданным способом организации приложения и высокоуровневыми абстракциями. Соответственно, он и не предоставляет таких абстракций — это уже дело фреймворков, которые работают поверх него: Rails, Sinatra и других.


Читать далее »

Rack

ТехнологииПрограммированиеRubyПубликации«Системный администратор»

2016.03.09 • Иван Шихалев

Средства самопознания в Ruby

Оригинал этой статьи опубликован в журнале «Системный администратор» №1-2 (146-147) за февраль 2015.


Что программа может знать о самой себе?

Практически все современные языки программирования содержат средства, позволяющие во время выполнения программы получить какие-то данные о структуре самой этой программы. В компилируемых языках такие возможности, как правило, ограничены и отключаемы, в целях оптимизации, в интерпретируемых же более обширны, поскольку эти данные все равно необходимы самому интерпретатору, соответственно, содержатся в памяти, и вопрос только в том, предоставлять ли к ним доступ языковыми средствами.

В данной статье я планирую рассмотреть те средства «самопознания», которые доступны для программ на Ruby.


Читать далее »

интроспекцияметапрограммирование

ТехнологииПрограммированиеRubyПубликации«Системный администратор»

2016.01.27 • Иван Шихалев

Мета­программи­ро­вание в Ruby: разбор примера

Оригинал этой статьи опубликован в журнале «Системный администратор» №12 (145) за декабрь 2014.


Добавление собственных абстракций в объектную модель — это просто. И интересно.

Авторы книги «Programming Ruby: The Pragmatic Programmers’ Guide» называют метапрограммированием расширение и изменение абстракций языка (тогда как собственно программирование пользуется теми, что есть). Конечно, можно поспорить о том, что считать такой абстракцией, а что нет, однако нельзя не заметить, что в современных динамических языках, таких как Ruby или, например, Python, легко делаются некоторые вещи, которые в классических языках находились именно на языковом уровне и жестко определялись компилятором. Тут можно вспомнить, для примера, декораторы, о которых я писал в сентябре прошлого года1. И сейчас мы рассмотрим нечто подобное. В процессе я буду делать обобщающие отступления, переходя от частного примера к общим принципам программирования в Ruby.

  1. Статья «Декораторы в Ruby». «Системный администратор» № 9 (130), сентябрь 2013. Стр. 68–71. 


Читать далее »

метапрограммирование