Recently Written · git

coleslaw

Emacs mode for the "Coleslaw" site generator written in Common Lisp.

git clone https://github.com/equwal/coleslaw

Log | Files | Refs


src/documents.lisp (3214 bytes)

1 (in-package :coleslaw)
2 
3 ;;;; The Document Protocol
4 
5 ;; Data Storage
6 
7 (defvar *site* (make-hash-table :test #'equal)
8   "An in-memory database to hold all site documents, keyed on relative URLs.")
9 
10 ;; Class Methods
11 
12 (defgeneric publish (doc-type)
13   (:documentation "Write pages to disk for all documents of the given DOC-TYPE."))
14 
15 (defgeneric discover (doc-type)
16   (:documentation "Load all documents of the given DOC-TYPE into memory.")
17   (:method (doc-type)
18     (let ((file-type (format nil "~(~A~)" (class-name doc-type))))
19       (do-files (file (repo-dir *config*) file-type)
20         (let ((obj (construct (class-name doc-type) (read-content file))))
21           (add-document obj))))))
22 
23 (defmethod discover :before (doc-type)
24   (purge-all (class-name doc-type)))
25 
26 ;; Instance Methods
27 
28 (defgeneric page-url (document)
29   (:documentation "The relative URL to the DOCUMENT."))
30 
31 (defgeneric render (document &key &allow-other-keys)
32   (:documentation "Render the given DOCUMENT to HTML."))
33 
34 ;; Helper Functions
35 
36 (defun compute-url (document unique-id &optional class)
37   "Compute the relative URL for a DOCUMENT based on its UNIQUE-ID. If CLASS
38 is provided, it overrides the route used."
39   (let* ((class-name (or class (class-name (class-of document))))
40          (route (get-route class-name)))
41     (unless route
42       (error "No routing method found for: ~A" class-name))
43     (let* ((result (format nil route unique-id))
44            (type (or (pathname-type result) (page-ext *config*))))
45       (make-pathname :name (funcall (name-fn *config*) (pathname-name result))
46                      :type type
47                      :defaults result))))
48 
49 (defun get-route (doc-type)
50   "Return the route format string for DOC-TYPE."
51   (second (assoc (make-keyword doc-type) (routing *config*))))
52 
53 (defun add-document (document)
54   "Add DOCUMENT to the in-memory database. Error if a matching entry is present."
55   (let ((url (page-url document)))
56     (if (gethash url *site*)
57         (error "There is already an existing document with the url ~a" url)
58         (setf (gethash url *site*) document))))
59 
60 (defun delete-document (document)
61   "Given a DOCUMENT, delete it from the in-memory database."
62   (remhash (page-url document) *site*))
63 
64 (defun write-document (document &optional theme-fn &rest render-args)
65   "Write the given DOCUMENT to disk as HTML. If THEME-FN is present,
66 use it as the template passing any RENDER-ARGS."
67   (let ((html (if (or theme-fn render-args)
68                   (apply #'render-page document theme-fn render-args)
69                   (render-page document nil)))
70         (url (namestring (page-url document))))
71     (write-file (rel-path (staging-dir *config*) url) html)))
72 
73 (defun find-all (doc-type &optional (matches-p (lambda (x) (typep x doc-type))))
74   "Return a list of all instances of a given DOC-TYPE."
75   (loop for val being the hash-values in *site*
76      when (funcall matches-p val) collect val))
77 
78 (defun purge-all (doc-type)
79   "Remove all instances of DOC-TYPE from memory."
80   (flet ((matches-class-name-p (x)
81            (class-name-p (symbol-name doc-type)
82                          (class-of x))))
83     (dolist (obj (find-all doc-type #'matches-class-name-p))
84       (remhash (page-url obj) *site*))))