Praise of component reuse
I farm bits and pieces out to the guys who are much more brilliant than I am. I say, "build me a laser", this. "Design me a molecular analyzer", that. They do, and I just stick 'em together. (Seth Brundle, "The Fly")
When I decided to try and turn siterefactor into staticsite, I decided that I would go ahead only for as long as it could be done with minimal work, writing code in the most straightforward way on top of existing and stable components.
I am pleased by how far that went.
Python-Markdown
It works fast enough, already comes with extensions for most of what I needed, and can be extended in several ways.
One of the extension methods is a hook for manipulating the
ElementTree
of the rendered document before serializing it to HTML, which made it really
easy to go and process internal links in all <a href=
and <img src=
attributes.
To tell an internal link from an external link I just use the standard python
urlparse and see if the
link has a scheme
or a netloc
component. If it does not, and if it has a
path
, then it is an internal link.
This also means that I do not need to invent new Markdown syntax for internal
references, avoiding the need for remembering things like
[text]({{< relref "blog/post.md" >}})
or [text]({filename}/blog/post.md)
.
In staticsite
, it's just [text](/blog/post.md)
or [text](post.md)
if the
post is nearby.
This feels nicely clean to me: if I wanted to implement fancy markdown features, I could do it as Python-Markdown extensions and submit them upstream. If I wanted to implement fancy interlinking features, I could do it with a special url scheme in links.
For example, it would be straigtforward to implement a ssite:
url scheme that
expanded the url with elements from staticsite
's settings using a call to
python's string.format
(ssite:{SETTING_NAME}/bar
maybe?), except I do not
currently see any use cases for extending internal linking from what it is now.
Jinja2
Jina2 is a template engine that I already knew, it is widely used, powerful and pleasant to use, both on the templating side and on the API's side.
It is not HTML specific, so I can also use it to generate Atom, RSS2, "dynamic" site content, and even new site Markdown pages.
Implementing RSS and Atom feeds was just a matter of writing and testing these Jinja2 macros and then reusing them anywhere.
toml, yaml, json
No need to implement my own front matter parsing. Also, reusing the same syntax as Hugo allows me to just link to its documentation.
python-slugify
I found python-slugify so I did not bother writing a slug-generating function.
As a side effect, now things works better than I would even have thought to implement, including transliteration of non-ascii characters:
$ ./ssite new example --noedit --title "Cosí parlò Enrico"
/enrico-dev/staticsite/example/site/blog/2016/cosi-parlo-enrico.md
(I just filed an RFP)
python-livereload
Implementing ssite serve
which monitors the file system and autoreloads when
content changes and renders everything on the fly, took about an hour. Most of
that hour went into implementing rendering pages on demand.
Then I discovered that it autoreloads even when I edit staticsite
's source
code.
Then I discovered that it communicates with the browser and even automatically triggers a page refresh.
I can keep vim
on half my screen and a browser in the other half, and I get
live preview for free every time I save, without ever leaving the editor.
Bootstrap
I already use Bootstrap at work, so creating the default theme templates with it took about 10 minutes.
This morning I tried looking at my website using my mobile phone, and I pleasantly saw it automatically turning into a working mobile version of itself.
Pygments
Python-Markdown uses Pygments for syntax highlighting,
and it can be themed just by loading a .css
.
So, without me really doing anything, even staticsite
's syntax highligthing
is themable, and there's even a nice page with a list of themes to choose
from.
Everything else...
Command line parsing? Straight argparse.
Logging? python's logging support.
Copying static resource files? shutil.copy2.
Parsing dates? dateutil.parser.
Timing execution? time.perf_counter.
Timezone handling? pytz.
Building the command to run an editor? string.format.
Matching site pages? fnmatch.translate.
...and then some.
If I ever decide to implement incremental rendering, how do I implement tracking which source files have changed?
Well, for example, how about just asking git?