shikhalev.*

jekyll-is-images

Сначала коротко про jekyll-is-images. По срав­не­нию с со­сто­я­ни­ем на прош­лый пост в ос­нов­ном произошло много мелких правок мелких багов, из от­но­си­тель­но су­щес­т­вен­но­го — довел до ума работу галерей в ре­жи­ме слайд-шоу (впрочем, и там есть куда расти). А третью цифру я поднял при сме­не зависимости с is-kramdown-hooked на jekyll-is-hookdown, о ко­то­ром и поговорим далее. Это повлияло на то, что следует указывать в _config.yml — вместо input: ISKram в под­раз­де­ле kramdown следует указывать markdown: Hookdown в кор­не конфига. А под­раз­дел kramdown остается полностью рабочим, и там можно указывать любой input, на­при­мер — GFM1 (что я и собираюсь сделать на сай­те в бли­жай­шее время).

jekyll-is-hookdown

Сгенерированная картинка чисто для красоты

Итак, что же это такое? Это специальный гем, который «хакает» процесс обработки markdown в Je­kyll, добавляя хук на тот момент, когда страница уже распарсена во внут­рен­нее AST-представление и еще не пре­об­ра­зо­ва­на во что-то другое. Зачем это надо? Чтобы не де­лать лишней работы по пар­син­гу исходного markdown или фи­наль­но­го HTML.

Поскольку я планирую этот подход использовать не толь­ко в jekyll-is-images, вполне логично было вынести этот механизм в от­дель­ный гем. Более того, подобный хак плохо дублируется, и втыкать его в каж­дый функциональный плагин по отдельности потребует много хитрых оберток и постоянного разруливания конфликтов…

В общем, главный недостаток такого подхода в том, что он по су­ти формирует экосистему, несовместимую с дру­ги­ми экосистемами, использующими тот же хак… Поэтому я планирую отдельно пройтись по jekyll-is-images и четко разделить возможности, требующие хака, т.е. пе­ре­ин­тер­пре­та­цию стандартного markdown-синтаксиса, и тот же функционал в li­quid-тегах, хака соответственно не требующий.

Небольшое историческое отступление

Сначала я запихнул хук внутрь собственно Kramdown через кастомный пар­сер — в ге­ме is-kramdown-hooked. Причем это решение не тре­бо­ва­ло зависимости от Je­kyll, но — обратная сторона медали — и не осо­бо-то интегрировалось с сис­те­мой хуков в Je­kyll. Кроме того, использование кас­том­но­го пар­се­ра не поз­во­ля­ло использовать другие кастомные пар­се­ры…

А еще в про­цес­се работы я понял, что нужный мне момент вполне доступен и извне Kram­down — это момент между созданием Kramdown::Document и вызовом его to_html (или to_latex, etc).

Принцип работы

Во-первых, определен кастомный конвертер в Jekyll. Естественно, он просто унаследован от стандартного, и всего один метод переопределен. В _config.yml требуется указать markdown: Hookdown чтобы использовать этот конвертер вместо стандартного.

Во-вторых, и это уже чистый хак (или грязный…) к стандартному набору хуков Jekyll добавлено событие :post_parse для страниц, документов и постов. Тут на самом деле довольно интересно: почему-то Jekyll легко позволяет расширять систему хуков в разрезе объектов, но не дает этого делать в разрезе событий… Приходится хакать, благо в Ruby это сделать легко, но проблема тут, конечно, в том, что хак полагается на внутреннюю структуру, а не интерфейс.

Зато теперь можно писать:

Jekyll::Hooks::register [ :pages, :documents ], :post_parse do |page, document|
  # Тут что-то делаем с document (это Kramdown::Document)
end

И в-третьих, уже в порядке дополнительного расширения имеется хук на элементы по типам. Он уже вешается не через стандартный механизм, поскольку у стандартного параметров не хватает.

JekyllIS::Hookdown::register_element_hook [ :pages, :documents ], :img do |page, element|
  # Делаем что-то с element (Kramdown::Element)
end

При этом важно, что ваш обработчик возвращает:

  • Kramdown::Element — заменит элемент, на котором был вызван хук, в дереве документа.

  • nil — не приведет к изменениям в документе (если вы сами внутри обработчика не модифицировали дочерние элементы данного).

  • :delete — удалит элемент, на котором вызван хук из дерева документа.

  • Любые другие значения трактуются как ошибочные.

Использование

Думаю, уже понятно, что данный гем используется из других плагинов — расположенных в каталоге _plugins или в отдельных гемах — неважно.

При этом чтобы он работал, нужно включить кастомный конвертер в _config.yml, что делается конечным пользователем, а не разработчиком плагина… Зато разработчик плагина может это проверить посредством JekyllIS::Hookdown::enabled?, что крайне рекомендуется делать, чтобы конечный пользователь не получил неожиданных и непонятных для него ошибок.

Что дальше?

  • По jekyll-is-hookdown я не вижу направлений для расширения функциональности без потери логики. Буду писать документацию и править баги, если вдруг появятся.

  • По jekyll-is-images планов полно, приглашаю в Issues.

  • is-kramdown-hooked отправится в архив.

  1. GitHub Flavored Markdown. Конкретно мне это нужно для того, чтобы использовать ``` (тройной бэктик) для блоков кода вместо окружения {% highlight %}.