--- layout: post title: Использование YAML в Jekyll/Liquid # Front Matter является словарём (структурой), т.е. все элементы # верхнего уровня должны быть вида <имя>: <значение> # Пустые строки между элементами не несут нагрузки. # Комментарии обозначены знаком # # Имена могут содержать пробелы scalar element: 42 # комментарии могут быть в конце любой строки # Для логических переменных можно использовать true/false boolean_element: true # причём независимо от регистра another_boolean_element: FalSE # Кавычки (двойные, одинарные) не включаются в состав строки sequence_element: [zero, 1, 2, 3, 'inf'] # последовательность # (список, массив) в виде строки # Отображение (словарь) с несколькими элементами, построчно dictionary_element: an_integer_number: 12 a_float: 0.123 some_string: This is a string with--- AnotherString: "В кавычках можно спрятать: двоеточие" some_timestamp: 2019-04-20 # это дата (точнее, timestamp) # В словаре может быть последовательность # (последовательность может содержать строки) a_sequence: [1, 2, three, 'some string, and more'] another_sequence_element: # Содержит всего один элемент - последовательность (массив) # из 3 элементов, заданную "построчно" - 3.14 # Cловарь можно задать и "строчно". # Пробелы после двоеточий важны! - {a: 1, b: 0.4, c: "2019-04-30 12:51 +0400"} - Это строка - !!str 2019-03-14 # эта дата сохранена как строка! --- > TL;DR; Надо быть очень аккуратным, YAML очень требователен к синтаксису! [YAML](https://yaml.org) - это "простой" текстовый язык разметки, предназначенный для "человеко-читаемого" представления данных и конкурирующий с [json](https://www.json.org/), но на деле весьма нетривиален[^1]. Однако в простых случаях данные в формате [YAML](https://yaml.org) действительно проще понимать и редактировать в текстовом виде, чем в том же json. В Jekyll YAML используется и для конфигурации (`_config.yml`), и для хранения данных, а посему активно используется в "скриптах" Liquid для динамического формирования страниц[^2]. Сразу предупрежу, что если **Jekyll обнаружит ошибку в формате YAML**, страница или сайт целиком не будут обновлены. Поэтому крайне рекомендуется [поставить Jekyll локально]({% post_url 2019-04-24-local-jekyll %}) и проверять корректность YAML-элементов в валидаторе (см. [Ссылки](#ссылки)). > Минута теории. В YAML есть [три вида объектов](https://yaml.org/spec/1.2/spec#kind): **последовательности** (списки, массивы), **словари** (структуры, отображения) и **скаляры**. Последовательности и словари можно записывать двумя способами, построчно и "в скобках" (аналогично Python или формату json). Примеры: ```yaml # Front Matter является словарём, т.е. все элементы # верхнего уровня должны быть вида <имя>: <значение> # Пустые строки между элементами не несут нагрузки. # Комментарии обозначены знаком # # Имена могут содержать пробелы scalar element: 42 # комментарии могут быть в конце любой строки # Кавычки (двойные, одинарные) не включаются в состав строки sequence_element: [zero, 1, 2, 3, 'inf'] # последовательность # (список, массив) в виде строки # Для логических переменных можно использовать true/false boolean_element: true # причём независимо от регистра another_boolean_element: FalSE # Отображение (словарь) с несколькими элементами, построчно dictionary_element: an_integer_number: 12 a_float: 0.123 some_string: This is a string with--- AnotherString: "В кавычках можно спрятать: двоеточие" some_timestamp: 2019-04-20 # это дата (точнее, timestamp) # В словаре может быть последовательность # (последовательность может содержать строки) a_sequence: [1, 2, three, 'some string, and more'] another_sequence_element: # Содержит всего один элемент - последовательность (массив) # из 3 элементов, заданную "построчно" - 3.14 # Cловарь можно задать и "строчно". # Пробелы после двоеточий важны! - {a: 1, b: 0.4, c: "2019-04-30 12:51 +0400"} - Это строка - !!str 2019-03-14 # эта дата сохранена как строка! ``` Этот "зоопарк" добавлен в заголовок (Front Matter) [markdown-исходника этой страницы](https://raw.githubusercontent.com/atremba/atremba.github.io/master/_posts/2019-04-20-yaml.md). Отмечу, что пример содержит словарь, элементами которого являются: число, пара логичских переменных, последовательность (массив), вложенный словарь и ещё одна последовательность. ## YAML в файле конфигурации `_config.yml` (Jekyll) Данный вопрос заслуживает [отдельного рассмотрения]({ post_url 2019-04-28-jekyll-main-configuration }). ## YAML как источник данных на страницах (Liquid) В Jekyll каждая страница может иметь заголовок в YAML разметке ([Front Matter](https://jekyllrb.com/docs/front-matter/)), ограниченный строками c тремя дефисами. ```yaml --- # YAML-блок, содержащий словарь --- ``` Данный блок содержит **словарь**, который добавляется к переменной **page**, то есть все элементы должны иметь вид ```yaml name: content ``` причём двоеточие "прилипает" к имени, а после двоеточия есть пробел. Содержимое может быть списком, словарём или скалярной переменной (строкой, числом и т.п.), но об этом чуть ниже. При этом формируется поле `page.name` с указанным содержимым. Доступ к полям структуры/словаря осуществляется либо с помощью точки, либо с помощью индексации квадратными скобками: `page['name']` эквивалентно `page.name`. Индексация в скобках применяется для ключей с пробелам. Когда Jekyll обрабатывает страницы, шаблонизатор Liquid с помощью тегов[^3] {% raw %}`{{ ... }}` или `{% ... %}`{% endraw %} подставляет известные переменные и/или преобразует их. Подробнее об различиях этих видов тегов см. [запись о Liquid](\TODO), но для демонстрации понадобится только первый тип: {% raw %}`{{...}}` {% endraw %}, выводящий значения переменных. ### Отображение переменных из примера выше Текст примера был добавлен в заголовок [исходного md-файла этой страницы](https://raw.githubusercontent.com/atremba/atremba.github.io/master/_posts/2019-04-20-yaml.md). Результаты зависят не только от того, как YAML **преобразовал текст в данные**, но и от того, как Liquid **отобразил эти данные обратно в текст**. 1. Если ключ (id) в словаре содержит пробел, необходимо использовать доступ по индексации {% raw %}`{{ page['scalar element'] }}` {% endraw %} выведет: ```text {{ page['scalar element'] }} ``` 2. Значения логических переменных (флаги) не зависят от регистра: {% raw %}`{{ page.boolean_element }}` и `{{ page.another_boolean_element }}` {% endraw %} отображаются как: ```text {{ page.boolean_element }} {{ page.another_boolean_element }} ``` 3. Последовательность {% raw %}`{{ page.sequence_element }}` {% endraw %} при выводе "слипается": ```text {{ page.sequence_element }} ``` 4. Для последовательностей доступна индексация, начинающаяся с нуля: {% raw %}`{{ page.sequence_element[0] }}`, `{{ page.sequence_element[4] }}` {% endraw %} выведут ```text {{ page.sequence_element[0] }} {{ page.sequence_element[4] }} ``` Кстати, выход за границы или некорректное значение индекса безопасен, результатом будет пустой элемент: {% raw %}`{{ page.sequence_element[10] }}` и `{{ page.sequence_element[abc] }}` {% endraw %} выведут (внутри кавычек): `"{{ page.sequence_element[10] }}"` и `"{{ page.sequence_element[abc] }}"`. Хотя это больше относится к интерпретации переменных в Liquid (точнее, Ruby). 5. {% raw %}`Строка {{ page.dictionary_element.AnotherString }}` и элемент времени `{{ page.dictionary_element.some_timestamp }}` {% endraw %} выведут: ```text {{ page.dictionary_element.AnotherString }} {{ page.dictionary_element.some_timestamp }} ``` Обратите внимание, что из полного описания момента времени отобразилась только дата. Это - вывод по умолчанию. Для более полной информации надо использовать [**фильтр** Liquid]({ post_url 2019-05-12-jekyll-and-liquid }): {% raw %}`{{ page.dictionary_element.some_timestamp | date: "Year %Y %H:%M" }}` {% endraw %} ```text {{ page.dictionary_element.some_timestamp | date: "Year %Y %H:%M" }} ``` 6. По умолчанию YAML распознаёт **тип** скаляра по его текстовой записи. Но можно указать его явно, указав префикс. Например, указание `!!str` в последнем элементе последовательности `page.another_sequence_element[3]` выведет строку. ```text {{ page.another_sequence_element[3] }} ``` С одной стороны, тип данных важен в YAML, а с другой, прикладной стороны, Liquid автоматически приводит их к нужному типу. По всей видимости, это связано с тем, что Liquid написан на Ruby. К строке можно применить фильтр данных для времени: ({% raw %}`{{page.another_sequence_element[3] | date: "%d/%m/%Y"}}`{% endraw %} выведет `{{page.another_sequence_element[3] | date: "%d/%m/%Y"}}`). Аналогично и к строке `page.dictionary_element.some_string`, и к моменту времени `page.dictionary_element.some_timestamp` можно применить операцию замены (фильтр Liquid `| replace: "-", "=>"`): {% raw %}`{{page.dictionary_element.some_string | replace: "-", "=>"}}` и `{{page.dictionary_element.some_timestamp | replace: "-", "=>"}}` {% endraw %} выведут ```text {{page.dictionary_element.some_string | replace: "-", "=>"}} {{page.dictionary_element.some_timestamp | replace: "-", "=>"}} ``` Как видно, для даты было использовано её текстовое значение по умолчанию. Кроме чисел, строк и элементов времени, скалярами в YAML могут быть бинарные данные (например, записанные в текстовом формате Base64), и вообще скалярам может быть присвоен произвольный тип[^4], лишь бы он корректно обрабатывался приложением, "распознающим" этот тип. Вышенаписанного достаточно, чтобы использовать YAML. Некоторые источники можно почитать в разделе [Ссылок](#ссылки). --- ## YAML как источник данных Вместо базы данных Jekyll имеет достатуп к данным из папки `_data`, которая может содежать и YAML-файлы. ([в плане](/TODO#yaml-data)) ## Типичные ошибки В принципе, в сообщения об ошибках в YAML Jekyll [указывает строку, позицию и описание](https://help.github.com/en/articles/page-build-failed-invalid-yaml-in-data-file). - В имени (заголовке) записи/страницы есть двоеточие. В этом случае надо заключить его в кавычки (двойные или одинарные), т.е. вместо ```yaml title: Блог: выбор размещения ``` писать ```yaml title: "Блог: выбор размещения" ``` - Символ табуляции в префиксах (и не только). Jekyll прямо запрещает использование табуляции в конфигурационном файле. Если это и не приводит к ошибкам, вместо заданных Jekyll использует настройки по умолчанию. Проверить конфигурационный файл можно командой `bundle exec jekyll doctor` (или просто `jekyll doctor`), а параметр `--strict_front_matter` (или поле `strict_front_matter: true` в `_config.yml`) заставляет Jekyll не пропускать ошибки в заголовках Front Matter. - Запись полей словаря в неаккуратном формате: ```yaml key:value ``` вызовет ошибку сборки, необходим пробел. Правильно: ```yaml key: value ``` Вариант с `key : value` тоже работает, но лучше придерживаться общепринятого стандарта. ## Понимание YAML YAML-данные имеют три ипостаси: 1. Текстовую (**presentation**), описывающую представление данных (presentation) - собственно, именно в таком виде данные хранятся в текстовых файлах, обычно в формате UTF-8. 2. В виде графа (**representation**), в простом случае без ссылок - просто дерево, каждый узел которого (кроме корневого) - список или отображение, а висячая вершина/лист - скаляр. Особенность тут в том, что каждый тег имеет свой **тип** (число, строка и т.д.), который в в YAML называется... "тегом"[^4]. 3. В виде данных на соответствующем языке программирования (**native**). Они образуют цепочку преобразований, включающую сериализацию, см. пример из спецификации: ![from the specification of YAML](\assets\2019-04-20-yaml\overview2.png) По умолчанию данные записаны в текстовых файлах в формате UTF-8. Скаляры представляются в тексте несколькими символами UTF-8 **или их отсутствием**. --- ## Ссылки - - сайт YAML со [спецификацией](https://yaml.org/spec/1.2/spec). Спецификация кажется переусложнённой, но примеры вполне читаемы, особенно если знать, что: - во [втором разделе](https://yaml.org/spec/1.2/spec#Preview) примеры и в левых, и в правых табличках, - в [пятом разделе](https://yaml.org/spec/1.2/spec#Characters) (более детальном) в левых таблицах - исходный текст, а в правых - "каноническое" представление YAML этих данных (результат разбора и интерпретации), - существует [одностраничное описание разметки](https://yaml.org/refcard.html) (reference card). - - краткая памятка по YAML (cheat sheet), ссылка на [несколько](http://www.yamllint.com) [других](http://www.kuwata-lab.com/kwalify) валидаторов, включая [онлайн-конвертер в json/python](http://yaml-online-parser.appspot.com). - - онлайн-проверка синтаксиса YAML на корректность. - - ещё один валидатор. - - знакомство с YAML (на русском языке). - - волшебная Википедия содержит несколько ссылок, а [английская Википедия](https://en.wikipedia.org/wiki/YAML) вдобавок и пояснения по формату --- [^1]: Например, представление данных является [связным направленным графом с корнем, допускающим циклы](https://yaml.org/spec/1.2/spec#representation//). [^2]: Напомню, что в результате получаются обычные HTML-файлы. Jekyll/Liquid всего лишь динамически собирают эти страницы на специальном "языке программирования" Liquid. [^3]: Не путать с "тегами" в смысле "облака тегов" и "тегами" YAML. Теги Liquid по смыслу ближе всего к HTML-тегам. [^4]: Не путать с "тегами" в обычном понимании меток (в смысле "облака тегов") и с "тегами" Liquid. Тег YAML близок _типу данных_, отягощённуму дополнительной информацией (схемой), которая управляет дальнейшей обработкой этого куска данных в конкретном приложении.