Skip to content

scott-22/scotthao.com

Repository files navigation

Scott's Personal Website

My personal website, written with Common Lisp.

Framework

This website comes with a custom web framework, building on top of the open source Clack server. Similar to other web frameworks, you can define endpoint or error handlers that take a request as input and produce HTML as output. It also comes with a templating system supporting both static and dynamic (SSR) pages - the templating language is unsurprisingly Lisp itself.

Routes

Define route handlers in a file under src/controllers.

;; Ensure you are in the `controllers` package
(in-package controllers)

;; Syntactically, route definitions are similar to function definitions
(defroute "/example/path" :GET (env) ...)
(defroute 404 (env) ...)

;; Add a colon to use route parameters
(defroute "/users/:value" :ALL (env) ...)

As input, routes will receive a single argument env, a plist of request data. Note that when using route parameters, an entry with key :route-params is added to env, which is a plist of all route parameters. See the Clack docs for details on env, as well as the expected output.

Currently, when specifying the request method, exactly one of :GET, :HEAD, :OPTIONS, :PUT, :POST, or :DELETE should be used. To bind the handler to every method, use :ALL.

Templates

Why write HTML when you can write Lisp? Templates should be defined under templates/static or templates/dynamic depending on the type. Static templates are compiled exactly once, while dynamic templates are re-compiled every request. Templates are simply files containing a special Lisp function that produces HTML using the Spinneret with-html macro. See the Spinneret docs for more details.

To define a template called my-template, create a file with the same name (my-template.lisp) under the correct directory.

;; my-template.lisp
;; Ensure you are in the `templates` package
(in-package :templates)

;; You can optionally define utility macros in other files
(load "templates/utils.lisp")

;; Define a function called `my-template`
(defun my-template () ...) ; 0-arg or static template
(defun my-template (data1 data2) ...) ; dynamic template

Please ensure that all static templates have distinct names. Also, note that dynamic templates will take all their data as arguments. There should be no free variables.

You can create additional directories under templates/static or templates/dynamic. In that case, the name of the function should replace each slash separator with an underscore. That is, a template templates/static/folder/example.lisp should contain the function folder_example.

Once a template has been defined, it can be used from a route with the template function.

(in-package controllers)

;; To render a static template called `my-static`
(defroute "/static/template" :GET (env)
  `(200 (:content-type "text/html") ,(template "my-static" :static)))

;; To render a dynamic template called `my-dynamic`
(defroute "/static/template" :GET (env)
  (let* ((data1 ...)
         (data2 ...))
    `(200 (:content-type "text/html") ,(template "my-dynamic" :dynamic data1 data2))))

Development

To develop, run the Docker container while setting the target env variable to dev:

  1. TARGET=dev docker compose build site
  2. docker compose up site
  3. Use editor of choice to edit the project
  4. To access dev utilities, connect to port 4005 using emacs with SLIME (M-x slime-connect with default settings) and open a REPL

If using dev utilities, you can recompile the website from the REPL with (rl). Note that you must initiate a recompile before the tailwind CSS file is generated. See devutils.lisp for all utilities.

Deployment

To deploy, simply build and run the production image (default target if using Docker compose). This can then be deployed to the cloud, to a VPS, etc. If handling deployment manually (such as to a VPS), consider running a web server like NGINX and forwarding traffic to the container.

About

My personal website, written with Common Lisp

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published