По поводу и без Do about and for no reason

Использование YAML в Jekyll/Liquid

TL;DR; Надо быть очень аккуратным, YAML очень требователен к синтаксису!

YAML - это "простой" текстовый язык разметки, предназначенный для "человеко-читаемого" представления данных и конкурирующий с json, но на деле весьма нетривиален1.

Однако в простых случаях данные в формате YAML действительно проще понимать и редактировать в текстовом виде, чем в том же json. В Jekyll YAML используется и для конфигурации (_config.yml), и для хранения данных, а посему активно используется в "скриптах" Liquid для динамического формирования страниц2.

Сразу предупрежу, что если Jekyll обнаружит ошибку в формате YAML, страница или сайт целиком не будут обновлены. Поэтому крайне рекомендуется поставить Jekyll локально и проверять корректность YAML-элементов в валидаторе (см. Ссылки).

Минута теории. В YAML есть три вида объектов: последовательности (списки, массивы), словари (структуры, отображения) и скаляры. Последовательности и словари можно записывать двумя способами, построчно и "в скобках" (аналогично Python или формату json).

Примеры:

# 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-исходника этой страницы. Отмечу, что пример содержит словарь, элементами которого являются: число, пара логичских переменных, последовательность (массив), вложенный словарь и ещё одна последовательность.

YAML в файле конфигурации _config.yml (Jekyll)

Данный вопрос заслуживает [отдельного рассмотрения]({ post_url 2019-04-28-jekyll-main-configuration }).

YAML как источник данных на страницах (Liquid)

В Jekyll каждая страница может иметь заголовок в YAML разметке (Front Matter), ограниченный строками c тремя дефисами.

---
# YAML-блок, содержащий словарь
---

Данный блок содержит словарь, который добавляется к переменной page, то есть все элементы должны иметь вид

name: content

причём двоеточие "прилипает" к имени, а после двоеточия есть пробел. Содержимое может быть списком, словарём или скалярной переменной (строкой, числом и т.п.), но об этом чуть ниже. При этом формируется поле page.name с указанным содержимым. Доступ к полям структуры/словаря осуществляется либо с помощью точки, либо с помощью индексации квадратными скобками: page['name'] эквивалентно page.name. Индексация в скобках применяется для ключей с пробелам.

Когда Jekyll обрабатывает страницы, шаблонизатор Liquid с помощью тегов3 {{ ... }} или {% ... %} подставляет известные переменные и/или преобразует их. Подробнее об различиях этих видов тегов см. запись о Liquid, но для демонстрации понадобится только первый тип: {{...}} , выводящий значения переменных.

Отображение переменных из примера выше

Текст примера был добавлен в заголовок исходного md-файла этой страницы. Результаты зависят не только от того, как YAML преобразовал текст в данные, но и от того, как Liquid отобразил эти данные обратно в текст.

  1. Если ключ (id) в словаре содержит пробел, необходимо использовать доступ по индексации {{ page['scalar element'] }} выведет:

    42
    
  2. Значения логических переменных (флаги) не зависят от регистра: {{ page.boolean_element }} и {{ page.another_boolean_element }} отображаются как:

    true
    false
    
  3. Последовательность {{ page.sequence_element }} при выводе "слипается":

    zero123inf
    
  4. Для последовательностей доступна индексация, начинающаяся с нуля: {{ page.sequence_element[0] }}, {{ page.sequence_element[4] }} выведут

    zero
    inf
    

    Кстати, выход за границы или некорректное значение индекса безопасен, результатом будет пустой элемент: {{ page.sequence_element[10] }} и {{ page.sequence_element[abc] }} выведут (внутри кавычек): "" и "". Хотя это больше относится к интерпретации переменных в Liquid (точнее, Ruby).

  5. Строка {{ page.dictionary_element.AnotherString }} и элемент времени {{ page.dictionary_element.some_timestamp }} выведут:

    В кавычках можно спрятать: двоеточие
    2019-04-20
    

    Обратите внимание, что из полного описания момента времени отобразилась только дата. Это - вывод по умолчанию. Для более полной информации надо использовать [фильтр Liquid]({ post_url 2019-05-12-jekyll-and-liquid }):

    {{ page.dictionary_element.some_timestamp | date: "Year %Y %H:%M" }}

    Year 2019 00:00
    
  6. По умолчанию YAML распознаёт тип скаляра по его текстовой записи. Но можно указать его явно, указав префикс. Например, указание !!str в последнем элементе последовательности page.another_sequence_element[3] выведет строку.

    2019-03-14
    

    С одной стороны, тип данных важен в YAML, а с другой, прикладной стороны, Liquid автоматически приводит их к нужному типу. По всей видимости, это связано с тем, что Liquid написан на Ruby. К строке можно применить фильтр данных для времени: ({{page.another_sequence_element[3] | date: "%d/%m/%Y"}} выведет 14/03/2019).

    Аналогично и к строке page.dictionary_element.some_string, и к моменту времени page.dictionary_element.some_timestamp можно применить операцию замены (фильтр Liquid | replace: "-", "=>"): {{page.dictionary_element.some_string | replace: "-", "=>"}} и {{page.dictionary_element.some_timestamp | replace: "-", "=>"}} выведут

    This is a string with=>=>=>
    2019=>04=>20
    

    Как видно, для даты было использовано её текстовое значение по умолчанию.

