docs: initial scaffold implementation plan
This commit is contained in:
@@ -0,0 +1,250 @@
|
|||||||
|
# Spine Initial Scaffold — Implementation Plan
|
||||||
|
|
||||||
|
**Goal:** A minimal Emacs web server that serves a templated "Hello from Emacs." page, exercising the `simple-httpd` + `mustache.el` stack end-to-end.
|
||||||
|
|
||||||
|
**Architecture:** Single `spine.el` entry point bootstraps packages, starts `simple-httpd` on port 8080, and registers a `/hello` servlet. Templates are `.mustache` files read from a `templates/` directory relative to `spine.el`.
|
||||||
|
|
||||||
|
**Tech Stack:** Emacs Lisp, `simple-httpd` (MELPA), `mustache` (MELPA), `ht` (transitive).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File structure
|
||||||
|
|
||||||
|
| File | Responsibility |
|
||||||
|
|------|---------------|
|
||||||
|
| `templates/hello.mustache` | Smoke-test HTML template with `{{title}}` and `{{message}}` placeholders |
|
||||||
|
| `spine.el` | Entry point: package bootstrap, server config, template renderer, `/hello` handler |
|
||||||
|
|
||||||
|
`spine.el` is the sole Elisp file. No project file, no Makefile — invocation is `emacs --quick --load spine.el`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Create the hello.mustache template
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `templates/hello.mustache`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Create templates/ directory and hello.mustache**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p templates
|
||||||
|
```
|
||||||
|
|
||||||
|
Write `templates/hello.mustache`:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>{{title}}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>{{message}}</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify template file exists**
|
||||||
|
|
||||||
|
Run: `cat templates/hello.mustache`
|
||||||
|
Expected: outputs the template content above.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add templates/hello.mustache
|
||||||
|
git commit -m "feat: add hello.mustache smoke-test template"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: Write spine.el entry point
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `spine.el`
|
||||||
|
|
||||||
|
- [ ] **Step 1: Write the full spine.el**
|
||||||
|
|
||||||
|
Write `spine.el`:
|
||||||
|
|
||||||
|
```elisp
|
||||||
|
;;; spine.el — personal reading tracker, served from Emacs
|
||||||
|
|
||||||
|
;; --- package bootstrap ------------------------------------------------
|
||||||
|
|
||||||
|
(require 'package)
|
||||||
|
(add-to-list 'package-archives
|
||||||
|
'("melpa" . "https://melpa.org/packages/")
|
||||||
|
t)
|
||||||
|
(package-initialize)
|
||||||
|
|
||||||
|
(dolist (pkg '(simple-httpd mustache))
|
||||||
|
(unless (package-installed-p pkg)
|
||||||
|
(package-refresh-contents)
|
||||||
|
(package-install pkg)))
|
||||||
|
|
||||||
|
(require 'simple-httpd)
|
||||||
|
(require 'mustache)
|
||||||
|
|
||||||
|
;; --- configuration ----------------------------------------------------
|
||||||
|
|
||||||
|
(defvar spine-port 8080
|
||||||
|
"Port for the Spine HTTP server.")
|
||||||
|
|
||||||
|
(defvar spine-template-dir
|
||||||
|
(expand-file-name "templates/"
|
||||||
|
(file-name-directory load-file-name))
|
||||||
|
"Directory containing .mustache templates.")
|
||||||
|
|
||||||
|
;; --- template rendering -----------------------------------------------
|
||||||
|
|
||||||
|
(defun spine-render (name context)
|
||||||
|
"Render templates/NAME.mustache with CONTEXT (an ht hash table).
|
||||||
|
Returns the rendered string."
|
||||||
|
(mustache-render
|
||||||
|
(with-temp-buffer
|
||||||
|
(insert-file-contents
|
||||||
|
(expand-file-name name spine-template-dir))
|
||||||
|
(buffer-string))
|
||||||
|
context))
|
||||||
|
|
||||||
|
;; --- handlers ---------------------------------------------------------
|
||||||
|
|
||||||
|
(defservlet hello text/html ()
|
||||||
|
(insert (spine-render "hello.mustache"
|
||||||
|
(ht ("title" "Spine")
|
||||||
|
("message" "Hello from Emacs.")))))
|
||||||
|
|
||||||
|
;; --- start ------------------------------------------------------------
|
||||||
|
|
||||||
|
(setq httpd-port spine-port)
|
||||||
|
(httpd-start)
|
||||||
|
(message "Spine listening on http://localhost:%d" spine-port)
|
||||||
|
|
||||||
|
(provide 'spine)
|
||||||
|
;;; spine.el ends here
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify the file has no syntax errors by loading it with Emacs in batch mode**
|
||||||
|
|
||||||
|
Run: `emacs --quick --batch --load spine.el`
|
||||||
|
Expected: no errors printed to stderr. The server starts briefly then Emacs exits (batch mode runs the file then quits). You should see something like:
|
||||||
|
|
||||||
|
```
|
||||||
|
Spine listening on http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
If a package is missing, you'll see MELPA refresh output followed by install messages — that's fine, it means the bootstrap worked.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add spine.el
|
||||||
|
git commit -m "feat: add spine.el — package bootstrap, server, hello handler"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: Smoke test — first launch (package install)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- No changes — verification only.
|
||||||
|
|
||||||
|
**Prerequisite:** Delete any previously installed `simple-httpd` and `mustache` packages so we test the bootstrap path. Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -rf ~/.emacs.d/elpa/simple-httpd-* ~/.emacs.d/elpa/mustache-* ~/.emacs.d/elpa/ht-*
|
||||||
|
```
|
||||||
|
|
||||||
|
If you're using a custom `package-user-dir`, adjust the paths accordingly. If you don't want to nuke your real packages, set `package-user-dir` to a temp directory for this test:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p /tmp/spine-elpa
|
||||||
|
emacs --quick --eval "(setq package-user-dir \"/tmp/spine-elpa\")" --load spine.el
|
||||||
|
```
|
||||||
|
|
||||||
|
(Use whichever approach fits your setup — the key is testing the fresh-install path.)
|
||||||
|
|
||||||
|
- [ ] **Step 1: Start Emacs with spine.el (fresh package state)**
|
||||||
|
|
||||||
|
Run (in one terminal, Emacs stays running):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
emacs --quick --load spine.el
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output in the `*Messages*` buffer:
|
||||||
|
- MELPA archive refresh output (first-time only)
|
||||||
|
- Package install messages for `simple-httpd`, `mustache`, `ht`
|
||||||
|
- `Spine listening on http://localhost:8080`
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify /hello endpoint**
|
||||||
|
|
||||||
|
In another terminal:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s http://localhost:8080/hello
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: HTML output containing `<h1>Hello from Emacs.</h1>` and `<title>Spine</title>`.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Stop Emacs**
|
||||||
|
|
||||||
|
In the Emacs terminal, press `C-x C-c` to quit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: Smoke test — second launch (no package install)
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- No changes — verification only.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Start Emacs again (packages already installed)**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
emacs --quick --load spine.el
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: no MELPA refresh output, no package install messages. Only `Spine listening on http://localhost:8080`.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Verify /hello still works**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -s http://localhost:8080/hello
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: same HTML as before.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Stop Emacs** (`C-x C-c`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 5: Final commit
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- No code changes — just confirming all commits are done.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Verify git status is clean**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: `nothing to commit, working tree clean`
|
||||||
|
|
||||||
|
If anything shows as modified, stage and commit it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
git commit -m "chore: finalize initial scaffold"
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Review commit log**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git log --oneline
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: 2–4 commits covering the template, spine.el, and any fixes.
|
||||||
Reference in New Issue
Block a user