This site used to run on Snap, a choice I made quite a few years ago at this point. I think it was a pretty good choice at the time, since it let me tinker with some Haskell but mostly stayed out of the way (if I didn’t get into the parts of Snap that require understanding Lens).
However, I was starting to sense the age of that choice a little bit. There have been two issues posted to the issue tracker in the last three years.
While I don’t update the internals of my site often, one new feature I wanted to add—HTTP basic auth—turned out to be the starting point in ripping out the entire framework and starting fresh.
The one bit of guidance I found was eight years old: HTTP Basic Auth in Snap. It very nearly worked via copy and paste, except for one tiny part:
with auth
I didn’t know what with
was, so I assumed maybe it was
some kind of Lens? It wasn’t available via a quick
Hoogle (neither was any of the other Snap stuff, since it’s not on
Stackage—only Hackage). However,
if we look
it up, (as I did after the fact) that’s what we find.
with :: SnapletLens v v' -> m b v' a -> m b v a
However, I was getting compilation errors on auth
still,
wasn’t sure where it was coming from, and didn’t really feel like
digging in to defining a SnapletLens
.
This was the tiny straw that caused me to finally switch web frameworks.
In retrospect, the way I was building my old site was kind of
horrendous. I would write everything in markdown, and render to HTML
(technically, .tpl
files), but the road in between was
pretty wild.
I’ve recovered now, but I’d like to take a look back at a bunch of the things I was doing.
Most of the interesting stuff came from the build.sh
file, a bit of bash cobbled together over many years, starting from when
I knew hardly anything about programming, and even less about shell
scripting.
CSS caching was done by putting the literal string VERSION buried somewhere in my top-level template file.
<link rel="stylesheet" href="/css/main.css?v=VERSION">
Then, I’d use sed
to replace it with a random number
generated at build time.
VERSION=$RANDOM
...
sed -i .bak "s|VERSION|${VERSION}|g" snaplets/heist/templates/$sect/$base.tpl
I tried to only recompile what was necessary using a
--recent
flag to the build script.
isrecent=()
if [[ $* == *--recent* ]]; then
isrecent+=(-mtime -1)
fi
This would later be consumed by a find
command, but
meant that my blog would suddenly turn into a single post while I was
writing it.
find $sect/*.md -type f "${isrecent[@]}" -print0 | xargs -0 ls -t | while read file
I would generate cards that linked to each blog post by reading in a custom comment format at the top of every markdown file. This would output a little line of interpolated HTML and append it to a file.
card() {
file=$1
[ -e "$file" ] || continue
name=${file##*/}
base=${name%.md}
title=$(sed -n '2p' < $file)
desc=$(sed -n '3p' < $file)
date=$(sed -n '4p' < $file)
sect2=""
if [[ $sect != "blog" && $sect != "pages" ]]; then
sect2=$sect
fi
if [[ "$base" != "404" ]]; then
if [[ "$base" = "index" ]]; then
base=""
fi
echo "<a class='card' id='$date' href='$sect2/$base'><h2>$title</h2><p>$desc</p></a>" >> snaplets/heist/templates/$sect/posts.tpl
fi
}
Luckily, I had written a bit of Vimscript to autogenerate the desired
format for me, and would call it with :Post
in vim every
time I started writing a new post. Problem solved!
for creating a new blog post
" template PostGen()
command Post call ! PostGen()
functioninputsave()
call = input('Title: ')
let title = input('Subtitle: ')
let subtitle inputrestore()
call append('.', '')
call append('.', "# " . title)
call append('.', '')
call append('.', '-->')
call append('.', strftime('%Y-%m-%d'))
call append('.', subtitle)
call append('.', title)
call append('.', "<!---")
call
execute 'normal dd Go' endfunction
One of my favorite tidbits was that I made sure the format of the card html was ordered with the date first, so it was sortable by just sorting a file.
sort -r -o snaplets/heist/templates/posts.tpl snaplets/heist/templates/posts.tpl
On the less bad side, I had more-recently (maybe a couple years ago?) switched to writing Python to create links between consecutive posts and generate an RSS feed. Clearly, I had some troubles with Python package management though.
echo "injecting links"
python3 injectlinks.py
if [[ $? -ne 0 ]] ; then
echo "link creation failed!!! maybe you need to pip install beautifulsoup4"
exit 1
fi
echo "building rss feed"
python3 rss.py
if [[ $? -ne 0 ]] ; then
echo "rss feed creation failed!"
exit 1
fi
There was evidence of having changed my mind about how to build my Haskell code several times….
# to use ghc directly
# ghc Main
# kill $(lsof -t -i:5555)
# ./Main -p 5555 &
# sleep 2
# to use cabal instead
# cabal build
# kill $(lsof -t -i:5555)
# ./dist-newstyle/build/x86_64-osx/ghc-8.10.7/site-0.1.0.0/x/site/build/site/site -p 5555 &
# sleep 2
# to use nix instead
cabal2nix . > default.nix
nix-build release.nix
kill $(lsof -t -i:5555)
./result/bin/site -p 5555 &
sleep 2
And lastly, a delightful little touch: ringing the bell when all that nonsense was complete.
afplay /System/Library/Sounds/Glass.aiff
I’ve since obviated all of that Bash by switching to Yesod (and rewriting whatever was still necessary to handwrite, but in Haskell this time).
…well, except for one piece. I can’t help from indulging in the occasional bit of character soup. Here’s my latest and greatest script for autorefreshing the post I’m working on as I write it:
file="$1"
genfile=${file#markdown}
htmlfile=generated-html${genfile%.md}.html
echo "$file 👀"
when-changed -1 -s $file "echo \".\\c\"; pandoc --mathjax $file -o $htmlfile; osascript -e 'tell application \"Google Chrome\" to reload (tabs of every window whose URL contains \"localhost:3000\")';"
I really like how this ranges from the impenetrable
\".\\c\";
to the pseudoenglish of AppleScript’s
tabs of every window whose URL contains "localhost:3000"
.
Plus it’s got emoji!