Ruby
Язык программирования Ruby
Последние записи
Автодокументирование моделей Sequel

Набросал предварительную версию YARD-плагина для автодокументирования Sequel-моделей — yard-is-sequel.
Существующий yard-sequel с современными версиями YARD/Ruby/Sequel не работает.
Мой вариант, конечно, не может пока похвастаться полнофункциональностью (версия 0.8.0 — это ранняя альфа), но кое-что самое важное умеет:
-
Генерирует список ассоциаций:
many_to_many,many_to_oneиone_to_manyс корректными ссылками на типы. -
Генерирует список полей. Также с типами, но тут есть нюансы…
-
Маппинг типов полей требует доработки. Кроме того, типы, не поддерживаемые SQLite, скорее всего, не будут нормально обрабатываться в принципе.
Поля берутся из
Database#schemaна созданной in-memory SQLite базе данных. Было бы хорошо, безусловно, брать их непосредственно из миграций, но пока непонятно, как это сделать. -
Путь к миграциям следует указать через переменную окружения.
-
Чего нужно доделать?
-
Отрефакторить и упростить обработку ассоциаций.
-
Расширить обработку типов в полях и сделать ее менее хрупкой. Сейчас есть подозрение, что минорная смена версии Sequel может всё поломать…
-
Добавить возможность брать схему из отдельно сохраненного файла (в формате JSON, скорее всего).
Но надо понимать, что я буду что-то править и дорабатывать только постольку, поскольку мне это самому нужно… Однако, если кто-то предложит свои пулл-реквесты, или хотя бы подробные баг-репорты, отнесусь со вниманием.
Об инвалидацию кэша

Как известно, в программировании есть только две реально сложные задачи: именование переменных и инвалидация кэша1. С первой мы сделать ничего не можем, со второй, конечно, тоже, но что-то делать приходится…
И вот в процессе большого рефакторинга inat-get я в очередной раз задумался. Суть тут вот в чем: данные, которые требуется получать из API iNaturalist — очень большие (зависит от целей, конечно, но они могут быть очень большими), и логично их закэшировать в локальной базе данных. Естественно, кэшированные данные нужно обновлять.
Есть прекрасный параметр updated_since в запросах к API, т.е. мы храним у себя информацию о сделанных запросах, и когда
нам нужно получить новые данные по тем же условиям, указываем дату/время предыдущего запроса в этом параметре. Но полностью
проблему это не решает: updated_since не отменяет (и не должен отменять) все остальные параметры запроса, т.е. те
наблюдения, которые выпали из запроса, в выборку не попадут. И останутся в локальной БД в старом состоянии.
Ну, то есть, выбрали мы, например, данные по проекту, который фильтрует наблюдения с quality_grade=research, а потом
пришел добрый человек и заметил, что в наблюдении культурное растение. Наблюдение выпадает из проекта, но мы об этом
не можем узнать через обновление…
Какие есть пути решения? Вижу несколько вариантов, ни один из которых меня не устраивает полностью…
-
Автор этой фразы, предположительно, Фил Карлтон (Phil Karlton), ведущий инженер компании Netscape. ↩
Паттерн «Фасад» и гем для DSL

При написании inat-channel я столкнулся вот с какой проблемой: с одной стороны, более-менее сложные
действия должны быть декомпозированы, то есть разбиты на модули и отдельные методы в них; с другой — глубокая декомпозиция заставляет
писать длинные обращения к методам типа INatChannel::Telegram::send_observation, что неудобно, да и не эстетично. По хорошему вообще
нужно верхний уровень методов инклюдить и писать send_observation в основной программе, но если писать все как включаемые методы модулей,
то во-первых, они все из всех модулей попадут в финале в одно пространство имен, а во-вторых, туда же попадут и приватные методы.
Для подобных случаев и предназначен паттерн «Фасад» — мы создаем отдельный программный модуль — в данном случае это модуль же в терминах Ruby — который содержит только нужные извне методы, делегируя их в основной нормально декомпозированный код. И затем его спокойно инклюдим в коде основного скрипта.
Собственно, именно так я и сделал, определив модуль IC и заполняя его методами в тех же файлах, где они определены. Туда же отправились
некоторые методы, не нужные вовне, а используемые слабо логически связанными модулями — здесь речь скорее не о логике и отделении фасада,
а о сокращении (текстовом) кроссмодульных вызовов. Впрочем, по мере разрастания структуры вопрос, что считать внутренним, а что внешним,
становится не очень однозначным.
Подумав немного на эту тему, я решил вынести абстракцию в код и написал is-dsl — гем, упрощающий, а главное — структурирующий делегирование методов и констант фасаду. Подробнее — в README репозитория (есть русская версия), а также в yard-документации. Здесь коротко обозначу основные особенности:
-
Помимо основного модуля фасада формируется теневой модуль — для использования внутри библиотеки. Все, что попадает в основной, попадает и в теневой, обратное неверно. См.
shadow-методы. -
Можно делегировать как статически синглтон-методы классов и модулей, так и лениво методы произвольных синглтон-объектов, где сам объект создается или получается через вызов блока. См.
lazy-методы. Предполагается применение с методом классаinstanceв первую очередь.
Планов менять или добавлять что-то в основную функциональность нет, думаю когда-нибудь сделать плагин для YARD, чтобы делегирование методов правильно автоматически документировалось.
iNaturalist + Telegram

