My plan with this site was to see how little I could get away with. Call it an exercise in developer minimalism.
Libraries
Sinatra handles the incoming web requests with simple handlers:
get "/*" do
slug = params[:splat].first
cache slug do
post = PostStore.find(slug)
return not_found unless post
PostView.new(post: post).call
end
end
PostStore
is a simple class with a handful of methods that use pg directly. I'm using connection_pool although I suspect I could get away without it.
Views are built and rendered using Phlex because it's the Ruby view library we've all been waiting for 😄
class PostView < Phlex::HTML
include ViewHelpers
def initialize(post:)
@post = post
end
def template
render Layout.new(title: @post["title"]) do
div(class: "body") do
body
end
end
end
def body
markdown @post["body"]
end
end
The output from each URL is cached so most requests don't even hit the database.
The views render markdown with Red Carpet and rouge handles the syntax highlighting.
For development, I'm using rerun with rackup for the dev server and rspec with capybara for testing.
Server
I have a VPS from Ionos running nginx and passenger.
SSL certs are from Let’s Encrypt.
The setup and maintenance is manual which is a breath of fresh air after years of k8s
and docker
. Manually editing nginx configs via ssh gives me a strange sense of nostalgia.
Deploying
Deployment is a 2 line script using rsync:
rsync -av --exclude={'.env','.git','cache'} . user@server:/path/to/site/
ssh user@server -T "rm /path/to/site/cache/*.html && touch /path/to/site/tmp/restart.txt"
The app deploys in seconds.
Stripping everything back to basics has had some fun results:
Questioning everything has lead to simplifications I didn't expect. Normally I would naturally start to architect - with b;r I don't even really have a folder structure.
This has meant that the parts of the app are very clear at the top level. There are no controllers
or models
or actions
- it's clear - post_view.rb
has the... erm... "post view".
I can also postpone certain things as I’m the only one using it. Lack of certain validations will frustrate me eventually - when they do, I’ll find the smallest change to add them.
Deploying is a dream - I picked rsync
without really thinking about it, but the first time I deployed it genuinely surprised me. It's obvious of course - it's just copying files - but still 😍
I doubt this approach scales but that wasn't really the point.
Inspiration
Listening to Derek Sivers on Remote Ruby #216 sparked this all off.