Вообще-то я сначала собирался написать только про is-module-reset, но понял, что без упоминания ключевой зависимости — is-deep — а главное, ограничений, с нею связанных, обойтись никак нельзя.
И ключевое ограничение — это категорическая несовместимость с activesupport, поскольку is-deep
monkey-патчит те же методы (с теми же целями но полностью другим способом)… Возможно, я когда-нибудь придумаю другие имена,
но пока не знаю, чем корректно и идиоматично заменить deep_merge.
Начнем с того, что попроще.
is-module-reset
Собственно, задача, которая передо мной встала: это тестирование плагинов для Jekyll, в частности —
jekyll-is-hookdown,
о котором я писал ранее. Проблема возникает, в первую очередь, с тестированием хуков, поскольку Jekyll не предоставляет возможности
их дерегистрации, и пересоздание объекта сайта тут не помогает, поскольку хуки хранятся в переменных модуля Jekyll.
Соответственно, новый гем тем и занимается, что сохраняет и восстанавливает внутреннее состояние модулей (и классов). Интерфейс простой:
dump(*mods)-
Копирует данные модулей, возвращая
Hash, который затем можно использовать для восстановления. undump(hash)-
Восстанавливает ранее сохраненные данные. Очевидным образом
dumpиundumpиспользуются в паре. isolate(*mods) { ... }-
Сохраняет данные указанных модулей, выполняет блок кода, восстанавливает сохраненные данные обратно.
Сохраняются только переменные объекта указанных классов и модулей и переменные класса, причем последние — только принадлежащие непосредственно указанному классу или модулю, а не унаследованные им. Что это такое и в чем разница, можно посмотреть в моей давней статье. Константы не сохраняются, чтобы не вызывать лишних предупреждений. Если у вас где-то меняется содержимое констант-контейнеров, это нужно учитывать и обрабатывать как-то иначе.
Тут я хочу подчеркнуть, что гем предназначен для использования только в тестах и не обеспечивает полноценной изоляции контекста. Это не средство для создания «песочницы» или чего-то подобного в рабочих задачах.
Данные копируются через deep_dup, со всеми его ограничениями. И тут самое время перейти к следующему гему…
is-deep
Один из множества вариантов «глубокого» копирования и слияния. Основная особенность именно этой моей реализации — слияние массивов по ключам.
Если коротко, то гем добавляет классам Array и Hash три метода:
deep_dup-
«Глубокое» копирование, т.е. новый объект содержит копии (причем тоже «глубокие») элементов исходного, а не сами эти элементы.
При этом для вложенных объектов рекурсивно проверяется, поддерживает ли тот метод
deep_dup, если да, то вызывается он, если нет — стандартныйdup. Еслиdupбросает исключение (например, так делают объектыIO), берется сам исходный объект. deep_merge!(other, array_strategy: nil)-
«Глубокое» слияние «на месте», т.е. внутрь объекта, на котором вызван метод.
deep_merge(other, array_strategy: nil)-
«Глубокое» слияние в новый объект.
Причем данный метод определен в общем виде в модуле
IS::Deepпримерно какdeep_dup.deep_merge!(...)(на самом деле сложнее, но не суть).
Стратегию необязательно передавать при каждом вызове, можно указать присвоив значение IS::Deep::array_strategy.
Теперь что касается стратегий. Определены следующие варианты:
IS::Deep::ArrayStrategies::REPLACE-
Заменяет старый массив новым.
IS::Deep::ArrayStrategies::CONCAT-
Тупо плюсует новый массив к старому.
IS::Deep::ArrayStrategies::UNION-
Объединяет уникальные значения.
IS::Deep::ArrayStrategies::KeyBased-
А это уже класс, объекты которого создаются с указанием поля ключа. Для наиболее распространенных вариантов есть предсозданные в константе
IS::Deep::ArrayStrategies::KEY_BASED— это хеш с ключами:detect(пытается найти один их этих вариантов),:id,:name,:key,:host. - Произвольный callable-объект
-
В первую очередь имеются в виду лямбды и другие объекты класса
Proc, но необязательно. Главное, нужен методcall, принимающий на вход два массива и возвращающий один.
Замечу, что ни deep_dup, ни deep_merge! ничего не знают о внутренних переменных объектов. Для ваших собственных классов, если нужно, следует
заинклюдить IS::Deep и определить deep_dup и deep_merge! так, как вам будет угодно.
Подробнее см. в README репозитория inat-get/is-deep.
Вместо итого
Оба гема в принципе рабочие, но, как обычно, версия 0.8.x означает публичную альфу, не более того.