Анонс
Написал скрипт для автопостинга выборок из iNaturalist в tg-каналы. Скрипт делает выборку по произвольно сконфигурированным параметрам (которые, разумеется, должны поддерживаться iNaturalist API), затем берет случайное наблюдение, постит его, а остальные складывает в пул, который будет задействован, если свежие кончатся. Это если коротко.
Более подробно, как это все работает, а главное — как настраивается, я описал в README проекта inat-get/inat-channel. В том числе и на русском. Здесь пара моментов:
-
Наблюдения не дублируются.
-
Можно включить режим, когда и таксоны не будут дублироваться, с ограниченным, правда, сроком. Но его можно выставить произвольно большим.
-
Форматирование делается ERB-шаблоном, т.е. максимально гибко.
-
Скрипт прекрасно работает на GitHub Actions, запускаясь по расписанию. Для контроля неповторямости необходимо настроить обратный пуш, чтобы данные, которые хранятся в JSON-файлах, сохранялись в репозитории между сеансами.
-
Используется iNaturalist API v2, которое находится в ранней бете и может поломаться. Однако, на первой версии пришлось бы вытягивать в десятки, если не в сотни раз бо́льшие объемы данных, что малоприемлемо.
Примеры
На реальных примерах работу скрипта можно посмотреть на двух моих каналах:
-
Во-первых, я оживил канал «Природа Урала — наблюдения с iNaturalist». Там, напомню, наблюдения со всего Урала и прилегающих районов — от Оренбуржья до ЯНАО. Ни один регион не входит в выборку целиком, только отдельные районы, относящиеся непосредственно к Уральским горам, или прилегающие. В канале выходит до четырех постов в день по разным группам таксонов.
Все настройки и workflow для Actions доступны в репозитории inat-get/channel-ural.
-
Во-вторых, для проекта «Биоразнообразие районов Свердловской области» тоже завел канал, под немудреным названием «Биоразнообразие Свердловской области в TG». Там один пост в день (да и выборка поменьше).
Все его настройки и workflow также доступны на GitHub — inat-get/channel-sverdlobl.
Подписывайтесь, ставьте лайки, комментируйте… Отелеграмливайте свои проекты. В общем, велкам.
INat::Get — ранняя альфа

— Я зделяль. ©
Итак, прошу любить и жаловать — INat::Get — софтина для получения и обработки данных с iNaturalist. Основное изначальное предназначение — подбивать всякую статистику для проектов на том же iNaturalist’е, но варианты использования гораздо шире.
Первым делом хочу отметить, что текущее состояние — это ранняя альфа. Я не рекомендую никому этим пользоваться иначе как из любопытства и желания поучаствовать. Тем не менее делаю пост уже сейчас в надежде, что любопытные желающие найдутся. Со своей стороны готов подробно отвечать на вопросы и учитывать пожелания.
Зачем?
iNaturalist предоставляет открытый доступ к огромному массиву наблюдений, а также по сути к постоянно актуализируему таксономическому справочнику (тут можно обсуждать нюансы, но для любительских целей это очень хорошие данные). Интерфейс самого сайта не покрывает и, конечно, не может покрывать все возможные варианты запросов и выборок, но мы можем получить сами данные через механизм выгрузок или посредством открытого API, и второй вариант богаче, гибче и вообще интересней.
Rack — основа веб-фреймворков в Ruby
Оригинал этой статьи опубликован в журнале «Системный администратор» №5 (150) за май 2015. Прошу обратить внимание на год — какие-то моменты могут расходиться с современными версиями языка и библиотек…

