This commit is contained in:
ge 2022-10-03 13:03:39 +03:00
parent d14a3f95a7
commit 423468a50b
2 changed files with 1 additions and 340 deletions

View File

@ -13,6 +13,6 @@
* `vk-toot <https://git.nxhs.cloud/ge/vk-toot>`_ -- кросспостер VK -> Mastodon
* `piglet <https://git.hxhs.cloud/ge/piglet>`_ -- клиент DNS API Porkbun
* roadwarrior -- менеджер подключений к VPN и прокси
* `n! <https://git.nxhs.cloud/ge/n>` -- консольный менеджер заметок
* `n! <https://git.nxhs.cloud/ge/n>`_ -- консольный менеджер заметок
Остальное есть в `Gitea <https://git.nxhs.cloud/ge/>`_.

View File

@ -1,339 +0,0 @@
:title: Как я написал этот блог
:date: 5 Aug 22
:type: page
.. note::
Статья малось устарела и не соответсвует текущей действительности.
=======================
Как я написал этот блог
=======================
Я очень давно планировал написать блог. Именно что написать CMS для ведения
блога. Если погуглить, то CMS для этих целей сотни на разных языках с разным
позиционированием и фичами. Но мне хотелось своего.
Первые попытки создать сайт были на `Flask`_, особым успехом не увенчались,
но это была хорошая практика и я немного прокачал свой уровень программирования.
Для блога я не хотел использовать базу данных. Вместо этого я хранил статьи в
исходном Markdown, а метаданные по каждой статье писал в отдельный JSON, который
заменял мне БД. С тем же успехом я мог бы использовать SQLite, но мне нужно было
хранить контент в text/plain.
Блог даже работал, но мне хотелось сделать админку, чтобы можно было писать
прямо в браузере. На этой части разработка заглохла и я окончательно запутался в
импортах в Python)
Прошло ещё много времени, я вернулся к идее, но решил сделать ставку на простоту
и начал писать на `Bottle`_. Здесь разработка остановилась не дойдя даже до
варианта хоть как-то работающей системы. Причины скорее всего надо искать в
лени.
Устав я взял `CMS Bludit`_ и стал вести свои редкие заметки в нём. Решение не
самое плохое, но и не супер отличное. Для простой кастомизации футера пришлось
городить костыли.
.. _Flask: https://flask.palletsprojects.com/
.. _Bottle: https://bottlepy.org/
.. _CMS Bludit: https://www.bludit.com/ru/
Статика VS динамика
===================
Преимуществ у статических сайтов много. Об этом уже много написано. Мне больше
всего в статических сайтах нравится то, что:
* роутингом запросов не управляет какой-либо скрипт, я могу свободно
распоряжаться тем, что у меня есть в DocumentRoot;
* не нужно настраивать application-сервер и реверс-прокси на него.
С редактированием сайта чуть сложнее, так как сперва надо внести изменения
локально, а затем заменить изменившиеся файлы на сервере. Но и тут есть
какие-то решения.
Я рассматривал уже готовые генераторы статических сайтов, но у них всех были
фатальные недостатки:
* они сложные и требуют изучения документации чтобы начать писать;
* написать собственную тему задача весьма непростая;
* они написаны не мной.
Здесь и рождается мой велосипед, которому я не придумал названия. Буду называть
его `генератором`.
*re*\ **Structured**\ *Text*
============================
Я очень долго писал на Markdown, но в один момент понял, что возможностей языка
стало нехватать. Тут пришёлся очень кстати `Python-Markdown`_ к которому можно
было прикручивать `расширения`_. Одно даже сам `написал`_.
Постепенно я пришёл к `reStructuredText`_. Все приколюхи, которые в нём есть
существуют не за счёт расширения синтаксиса плагинами, а заложены в спецификации.
Функциональности из коробки хватает чтобы писать даже `формулы`_.
Такие вещи делаются через `роли` и `директивы`. Некоторые готовые директивы есть
в системе документации `Sphinx`_. Их я собираюсь потихоньку скопировать к себе.
Ещё одно большое преимущество перед Markdown состоит в том, что reStructuredText
поддерживает атрибуты. Вот как это выглядит:
.. code-block:: rst
:title: Как я написал этот блог
:date: 5 Aug 2022
Это нативная фича, которую можно использовать в статьях для добавления метаданных.
По сути это место, где можно сделать настройки, касающиеся отдельно взятой статьи.
Большинство генераторов статических сайтов колхозят нечно подобное в Markdown, делая
исходник статьи непригодным для парсинга другими инструментами. Это причина по
которой в старой реализации блога мне пришлось использовать JSON для метаданных.
.. _Sphinx: https://www.sphinx-doc.org/en/master/index.html
.. _Python-Markdown: https://python-markdown.github.io/
.. _расширения: https://python-markdown.github.io/extensions/
.. аписал: https://pypi.org/project/markdown-alerts/
.. _rST: https://docutils.sourceforge.io/rst.html
.. ормулы: https://docutils.sourceforge.io/docs/ref/rst/mathematics.html
Обзор
=====
Сайт состоит из следующих частей.
rst_blg.py
Непосредственно код генератора. Сейчас там всего около 200 строк без учёта
комментариев.
requirements.txt
Список зависимостей. Стараюсь держать минимум. Пока что там: `docutils`,
`jinja2`, `toml` и `pygments`.
settings.toml
Файл с настройками. Здесь можно переопределить практически всё.
Makefile
Через него запускаются команды для сборки сайта и некоторые другие. Не
является обязательным, но с ним удобнее.
layouts
Директория для шаблонов Jinja2.
assets
Директория для хранения статических файлов, будь то CSS, JS или изображения.
Всё что нужно для визуального оформления страниц. Внутренняя структура
каталога может быть произвольной.
content
Директория с исходниками статей в reStructuredText. Сюда можно также
положить любые файлы и директории.
build
В эту директорию копируются ассеты, файлы и сгенерированный HTML.
Статьи, шаблоны и ассеты могут быть оформлены абсолютно любым образом. Скрипту
безразлично что собирать. Пути и имена всех директорий можно переопределить в
settings.toml.
Исходники с небольшой инструкцией я положил сюда: https://git.nxhs.cloud/ge/blog
Написание постов
================
Тут всё предельно просто. Пишем файл и кладём в директорию `content`. В файле
должны быть указаны обязательные атрибуты ``:title:`` и ``:date:``.
Блог пока не умеет работать с вложенной структурой статей. Поэтому всё будет
свалено в кучу в корневую директорию сайта.
Все статьи добавляются в список постов и отображаются на главной странице.
Для того, чтобы сделать "отдельную" страницу, надо добавить атрибут
``:not_a_post:``.
================= ======= =========== ====================================
Атрибут Тип Умолчание Описание
================= ======= =========== ====================================
``:title:`` Строка Заголовок статьи
``:date:`` Дата Дата публикации, формат задаётся в
settings.toml
``:not_a_post:`` Флаг для одиночных страниц
================= ======= =========== ====================================
Темы оформления
===============
Никаких готовых тем. Пишем CSS вручную. Генератору сайтов всё равно что там
будет. От уровеня представления он никак не зависит. Так что можно писать любые
шаблоны и стили для них.
Отдельно можно задать тему для блоков кода. Список тем для Pygments с превью
есть на странице https://pygments.org/styles/
Чтобы поменять тему, например, на `default` надо выполнить команду:
.. code-block:: shell
make css default
# или
pygmentize -f html -S default -a .highlight > assets/css/pygments/default.css
Затем поменять значение ``theme`` в секции ``pygments`` settings.toml.
.. code-block:: toml
[pygments]
theme = 'default'
Шаблоны
========
Шаблоны можно писать какие угодно. Для этогой сайта я написал три файла.
Приведу их без лишних строк.
**base.j2**
.. code-block:: html+jinja
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="icon" href="data:image/png;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=">
<link rel="stylesheet" href="assets/css/pygments/{{ pygments_theme }}.css">
<link rel="stylesheet" href="assets/css/custom.css">
<title>{{ page_title }} | {{ site_title }}</title>
</head>
<body>
<header>
<p>
{% if posts %}
{{ site_title }}
{% else %}
<a href="/">{{ site_title }}</a> / {{ page_title }}</p>
{% endif %}
</p>
</header>
{% block content %}{% endblock %}
<footer>
<!-- Footer content -->
</footer>
</body>
</html>
**post.j2**
.. code-block:: html+jinja
{% extends "base.j2" %}
{% block content %}
<article>
{{ post | safe }}
<article>
{% endblock %}
**index.j2**
.. code-block:: html+jinja
{% extends "base.j2" %}
{% block content %}
<section>
<ul id="posts">
{% for post in posts %}
<li>
<a href="/{{ post['path'] }}">{{ post['title'] }}</a>
<span class="meta"> — {{ post['date'] }}</span>
</li>
{% endfor %}
</ul>
</section>
{% endblock %}
settings.toml
=============
Сначала я хотел использовать обычный INI, но мне нужно было получать из конфига
словарь. Немного подумал и выбрал TOML. Он отлично сериализируется в словарь,
визуально повторяет INI.
**settings.toml** разделён на несколько секций.
site
Данные касающиеся непосредственно сайта.
title
Название сайта
index_page_title
Заголовок главной страницы. Для всех остальных страниц заголовок
берётся из атрибута.
datatime_format
Формат даты для атрибута ``:date:``.
build
Параметры, используетмые при сборке.
build_dir
Директория, куда будет сохранён собранный сайт.
content_dir
Директория с исходниками статей.
templates_dir
Директория с шаблонами Jinja2.
assets_dir
Директория с ассетами.
pygments
Параметры подсветки синтаксиса в блоках кода.
theme
Стиль Pygments.
docutils
Конфигурация для docutils. Здесь можно указать любые параметры, которые
есть здесь: https://docutils.sourceforge.io/docs/user/config.html
Мне достаточно:
.. code-block:: toml
[docutils]
initial_header_level = 2
section_self_link = true
syntax_highlight = 'short'
Что ещё хочется сделать
=======================
В перспективе я планирую добавить следующие фичи.
- RSS
- OpenGraph
- Webmention
- Расширить возможности rST до уровня Sphinx
- Улучшить CSS
- Комментарии
- Кастомный лейаут для отдельных статей
- Поддержка вложенной структуры статей
.. * https://jinja.palletsprojects.com/en/3.0.x/templates/
* https://docutils.sourceforge.io/docs/user/config.html