coleslaw/src/posts.lisp

73 lines
3.1 KiB
Common Lisp
Raw Normal View History

2011-04-16 15:45:37 -04:00
(in-package :coleslaw)
(defparameter *posts* (make-hash-table :test #'equal)
"A hash table to store all the posts and their metadata.")
2011-04-16 15:45:37 -04:00
(defclass post ()
((slug :initform nil :initarg :slug :accessor post-slug)
(title :initform nil :initarg :title :accessor post-title)
(tags :initform nil :initarg :tags :accessor post-tags)
(date :initform nil :initarg :date :accessor post-date)
(format :initform nil :initarg :format :accessor post-format)
2012-08-20 20:36:57 -04:00
(content :initform nil :initarg :content :accessor post-content)))
2011-04-16 15:45:37 -04:00
(defun render-posts ()
"Iterate through the files in the repo to render+write the posts out to disk."
(do-files (file (repo *config*) "post")
(with-open-file (in file)
(let ((post (read-post in)))
(setf (gethash (post-slug post) *posts*) post))))
(maphash #'write-post *posts*))
2011-04-16 15:45:37 -04:00
2012-08-20 19:43:03 -04:00
(defgeneric render-content (text format)
(:documentation "Compile TEXT from the given FORMAT to HTML for display.")
(:method (text (format (eql :html)))
text))
2012-08-20 20:36:57 -04:00
(defun read-post (in)
"Make a POST instance based on the data from the stream IN."
(flet ((check-header ()
(unless (string= (read-line in) ";;;;;")
(error "The provided file lacks the expected header.")))
(parse-field (str)
(nth-value 1 (cl-ppcre:scan-to-strings "[a-zA-Z]+: (.*)" str)))
(slurp-remainder ()
(read-sequence (make-string (- (file-length in)
(file-position in))
:element-type 'character) in)))
(check-header)
(let ((args (loop for field in '("title" "tags" "date" "format")
for line = (read-line in nil)
when (not (search field line :test #'string=))
do (error "The provided file lacks the expected header.")
appending (list (intern (string-upcase field) :keyword)
(aref (parse-field (read-line in)) 0)))))
(check-header)
(apply 'make-instance 'blog
(append args (list :content (slurp-remainder)
:slug (slugify (getf args :title))))))))
2011-04-16 15:45:37 -04:00
(defun write-post (slug post)
"Write out the HTML for POST in SLUG.html."
2012-08-20 11:53:39 -04:00
(render-page (format nil "posts/~a.html" slug)
(funcall (theme-fn "POST")
(list :title (post-title post)
:tags (post-tags post)
:date (post-date post)
2012-08-20 19:43:03 -04:00
:content (render-content (post-content post)
(post-format post))
2012-08-20 11:53:39 -04:00
; TODO: Populate prev and next with links.
:prev nil
:next nil))))
2012-08-20 16:21:12 -04:00
(defun slug-char-p (char)
"Determine if CHAR is a valid slug (i.e. URL) character."
2012-08-20 16:21:12 -04:00
(or (char<= #\0 char #\9)
(char<= #\a char #\z)
(char<= #\A char #\Z)
(member char '(#\_ #\-))))
(defun slugify (string)
"Return a version of STRING suitable for use as a URL."
2012-08-20 16:21:12 -04:00
(remove-if-not #'slug-char-p (substitute #\- #\Space string)))