Wrangler — генератор статических сайтов для людей, которые не создают блоги.
Функции:
В Springload нам часто приходится создавать статические сайты, но мы изо всех сил пытались найти инструмент, который... позволил бы нам с этим справиться. Введите Рэнглера. Он не ожидает, что ваш контент будет отформатирован как серия сообщений в блоге. Он не копирует статические ресурсы, не обрабатывает SaSS и не готовит кофе.
Он делает одну вещь, и делает это довольно хорошо.
Мы надеемся, что вам это понравится.
Установите Wrangler через pip:
pip install wrangler
Создайте новый проект в текущем каталоге .
wrangler create .
Это создаст кучу каталогов. Чтобы проверить, работает ли это, создайте небольшой автоматически сгенерированный сайт:
wrangler build content www
Обслуживание вашего сайта через движок по вашему выбору или удобный встроенный сервер по адресу http://127.0.0.1:8000/
:
wrangler serve www
Хотите отслеживать изменения в своем контенте и шаблонах и автоматически перестраивать их? Для этого есть приложение. Задача watch принимает все те же параметры, что и build
.
wrangler watch content www
Wrangler следует довольно простому соглашению о файловой системе:
my-site/
| --content
| ----index.yaml
| --lib
| --templates
| ----template.j2
| --var
| --wrangler.yaml
| --www
content
- каталог контента. Структура вашего сайта будет отражать этот каталог. Если у вас есть content/index.yaml
, вы получите www/index.html
templates
— где живут ваши шаблоны jinja2 .j2
www
— корневая папка веб-сайта. Действительно, вы можете выводить куда угодно, мы просто выбрали www как разумное значение по умолчанию.lib
— сюда входят пользовательские расширения, классы, хуки и т. д. Напишите несколько модулей Python и передайте результаты обратно в Wrangler.var
— содержит кеш шаблонов и кеш файловых объектов (wrangler использует готовую комбинацию Pickle и Shelve) Все эти пути можно изменить в wrangler.yaml
Wrangler предполагает, что вы работаете с какими-то структурированными данными и хотите перевести эти файлы данных в HTML (или даже в классический ASP, если вас это интересует).
В пакет включены три парсера: json, yaml и markdown (с начальным текстом yaml). Они работают отдельно для каждого файла, поэтому это совершенно справедливо:
my-site/
| --content
| ----index.yaml
| ----page-2.json
| ----page-3.md
meta :
title : My title
template : template.j2
description : " My cool page! "
data :
content : " Here's some page content! "
blocks :
- " Lots of content "
- " even more content "
- " wow, so much content! "
{
"meta" : {
"title" : " My title " ,
"template" : " template.j2 " ,
"description" : " My cool page! "
},
"data" : {
"content" : " Here's some page content! " ,
"blocks" : [
" Lots of content " ,
" even more content " ,
" wow, so much content! "
]
}
}
---
title : My title
template : template.j2
description : " Markdown uses yaml front-matter "
---
# A heading!
Some paragraph text
## Another heading!
Even more text
---
Nice HR you got there.
* A list
* with some
* list items
Вот хорошая шпаргалка по уценке
Используйте метаданные для всего, что связано со страницей. Вы можете добавить сюда все, что захотите, но есть несколько зарезервированных слов:
meta :
title : " Musings on the pronounciation of doge "
alias : " Doge "
template : " template.j2 "
class : DogePage
hide_from_nav : true
description : " Is it dog-e, doog, douge, douche? How do I properly refer to this meme? "
keywords : ["much", "analytics", "such", "SEO"]
output_file_extension : asp
weight : 1
thumbnail : /assets/images/thumb/doge-100x100.jpg
related :
- content/other-page.yaml
- content/pages/this-page-here.yaml
Название страницы в навигации (и, возможно, ваш тег заголовка тоже)
Путь к шаблону относительно templates_dir
вашего wrangler.yaml.
Сокращение заголовка, которое вместо этого можно использовать в навигации.
Пытается заменить класс Python для объекта страницы. Должен быть подклассом wrangler.Core.Page
Скройте эту страницу из дерева навигации.
Что написано на банке
Список ключевых слов
Переопределить output_file_extension
по умолчанию из wrangler.yaml. Страница будет отображаться с этим расширением.
Удобно для упорядочивания страниц от нижнего к высокому. По умолчанию Wrangler будет использовать алфавитную сортировку файловой системы.
Путь к миниатюре изображения
Список связанных страниц. В вашем шаблоне это позволит вам получить базовую информацию о других страницах (например, заголовок и описание).
Wrangler автоматически добавляет некоторые элементы в ваши метаданные. В ваших шаблонах вы можете получить доступ к:
{{ meta.url }}
{{ meta.segments }}
{{ meta.filepath }}
{{ meta.mtime }}
{{ meta.children }}
{{ meta.parents }}
{{ meta.parents_siblings }}
Путь к построенному файлу относительно output_dir
, например /
Список всех сегментов URL: ["sub-directory", "index.html"]
Имя входного файла
Измененное время. Вы можете использовать это, например, для создания временной метки в блоге.
Любые прямые дочерние элементы текущего каталога.
Все узлы между текущим файлом и /
Одноуровневые элементы родительского каталога.
Данные страницы не являются обязательными.
Давайте посмотрим на эту маленькую страницу custom-page.yaml
Вы можете создать wrangler страницу без данных и жестко закодировать все в шаблоне custom-page.j2
если хотите.
meta :
title : No content!
template : custom-page.j2
output_file_extension : txt
Это создаст www/custom-page.txt
.
Wrangler поставляется с фильтром уценки jinja2. Если вы используете файл уценки, она доступна по адресу data.content
. Перенаправьте содержимое в фильтр уценки, и все готово.
< div class = " content " >
{{ data.content|markdown }}
</ div >
Markdown — это фантастический формат письма, но он может иметь некоторые ограничения, когда вы имеете дело с более структурированными данными. Для файлов YAML и JSON получите доступ к частям словаря data
и подключите их по своему усмотрению:
< div class = " content " >
{{ data.content }}
{% for block in data . blocks %}
< p >{{ block }}</ p >
{% endfor %}
</ div >
Редактируемые параметры wrangler сохраняются в файле wrangler.yaml
в корне вашего проекта.
Откройте его, и вы обнаружите три узла: wrangler
, site
и extensions
Это основная конфигурация, самое главное. Выглядит это примерно так:
wrangler :
# Template directory relative to your project root
templates_dir : templates
# Default template to load if no template is specified for a page
default_template : template.j2
# Default output file extension. Note this can be overwritten in the content
# by specifying 'output_file_extension' in the 'meta' area
output_file_extension : html
# Supported data formats. Ensure a parser is registered for each type.
# More information about parsers can be found in the link at the top of the file.
data_formats : ['yaml', 'yml', 'json', 'js', 'md', 'markdown']
# Ignore hidden files, and files starting with underscores
ignore : ['.','_']
# Prints all the internal plumbing output to stdout
verbose : false
# Always force all pages to be rendered
force : false
# Run without the cache (useful for developing custom page classes, to prevent them
# from being cached each run).
nocache : false
# The location of the template cache zip file.
# Ensure the var path exists and is writeable by the user
build_cache_file : var/build.cache
compiled_templates_file : var/jinja
compiled_templates_log : var/jinja.log
# Custom methods/classes go in the lib directory, for instance
# lib/Page.py or lib/Extensions.py or lib/Filters.py
lib_path : lib
# file continues....
Настройте все расширения, которые вы установили здесь. Расширения позволяют запускать любую функцию Python и вставлять результаты в ваши шаблоны.
# wrangler.yaml continued...
extensions :
# Sitemap generates a tree structure of your entire site, relative to the
# webroot specified here
#
# {{ extensions.sitemap }}
#
# We leave it up to you to iterate over the sitemap and print everything in
# a pretty manner, but this gist might get you started:
# https://gist.github.com/joshbarr/111
sitemap :
webroot : /
{{ extensions.cachebuster }}
Включены некоторые расширения по умолчанию: sitemap
, fileinfo
и cachebuster
Переменные сайта — это переменные всего сайта, доступные внутри ваших шаблонов как дочерние элементы объекта site
.
Например, чтобы получить путь к изображениям, вы можете вызвать {{ site.paths.images }}
и сэкономить время на вводе текста.
# wrangler.yaml continued...
site :
paths :
css : assets/css
js : assets/js
assets : assets
{# Hey, it's those handy vars I set in my site_vars #}
{{ site.paths.css }}
Вся эта документация также находится в файле wrangler.yaml
, так что вы не заблудитесь!
Принимает один позиционный аргумент — путь для создания нового проекта:
cd my-sweet-site && wrangler create .
# or
wrangler create my-sweet-site
input_dir
Входной каталог, например site/content
output_dir
Выходной каталог, например www
wrangler build content www
Принудительный рендеринг независимо от того, когда ваш контент был последний раз изменен:
wrangler build content www --force
Перекэшируйте все объекты страницы.
wrangler build content www --nocache
Измените расширение выходного файла для всех страниц на классический asp
. (Зачем кому-то это делать?)
wrangler build content www -o ".asp"
Измените формат данных для поиска во input_dir
на json
wrangler build content www -d 'json'
Измените расположение каталога шаблонов
wrangler build content www -t other-templates-dir
Измените местоположение файла конфигурации
wrangler build content www -c site/config/wrangler.yaml
Имеет все те же возможности, что и wrangler build
Выводить всю сантехнику каждый раз при изменении файла:
wrangler watch content www --verbose
Принимает один позиционный аргумент (каталог для обслуживания) и необязательный --port
(по умолчанию 8000).
wrangler serve www --port 8001
Удалите кеш шаблонов и кеш объектов из каталога «var».
wrangler clean
Wrangler загружает все модули Python, найденные в каталоге lib
вашего проекта, при загрузке.
Это дает вам возможность расширять основные функции и манипулировать данными страницы — например, вы можете загружать некоторые значения из базы данных и делать их доступными в своих шаблонах.
Когда вы вызываете build
, wrangler создает представление древовидной структуры в вашем каталоге content/
.
Он использует двусвязный список объектов Node
, которые объединяются в NodeGraph
, удобный контейнер для работы с узлами.
# Pseudocode
NodeGraph :
# The nodes in their hierarchical structure, eg:
tree :
Node :
children :
- Node :
children :
- Node
- Node :
children :
- Node
- Node
- Node
# The 'all' dictionary is the same nodes represented in a flat structure.
# This can be much quicker to iterate over than the tree, and you can
# access both from within your hooks and extensions.
# The filepath is used as the unique key.
all :
content/index.md :
Node :
# node's data...
content/other-page.md :
Node :
# node's data...
Узлы могут получить доступ к своим детям, а также к своим родителям:
# More pseudocode
Node :
path : " content/index.md "
children :
- Node :
- Node :
- Node :
parent :
Node :
Чтобы сохранить порядок, объект Node не содержит непосредственного представления данных страницы — узлы — это просто контейнеры.
Следуя идеям, изложенным в этом обсуждении, у Node есть свойство Cargo , которое содержит реальный класс страницы:
from wrangler . Core import Node
class GoldBullion ( object ):
price = 1200
the_node = Node ( "index" , "content/index.md" , parent = None , cargo = None )
the_node . add_cargo ( GoldBullion ())
cargo = the_node . get_cargo ()
print cargo . price
Страницы содержат dict
представление данных исходного файла и предоставляют Renderer
единый способ доступа к данным. Чтобы создать собственную страницу, просто создайте подкласс wrangler.Core.Page
, и она загрузится автоматически.
Полезный совет: если ваш пользовательский класс имеет имя Page
, он перезапишет объект Page
по умолчанию для всех страниц.
# lib/Page.py
import wrangler . Core as wrangler
class Page ( wrangler . Page ):
def get_content ( self ):
return self . data [ "data" ]
def get_metadata ( self ):
return self . data [ "meta" ]
def get_properties ( self ):
print "Hey, I'm a custom page instance!"
return {
"title" : self . get_title (),
"alias" : self . get_short_title (),
"description" : self . get_meta_description (),
"url" : self . get_tidy_url (),
"show_in_navigation" : self . show_in_navigation (),
"weight" : self . get_weight (),
"thumbnail" : self . get_thumbnail ()
}
В нашем примере выше мы модифицируем три метода главной страницы: get_content()
, get_metadata()
и get_properties()
Вызывается при отрисовке страницы и доступен в вашем шаблоне как объект data
:
<!doctype html>
< div class = ' dump-of-data-object ' >
{{ data.content }}
</ div >
Вызывается при отрисовке страницы. Это meta
:
<!doctype html>
< title >{{ meta.title }}
Немного сложнее объяснить, но все равно здорово. Когда Node
отображается, он запрашивает определенную информацию о страницах, связанных с текущей страницей, например о дочерних, одноуровневых, родительских страницах и страницах, связанных вручную.
Вместо того, чтобы делиться всем со всем остальным , каждый класс Page
описывает основную информацию, которой он готов поделиться с другими страницами.
def get_properties ( self ):
return {
"title" : self . get_title (),
"alias" : self . get_short_title (),
"url" : self . get_tidy_url (),
"show_in_navigation" : self . show_in_navigation (),
"weight" : self . get_weight (),
# Let's add the modified time, so our theoretical parent
# page could know when we last saved the file.
"mtime" : self . getmtime ()
}
Давайте рассмотрим очень простой пример: пользовательский класс страницы, который переворачивает весь текст на странице. Очень практично.
Во-первых, установите свойство class
в мета-странице, чтобы сообщить wrangler, какой класс загружать:
контент/custom.md:
---
class : RightToLeft
---
# My custom page
With its custom content.
Затем создайте где-нибудь в каталоге lib/
новый класс, который является подклассом Page
. Неважно, где внутри вашего каталога lib/
он окажется, единственное правило состоит в том, что он должен создать подкласс объекта Page
:
lib/pages.py
import wrangler . Core as wrangler
class RightToLeft ( wrangler . Page )
def get_content ( self ):
for key , val in self . data [ "data" ]:
self . data [ "data" ][ key ] = val [:: - 1 ]
return self . data [ "data" ]
Большой! Наша страница будет напечатана с текстом, написанным справа налево.
Если вы посмотрите файл wrangler.yaml
, то заметите, что он принимает файлы трех типов: ["yaml", "json", "md"]
Wrangler по умолчанию включает в себя три синтаксических анализатора: Yaml
, Markdown
и Json
, которые обрабатывают входные файлы и представляют их как значимые данные.
Автозагрузчик ищет все, что является подклассом wrangler.Core.Parser
.
Например, вы можете сделать это где-нибудь в вашем lib/Parsers.py
для поддержки текстового формата.
from wrangler . Core import Parser
from lxml import objectify
from collections import defaultdict
class XmlParser ( Parser ):
accepts = [ "xml" , "robotlanguage" ]
def interpret ( self , file_contents ):
return root = objectify . fromstring ( file_contents )
Wrangler использует сигналы блинкера для обработки хуков и расширений.
Хуки — это сигналы, которые срабатывают в критических точках процесса рендеринга. Они обрабатываются в том порядке, в котором они появляются в ваших модулях, и могут напрямую изменять входящие объекты. У них также есть доступ к config
Wrangler, renderer
и reporter
.
from wrangler . Core import before_render , after_render , load_item , save_item , render_item
@ before_render
def before ( ** kw ):
nodes = kw [ 'nodes' ]
config = kw [ 'config' ]
renderer = kw [ 'renderer' ]
reporter = kw [ 'reporter' ]
print "Hey, I'm a hook!"
return "foo!"
@ after_render
def after ( ** kw ):
nodes = kw [ 'nodes' ]
config = kw [ 'config' ]
renderer = kw [ 'renderer' ]
reporter = kw [ 'reporter' ]
print "Hey, I'm a hook!"
return ""
Расширения — это скрипты Python, которые возвращают полезные данные в словарь extensions
ваших шаблонов.
Давайте возьмем этот небольшой скрипт:
# lib/my_extensions.py
from wrangler . Core import extension
@ extension
def my_extension ( sender , ** kwargs ):
# Add some config to your YAML file and access it here:
config = kwargs [ 'config' ][ 'extensions' ][ 'my_extension' ]
return config [ "string_to_print" ]
Будет доступен из вашего шаблона по адресу extensions.YOUR_EXTENSION_NAME
:
< em class = " extension " >
{{ extensions.my_extension }}
</ em >
Что приводит к такому результату:
< i > "This is my basic extension!" </ i >
В вашем wrangler.yaml
есть раздел для управления вашими расширениями:
# My extension just prints a string... not very exciting!
my_extension :
string_to_print : " This is my basic extension! "
Wrangler позволяет расширять Jinja2 с помощью пользовательских фильтров.
Фильтры могут находиться в любом файле в вашем каталоге lib, lib/
. Они подключаются через декоратор с метким названием template_filter
#lib/filters.py
from wrangler . Core import template_filter
from jinja2 import contextfilter
@ template_filter
def my_filter ( value ):
return value . lower ()
Если вам нужен доступ к contextfilter
jinja или envcontextfilter
вы можете импортировать их и применить к своей функции:
Узнайте больше о контекстных фильтрах jinja2.
#lib/filters.py
from wrangler . Core import template_filter
from jinja2 import contextfilter
@ template_filter
@ contextfilter
def my_filter ( context , value ):
print context
return value