Flexible Lisp Blogware. Fork for personal use. Mirrored from https://github.com/kingcons/coleslaw originally.

documents.lisp 2.8KB

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