Praise of component reuse

Fri, 04 Mar 2016 10:54:45 -0000
pdo, staticsite, sw, eng

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?