2014-04-15 11:28:41 -04:00
|
|
|
(in-package :coleslaw)
|
|
|
|
|
|
|
|
;;;; The Document Protocol
|
|
|
|
|
|
|
|
;; Data Storage
|
|
|
|
|
|
|
|
(defvar *site* (make-hash-table :test #'equal)
|
2014-05-01 17:28:51 -04:00
|
|
|
"An in-memory database to hold all site documents, keyed on relative URLs.")
|
2014-04-15 11:28:41 -04:00
|
|
|
|
|
|
|
;; Class Methods
|
|
|
|
|
|
|
|
(defgeneric publish (doc-type)
|
|
|
|
(:documentation "Write pages to disk for all documents of the given DOC-TYPE."))
|
|
|
|
|
|
|
|
(defgeneric discover (doc-type)
|
|
|
|
(:documentation "Load all documents of the given DOC-TYPE into memory.")
|
|
|
|
(:method (doc-type)
|
2014-05-06 15:19:43 -04:00
|
|
|
(let ((file-type (format nil "~(~A~)" (class-name doc-type))))
|
2014-11-02 23:04:45 -05:00
|
|
|
(do-files (file (repo-dir *config*) file-type)
|
2014-05-06 15:19:43 -04:00
|
|
|
(let ((obj (construct (class-name doc-type) (read-content file))))
|
2014-04-15 15:27:46 -04:00
|
|
|
(add-document obj))))))
|
2014-04-15 11:28:41 -04:00
|
|
|
|
2014-04-15 20:43:56 -04:00
|
|
|
(defmethod discover :before (doc-type)
|
|
|
|
(purge-all (class-name doc-type)))
|
|
|
|
|
2014-04-15 11:28:41 -04:00
|
|
|
;; Instance Methods
|
|
|
|
|
|
|
|
(defgeneric page-url (document)
|
2014-08-27 11:51:12 -04:00
|
|
|
(:documentation "The relative URL to the DOCUMENT."))
|
|
|
|
|
2014-08-27 14:08:48 -04:00
|
|
|
(defgeneric render (document &key &allow-other-keys)
|
|
|
|
(:documentation "Render the given DOCUMENT to HTML."))
|
|
|
|
|
|
|
|
;; Helper Functions
|
|
|
|
|
|
|
|
(defun compute-url (document unique-id &optional class)
|
|
|
|
"Compute the relative URL for a DOCUMENT based on its UNIQUE-ID. If CLASS
|
|
|
|
is provided, it overrides the route used."
|
|
|
|
(let* ((class-name (or class (class-name (class-of document))))
|
2014-08-27 11:51:12 -04:00
|
|
|
(route (get-route class-name)))
|
|
|
|
(unless route
|
|
|
|
(error "No routing method found for: ~A" class-name))
|
|
|
|
(let* ((result (format nil route unique-id))
|
|
|
|
(type (or (pathname-type result) (page-ext *config*))))
|
|
|
|
(make-pathname :type type :defaults result))))
|
2014-04-15 11:28:41 -04:00
|
|
|
|
2014-08-27 14:08:48 -04:00
|
|
|
(defun get-route (doc-type)
|
|
|
|
"Return the route format string for DOC-TYPE."
|
|
|
|
(second (assoc (make-keyword doc-type) (routing *config*))))
|
2014-04-28 14:10:27 -04:00
|
|
|
|
2014-05-01 17:28:51 -04:00
|
|
|
(defun add-document (document)
|
|
|
|
"Add DOCUMENT to the in-memory database. Error if a matching entry is present."
|
|
|
|
(let ((url (page-url document)))
|
2014-04-28 14:10:27 -04:00
|
|
|
(if (gethash url *site*)
|
|
|
|
(error "There is already an existing document with the url ~a" url)
|
2014-05-01 17:28:51 -04:00
|
|
|
(setf (gethash url *site*) document))))
|
2014-04-28 14:10:27 -04:00
|
|
|
|
2014-06-03 16:32:43 -04:00
|
|
|
(defun delete-document (document)
|
|
|
|
"Given a DOCUMENT, delete it from the in-memory database."
|
|
|
|
(remhash (page-url document) *site*))
|
|
|
|
|
2014-04-28 13:55:55 -04:00
|
|
|
(defun write-document (document &optional theme-fn &rest render-args)
|
|
|
|
"Write the given DOCUMENT to disk as HTML. If THEME-FN is present,
|
|
|
|
use it as the template passing any RENDER-ARGS."
|
2014-04-28 14:09:27 -04:00
|
|
|
(let ((html (if (or theme-fn render-args)
|
2014-04-28 13:55:55 -04:00
|
|
|
(apply #'render-page document theme-fn render-args)
|
2014-05-01 17:28:51 -04:00
|
|
|
(render-page document nil)))
|
|
|
|
(url (namestring (page-url document))))
|
|
|
|
(write-file (rel-path (staging-dir *config*) url) html)))
|
2014-04-28 14:10:27 -04:00
|
|
|
|
|
|
|
(defun find-all (doc-type)
|
|
|
|
"Return a list of all instances of a given DOC-TYPE."
|
|
|
|
(loop for val being the hash-values in *site*
|
|
|
|
when (typep val doc-type) collect val))
|
|
|
|
|
|
|
|
(defun purge-all (doc-type)
|
|
|
|
"Remove all instances of DOC-TYPE from memory."
|
|
|
|
(dolist (obj (find-all doc-type))
|
|
|
|
(remhash (page-url obj) *site*)))
|