diff --git a/src/lesscode.md b/src/lesscode.md new file mode 100644 index 0000000..54c4c41 --- /dev/null +++ b/src/lesscode.md @@ -0,0 +1,214 @@ +# Writing less code + +Code is bad. It’s confusing, it’s easy to break, and it needs to be maintained or even updated. +And the more code you have, the worse it gets. + +I sometimes get bored, perhaps more often than I’d like to admit, + and one of the things I do to fight that boredom is writing code. +I’ve created lots of small pieces of software, + most of which are awful, useless, or both. +My old blog may was one of them, + although the exact classification into those categories + shall be left as an exercise to the reader. + +I realized the process of writing and uploading content to it was also anything but streamlined + and likely contributed to my lack of motivation to write and release anything, + so I decided to replace it. +At first, I thought about using [Jekyll](https://jekyllrb.com/), + but remember, I’m bored and looking for opportunities to write code + (which admittedly is the opposite of today’s title). + +So I decided to rewrite it. +Not as another Python Django application, not as a Rails project or whatever people do these days. +No, I wanted to know how little I could get away with. +I wasn’t golfing for line count, obviously (because that’s just stupid), + but I ideally wanted a simple shell script that would do everything I needed and only that. +I wanted to write markdown and get static HTML. Simple as that. +So here’s how you do that while writing as little code as possible: +```sh +$ pandoc input.md -t html > output.html +``` +And that’s the secret to all of this. + +## DRY? More like DRSE +The DRY principle (“don’t repeat yourself”) is something most programmers are familiar with + and are probably trying to adhere to. + Writing duplicate code feels inherently wrong to most people. +But why not take that one step further? +Don’t just not repeat yourself; don’t repeat someone else either. +If someone has already written software that converts markdown to html, + you don’t have to do it again. +That part might have been obvious, but we can apply it to everything that is necessary for this little project + (within reason, otherwise we wouldn’t write any code at all). + +## The components +So what does my blog need to do? +Well, quite simple: +- read markdown and convert it to HTML +- generate an index of all the blog entries +- include some basic CSS/JS in the output +- update itself automatically when I publish something +- be compatible with the content from my previous blog + +That last point might be the worst, but it’s what I wanted/needed. + +The old blog had a simple sqlite database that would hold the title, date, and link of all blog posts. +It then had a predefined template for site header and footer and would just insert the content between those. +Relatively simple, but way more than what was necessary + and also relatively slow because the template would be rendered for each request. +Oh, and I had to write the content directly in HTML. + +Static pages converted from markdown would do the job just as well, so that was my new goal. + +### Markdown conversion +The first and most obvious step is converting my hand-written markdown files to beatiful HTML for the browser. +As mentioned previously, I am going to use markdown for the conversion logic. + +All I had to do now was define a folder structure which in my case has a `src` folder with all the .md files + and a `content` folder with the resulting .html documents. +The rest is a simple loop and some shell built-ins. +```sh +convert_file() { + path="$9" + outpath="content/$(basename "$path" .md).html" + pandoc "$path" -t html > "$outpath" +} + +ls -ltu src/*.md | tail -n+1 | while read f; do convert_file $f; done +``` + +I used `ls -l` to have each file on a separate line which makes the parsing much easier. +`ls -tu` will sort the files by modification time so the newest entries are at the top. +`tail -n+1` removes the first line which is `total xxx` because of `-l`. + +Step 1 done. + +### Index generation + +This problem was partially solved in the last step because we already had a list of all output paths sorted by edit date. +All that is left now is to generate some static html from that. We thus make some changes: +```sh +output() { + echo "$1" >> index.html +} + +create_entry() { + # the code from step 1 + path="$9" + outpath="content/$(basename "$path" .md).html" + pandoc "$path" -t html > "$outpath" + output "$outpath" +} + +rm -f index.html # -f so it doesn’t fail if index.html doesn’t exist yet +ls -ltu src/*.md | tail -n+1 | while read f; do create_entry $f; done +``` +That will give us a list of links to the blog entries with the filenames as titles. +We can do better than that. +First, by extracting titles from the files. +This is based on the assumption that I begin every blog post with an h1 heading, or a single `# Heading` in markdown. +```sh +title="$(rg 'h1' "$outpath" | head -n1 | rg -o '(?<=>).*(?=<)' --pcre2)" +``` +Match the first line that contains an h1 and return whatever is inside `>` and `<` – the title. +By then making the src directory part of a git repository + (which I wanted to do anyway because it’s a good way to track changes), + we can get the creation time of each file. +```sh +created=$(git log --follow --format=%as "$path" | tail -1) +``` +`--format=%as` returns the creation date of a file as YYYY-MM-DD. +`man git-log` is your friend here. + +We can combine this with some more static HTML to turn our index into a table with all the titles, dates, and links: +```sh +html_entry() { + output '