summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Okay <okay@toyful.space>2021-05-13 21:50:52 -0500
committerColin Okay <okay@toyful.space>2021-05-13 21:50:52 -0500
commitf4a607f7efc457f7afe299b721487f348bbf7ace (patch)
tree19413a475c89c1eadf626e4f6682539301982067
parent747acf957ef1542cee64298148ed2a4960408390 (diff)
filling out
-rw-r--r--flexo.lisp147
1 files changed, 129 insertions, 18 deletions
diff --git a/flexo.lisp b/flexo.lisp
index e49bbe2..64a2203 100644
--- a/flexo.lisp
+++ b/flexo.lisp
@@ -2,27 +2,138 @@
(in-package #:flexo)
-;;; CONTENT CLASSES
+;;; CONTENT
-(defclass content () ())
+(defvar *content* nil
+ "Dynamic hash-table, bound before building a site. All instances of
+ subclasses of content are automatically inserted into this index.")
-(defclass local-file-content (content)
+(defclass content ()
+ ((keywords
+ :reader content-keywords
+ :initarg :keywords
+ :initform nil
+ :documentation "A list of content keywords that may be used to
+ look up this piece of content."))
+ (:documentation
+ "The base class for all raw content. CONTENT instances represent
+ unprocessed, unadorned files, database queries, or any source of
+ content data that FLEXO can import and use to create and publish
+ site artifacts."))
+
+(defmethod initialize-instance :after ((content content) &key)
+ (dolist (key (content-keywords content))
+ (when *content*
+ (if (gethash key *content*)
+ (pushnew content (gethash key *content*))
+ (setf (gethash key *content*)
+ (list content))))))
+
+(defclass file (content)
((filepath
- :reader local-filepath
+ :reader filepath
:initarg :filepath
- :initform (error "LOCAL-FILE-CONTENT must have a FILEPATH slot value."))))
-
-(defclass plaintext-content (content)
- ((encoding
- :reader encoding
- :initarg :encoding
- :initform :utf8)))
-
-(defclass markdown-content (plaintext-content) ())
-(defclass org-content (plaintext-content) ())
-(defclass pdf-content (content) ())
-(defclass image-content (content) ())
-(defclass audio-content (content) ())
-(defclass video-content (content) ())
+ :initform (error "FILE must have a FILEPATH slot value."))))
+
+(defmethod initialize-instance :after ((content file) &key)
+ (when *content*
+ (setf (gethash (filepath content) *content*)
+ content)))
+
+(defun content-with-tags (&rest tags)
+ "Content utility function to locate all content with all of the
+ supplied keyword tags"
+ (when *content*
+ (let ((tagged (gethash (first tags) *content*)))
+ (dolist (tag (rest tags) tagged)
+ (setf tagged
+ (intersection tagged (gethash tag *content*)))))))
+
+(defun find-content (pred)
+ "Generic content query. PRED is a preedicate of one argument, and is
+ passed a CONTENT instance. Returns a list of instances for which
+ PRED returns non NIL."
+ (when *content*
+ (loop :for instance :being :the :hash-value :of *content*
+ :when (funcall pred instance)
+ :collect instance)))
+
+(defun lookup-content (key)
+ "Looks up KEY in *CONTENT*."
+ (when *content*
+ (gethash key *content*)))
+
+(defun content-with-filepath-like (regex)
+ "Returns all FILE content instances whose filepath matches the
+ supplied regular expression."
+ (find-content
+ (lambda (content)
+ (and (typep content 'file)
+ (ppcre:scan regex (namestring (filepath content)))))))
+
+;;; ARTIFACTS
+
+(defvar *artifacts* nil
+ "Dynamic hash-table, bound before building a site. A collection of
+ artifacts, indexed by the url path of the artifact.")
+
+(defun find-artifacts (pred)
+ "Generic artifact query. PRED is a preedicate of one argument, and is
+ passed an ARTIFACT instance. Returns a list of instances for which
+ PRED returns non NIL."
+ (when *artifacts*
+ (loop :for instance :being :the :hash-value :of *artifacts*
+ :when (funcall pred instance)
+ :collect instance)))
+
+(defun artifacts-with-class (class)
+ "Returns a list of instances of CLASS from the *ARTIFACT* store."
+ (find-artifacts
+ (lambda (artifact) (typep artifact class))))
+
+(defun artifacts-with-urlpath-like (regex)
+ "Returns a list of instances of artifacts whose url path matches the
+ supplied regex."
+ (find-artifacts
+ (lambda (artifact)
+ (ppcre:scan regex (url-path artifact)))))
+
+(defclass artifact ()
+ ((url
+ :accessor url-path
+ :initarg :url
+ :initform (error "An artifact needs a url")
+ :documentation "A URL path, relative to the site root, from where
+ this artifact is to be served."))
+ (:documentation
+ "ARTIFACT instances represent what flexo publishes: i.e. pages and
+ files to be served from some web root."))
+
+(defmethod initialize-instance :after ((artifact artifact) &key)
+ (when *artifacts*
+ (setf (gethash (url-path artifact) *site*)
+ artifact)))
+
+(defclass page (artifact)
+ ((html
+ :accessor page-html
+ :initarg :html
+ :initform (error "A PAGE must have some HTML")
+ :documentation
+ "A utf8 encoded string holding the html content of this page artifact.")))
+
+(defmacro page (url &body body)
+ "Creates a page instance with the given url path and expdning the
+spinneret template in BODY."
+ `(make-instance
+ 'page
+ :url ,url
+ :html (with-html-string ,@body)))
+
+(defmacro define-page-template (template-name (url-arg &rest lambda-list-def) &body template-body)
+ "Defines a function that creates an instance of PAGE from a reusable template."
+ `(defun ,template-name (,url-arg ,@lambda-list-def)
+ (page ,url-arg
+ ,@template-body)))
;;;