Wrangler é um gerador de sites estáticos para pessoas que não criam blogs.
Características:
No Springload, muitas vezes precisamos criar sites estáticos, mas temos dificuldade em encontrar uma ferramenta que, bem... nos permita continuar com isso. Entre no Wrangler. Não espere que seu conteúdo seja formatado como uma série de postagens de blog. Ele não copia ativos estáticos, nem processa SaSS, nem faz café.
Ele faz uma coisa, e faz essa coisa muito bem.
Esperamos que você goste.
Instale o wrangler via pip:
pip install wrangler
Gere um novo projeto no diretório atual .
wrangler create .
Isso criará vários diretórios. Para verificar se funciona, crie o pequeno site gerado automaticamente:
wrangler build content www
Sirva seu site por meio de um mecanismo de sua escolha ou do prático servidor integrado em http://127.0.0.1:8000/
:
wrangler serve www
Quer observar se há alterações em seu conteúdo e modelos e reconstruí-los automaticamente? Existe um aplicativo para isso. A tarefa watch usa todas as mesmas opções de build
.
wrangler watch content www
O Wrangler segue uma convenção de sistema de arquivos bastante simples:
my-site/
| --content
| ----index.yaml
| --lib
| --templates
| ----template.j2
| --var
| --wrangler.yaml
| --www
content
- o diretório de conteúdo. A estrutura do seu site irá espelhar este diretório. Se você tiver content/index.yaml
, obterá www/index.html
templates
- onde ficam seus templates jinja2 .j2
www
- a pasta raiz da web. Na verdade, você pode enviar para qualquer lugar, apenas escolhemos www como um padrão sensato.lib
- extensões personalizadas, classes, ganchos, etc. entram aqui. Escreva alguns módulos python e passe os resultados de volta ao wranglervar
- contém o cache do modelo e o cache dos objetos do arquivo (o wrangler usa o combo Pickle and Shelve pronto para uso) Todos esses caminhos podem ser alterados em wrangler.yaml
O Wrangler assume que você está trabalhando com algum tipo de dados estruturados e deseja traduzir esses arquivos de dados para HTML (ou até mesmo ASP clássico, se preferir).
Três analisadores estão incluídos no pacote: json, yaml e markdown (com yaml front-matter). Eles operam por arquivo, então isso é perfeitamente válido:
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
Aqui está uma bela folha de dicas de markdown
Use os metadados para qualquer coisa relacionada à página. Você pode colocar o que quiser aqui, mas há algumas palavras reservadas:
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
O nome da página na navegação (e provavelmente sua tag de título também)
Caminho do modelo, relativo ao templates_dir
do seu wrangler.yaml
Abreviação do título, que pode ser usado na navegação
Tenta substituir a classe Python pelo objeto de página. Deve ser uma subclasse de wrangler.Core.Page
Oculte esta página da árvore de navegação.
O que diz na lata
Uma lista de palavras-chave
Substitua o output_file_extension
padrão de wrangler.yaml. A página será renderizada com esta extensão.
Útil para ordenar páginas, de baixo para cima. Por padrão, o wrangler usará a classificação alfabética do sistema de arquivos.
Caminho para uma imagem em miniatura
Uma lista de páginas relacionadas. No seu modelo, isso permitirá que você obtenha algumas informações básicas sobre outras páginas (como título e descrição).
O wrangler adiciona algumas coisas aos seus metadados automaticamente, nos seus templates você pode acessar:
{{ meta.url }}
{{ meta.segments }}
{{ meta.filepath }}
{{ meta.mtime }}
{{ meta.children }}
{{ meta.parents }}
{{ meta.parents_siblings }}
O caminho para o arquivo compilado, relativo ao output_dir
, por exemplo /
Uma lista de todos os segmentos de URL: ["sub-directory", "index.html"]
O nome do arquivo de entrada
A hora modificada. Você pode usar isso para criar um carimbo de data/hora do blog, por exemplo.
Quaisquer filhos diretos do diretório atual
Todos os nós entre o arquivo atual e /
Os irmãos do diretório pai.
Os dados da página são totalmente opcionais.
Vejamos esta pequena página, custom-page.yaml
Você pode lançar uma página sem dados no wrangler e codificar tudo no modelo, custom-page.j2
se desejar.
meta :
title : No content!
template : custom-page.j2
output_file_extension : txt
Isso criará www/custom-page.txt
.
O Wrangler vem com um filtro de redução jinja2. Se você estiver usando um arquivo markdown, o markdown estará disponível em data.content
. Canalize o conteúdo para o filtro de redução e pronto.
< div class = " content " >
{{ data.content|markdown }}
</ div >
Markdown é um formato de escrita fantástico, mas pode apresentar algumas limitações quando você lida com dados mais estruturados. Para arquivos YAML e JSON, acesse partes do dicionário data
e conecte-as conforme achar necessário:
< div class = " content " >
{{ data.content }}
{% for block in data . blocks %}
< p >{{ block }}</ p >
{% endfor %}
</ div >
As opções editáveis para o wrangler são salvas no arquivo wrangler.yaml
na raiz do seu projeto.
Abra-o e você encontrará três nós: wrangler
, site
e extensions
Esta é a configuração principal, o material principal. Parece algo assim:
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....
Configure todas as extensões que você configurou aqui. As extensões permitem que você execute qualquer função python desejada e injete os resultados em seus modelos.
# 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 }}
Algumas extensões padrão estão incluídas: sitemap
, fileinfo
e cachebuster
Site vars são variáveis de todo o site, disponíveis em seus modelos como filhos do objeto site
.
Por exemplo, para obter o caminho das imagens, você pode chamar {{ site.paths.images }}
e economizar digitação.
# 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 }}
Toda essa documentação também está no arquivo wrangler.yaml
, então você não se perderá!
Utiliza um único argumento posicional, o caminho para criar seu novo projeto:
cd my-sweet-site && wrangler create .
# or
wrangler create my-sweet-site
input_dir
Diretório de entrada como site/content
output_dir
Diretório de saída como www
wrangler build content www
Force a renderização independentemente de quando seu conteúdo foi modificado pela última vez:
wrangler build content www --force
Armazene novamente em cache todos os objetos da página
wrangler build content www --nocache
Altere a extensão do arquivo de saída de todas as páginas para asp
clássico. (Por que alguém faria isso?)
wrangler build content www -o ".asp"
Altere o formato dos dados a serem pesquisados em input_dir
para json
wrangler build content www -d 'json'
Altere a localização do diretório de modelos
wrangler build content www -t other-templates-dir
Altere a localização do arquivo de configuração
wrangler build content www -c site/config/wrangler.yaml
Tem todas as mesmas opções do wrangler build
Imprima todo o encanamento sempre que um arquivo for alterado:
wrangler watch content www --verbose
Aceita um argumento posicional (o diretório a ser servido) e um --port
opcional (padrão 8000).
wrangler serve www --port 8001
Remova o cache de modelo e o cache de objeto do diretório ‘var’.
wrangler clean
O Wrangler carrega todos os módulos python encontrados no diretório lib
do seu projeto quando é inicializado.
Isso lhe dá o poder de estender as funções principais e manipular os dados da página - por exemplo, você pode carregar alguns valores de um banco de dados e disponibilizá-los em seus modelos.
Quando você chama build
, o wrangler constrói uma representação da estrutura em árvore em seu diretório content/
.
Ele está usando uma lista duplamente vinculada de objetos Node
, que são mesclados em um NodeGraph
, um contêiner útil para lidar com os nós.
# 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...
Os nós podem acessar seus filhos e também seus pais:
# More pseudocode
Node :
path : " content/index.md "
children :
- Node :
- Node :
- Node :
parent :
Node :
Para manter as coisas organizadas, o objeto Node não contém uma representação dos dados da página diretamente nele – os nós são apenas contêineres.
Seguindo as ideias desta discussão, o Node possui uma propriedade cargo que contém a classe de página real :
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
As páginas contêm uma representação dict
dos dados do seu arquivo de origem e fornecem uma maneira consistente para o Renderer
acessar os dados. Para criar uma página personalizada, basta criar uma subclasse wrangler.Core.Page
e ela será carregada automaticamente.
Dica útil: se sua classe personalizada tiver o nome Page
, ela substituirá o objeto Page
padrão para todas as páginas.
# 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 ()
}
Em nosso exemplo acima, estamos modificando os três métodos da página principal, get_content()
, get_metadata()
e get_properties()
Chamado quando a página é renderizada, está disponível em seu modelo como o objeto data
:
<!doctype html>
< div class = ' dump-of-data-object ' >
{{ data.content }}
</ div >
Chamado quando a página é renderizada, este é o meta
:
<!doctype html>
< title >{{ meta.title }}
Um pouco mais complicado de explicar, mas ainda assim incrível. Quando um Node
é renderizado, ele solicita certas informações sobre páginas relacionadas à página atual, como filhos, irmãos, pais e páginas relacionadas manualmente.
Em vez de compartilhar tudo com todo o resto , cada classe Page
descreve as informações básicas que ela deseja compartilhar com outras páginas.
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 ()
}
Vejamos um exemplo realmente simples, uma classe de página personalizada que inverte todo o texto da página. Muito prático.
Em primeiro lugar, defina a propriedade class
na meta da sua página para informar ao wrangler qual classe carregar:
conteúdo/custom.md:
---
class : RightToLeft
---
# My custom page
With its custom content.
Em seguida, crie uma nova classe em algum lugar do seu diretório lib/
que subclasse Page
. Não importa onde ele esteja no diretório lib/
, a única regra é que ele deve criar uma subclasse do objeto 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" ]
Ótimo! Nossa página será impressa com texto da direita para a esquerda.
Se você olhar em seu arquivo wrangler.yaml
, notará que ele aceita três tipos de arquivo: ["yaml", "json", "md"]
O Wrangler inclui três analisadores por padrão, Yaml
, Markdown
e Json
, que consomem os arquivos de entrada e os representam como dados significativos.
O carregador automático procura qualquer coisa que subclasse wrangler.Core.Parser
.
Por exemplo, você poderia fazer isso em algum lugar do seu lib/Parsers.py
para suportar o formato de texto
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 )
O Wrangler usa sinais de pisca-pisca para processar ganchos e extensões.
Ganchos são sinais disparados em pontos críticos do processo de renderização. Eles são processados na ordem em que aparecem nos módulos e podem modificar diretamente os objetos recebidos. Eles também têm acesso ao config
, renderer
e reporter
do wrangler.
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 ""
Extensões são scripts python que retornam dados úteis para o dicionário de extensions
dos seus modelos.
Vamos pegar este pequeno script:
# 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" ]
Estará acessível a partir do seu modelo em extensions.YOUR_EXTENSION_NAME
:
< em class = " extension " >
{{ extensions.my_extension }}
</ em >
O que resulta nesta saída:
< i > "This is my basic extension!" </ i >
No seu wrangler.yaml
há uma seção para gerenciar suas extensões:
# My extension just prints a string... not very exciting!
my_extension :
string_to_print : " This is my basic extension! "
O Wrangler permite estender o Jinja2 com filtros personalizados.
Os filtros podem estar em qualquer arquivo em seu diretório lib, lib/
. Eles são conectados por meio de um decorador, apropriadamente chamado template_filter
#lib/filters.py
from wrangler . Core import template_filter
from jinja2 import contextfilter
@ template_filter
def my_filter ( value ):
return value . lower ()
Se você precisar de acesso ao contextfilter
ou envcontextfilter
do jinja, você pode importá-los e aplicá-los à sua função também:
Leia mais sobre os filtros de contexto do 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