Кроме чисел, строк и элементов времени, скалярами в YAML могут быть бинарные данные (например, записанные в текстовом формате Base64), и вообще скалярам может быть присвоен произвольный тип4, лишь бы он корректно обрабатывался приложением, "распознающим" этот тип.

Вышенаписанного достаточно, чтобы использовать YAML. Некоторые источники можно почитать в разделе Ссылок.


YAML как источник данных

Вместо базы данных Jekyll имеет достатуп к данным из папки _data, которая может содежать и YAML-файлы. (в плане)

Типичные ошибки

В принципе, в сообщения об ошибках в YAML Jekyll указывает строку, позицию и описание.

  • В имени (заголовке) записи/страницы есть двоеточие. В этом случае надо заключить его в кавычки (двойные или одинарные), т.е. вместо

    title: Блог: выбор размещения
    

    писать

    title: "Блог: выбор размещения"
    
  • Символ табуляции в префиксах (и не только). Jekyll прямо запрещает использование табуляции в конфигурационном файле. Если это и не приводит к ошибкам, вместо заданных Jekyll использует настройки по умолчанию. Проверить конфигурационный файл можно командой bundle exec jekyll doctor (или просто jekyll doctor), а параметр --strict_front_matter (или поле strict_front_matter: true в _config.yml) заставляет Jekyll не пропускать ошибки в заголовках Front Matter.

  • Запись полей словаря в неаккуратном формате:

    key:value
    

    вызовет ошибку сборки, необходим пробел. Правильно:

    key: value
    

    Вариант с key : value тоже работает, но лучше придерживаться общепринятого стандарта.

Понимание YAML

YAML-данные имеют три ипостаси:

  1. Текстовую (presentation), описывающую представление данных (presentation) - собственно, именно в таком виде данные хранятся в текстовых файлах, обычно в формате UTF-8.
  2. В виде графа (representation), в простом случае без ссылок - просто дерево, каждый узел которого (кроме корневого) - список или отображение, а висячая вершина/лист - скаляр. Особенность тут в том, что каждый тег имеет свой тип (число, строка и т.д.), который в в YAML называется... "тегом"4.
  3. В виде данных на соответствующем языке программирования (native).

Они образуют цепочку преобразований, включающую сериализацию, см. пример из спецификации: from the specification of YAML

По умолчанию данные записаны в текстовых файлах в формате UTF-8.

Скаляры представляются в тексте несколькими символами UTF-8 или их отсутствием.


Ссылки


  1. Например, представление данных является связным направленным графом с корнем, допускающим циклы.

  2. Напомню, что в результате получаются обычные HTML-файлы. Jekyll/Liquid всего лишь динамически собирают эти страницы на специальном "языке программирования" Liquid.

  3. Не путать с "тегами" в смысле "облака тегов" и "тегами" YAML. Теги Liquid по смыслу ближе всего к HTML-тегам.

  4. Не путать с "тегами" в обычном понимании меток (в смысле "облака тегов") и с "тегами" Liquid. Тег YAML близок типу данных, отягощённуму дополнительной информацией (схемой), которая управляет дальнейшей обработкой этого куска данных в конкретном приложении.