Библиотека Rack — простой объектный интерфейс для написания веб-приложений.
Слово «rack» в английском языке имеет множество значений, включая такие, как «пытка» и «разрушение»… Однако, надо полагать, название рассматриваемой библиотеки произошло от другой группы смыслов: «стойка», «штатив», «каркас» и т.д. Rack обеспечивает простой и в то же время удобный интерфейс, обеспечивающий взаимодействие между веб-сервером и приложением, позволяя программисту сосредоточиться исключительно на логике последнего.
Этот интерфейс достаточно низкоуровневый и не ограничивает разработчика каким-либо заранее заданным способом организации приложения и высокоуровневыми абстракциями. Соответственно, он и не предоставляет таких абстракций — это уже дело фреймворков, которые работают поверх него: Rails, Sinatra и других.
Средства самопознания в Ruby
Оригинал этой статьи опубликован в журнале «Системный администратор» №1-2 (146-147) за февраль 2015.

Что программа может знать о самой себе?
Практически все современные языки программирования содержат средства, позволяющие во время выполнения программы получить какие-то данные о структуре самой этой программы. В компилируемых языках такие возможности, как правило, ограничены и отключаемы, в целях оптимизации, в интерпретируемых же более обширны, поскольку эти данные все равно необходимы самому интерпретатору, соответственно, содержатся в памяти, и вопрос только в том, предоставлять ли к ним доступ языковыми средствами.
В данной статье я планирую рассмотреть те средства «самопознания», которые доступны для программ на Ruby.
Метапрограммирование в Ruby: разбор примера
Оригинал этой статьи опубликован в журнале «Системный администратор» №12 (145) за декабрь 2014.

Добавление собственных абстракций в объектную модель — это просто. И интересно.
Авторы книги «Programming Ruby: The Pragmatic Programmers’ Guide» называют метапрограммированием расширение и изменение абстракций языка (тогда как собственно программирование пользуется теми, что есть). Конечно, можно поспорить о том, что считать такой абстракцией, а что нет, однако нельзя не заметить, что в современных динамических языках, таких как Ruby или, например, Python, легко делаются некоторые вещи, которые в классических языках находились именно на языковом уровне и жестко определялись компилятором. Тут можно вспомнить, для примера, декораторы, о которых я писал в сентябре прошлого года1. И сейчас мы рассмотрим нечто подобное. В процессе я буду делать обобщающие отступления, переходя от частного примера к общим принципам программирования в Ruby.
-
Статья «Декораторы в Ruby». «Системный администратор» № 9 (130), сентябрь 2013. Стр. 68–71. ↩
Ruby и многозадачность
Оригинал этой статьи опубликован в журнале «Системный администратор» №3 (136) за март 2014.

В статье рассматриваются основные средства работы с потоками (threads) и процессами в языке и стандартной библиотеке Ruby
Немного о терминологии: англоязычный термин «thread» на русский переводится в двух вариантах — как «поток» и как «нить». Второй вариант точнее и не вызывает неоднозначности с потоками данных (streams), однако первый уже прижился в качестве основного. Кроме того, есть еще производные термины и варианты вроде «многонитевость» (или «многонитность»), но они мне не встречались и, честно говоря, режут глаз. Поэтому я буду использовать «поток».
Прежде, чем перейти к описаниям имеющегося инструментария, хотелось бы заметить, что Ruby создавался не как специальный язык параллельного программирования, при этом во время его создания и становления многозадачность уже стала привычной и необходимой. Из этих двух посылок, в общем-то, можно вывести текущую картину: никаких специфических концепций мы в нем не увидим, только поддержку привычной для всех языков общего назначения модели с некоторыми нюансами реализации.
Блоки и контекст в Ruby

Что стоит за конкретным идентификатором в данном окружении
Давайте разберемся с программным контекстом в Ruby: какие переменные и другие объекты доступны в конкретном месте
программы, и как интерпретатор их ищет? Что обозначает конкретный идентификатор, откуда он берется? Почему отсюда,
а не оттуда? И чему, наконец, в этом трижды перекинутом блоке будет равен self?
Ruby очень гибок и позволяет переопределить так много, что, образно выражаясь, вы можете выстрелить себе в ногу из самой этой ноги. Картечью.
Показаны 10 записей из 14