From a176f38537d0641f3a187157f5e5d851bd86ffb7 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 2 Jul 2014 16:22:04 -0400 Subject: [PATCH 01/44] Bugfix: Feed should be an abstract class. --- coleslaw.asd | 2 +- src/feeds.lisp | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/coleslaw.asd b/coleslaw.asd index 71552b4..40ab023 100644 --- a/coleslaw.asd +++ b/coleslaw.asd @@ -1,7 +1,7 @@ (defsystem #:coleslaw :name "coleslaw" :description "Flexible Lisp Blogware" - :version "0.9.5" + :version "0.9.5-dev" :license "BSD" :author "Brit Butler " :pathname "src/" diff --git a/src/feeds.lisp b/src/feeds.lisp index 8aa5e3c..a1236c1 100644 --- a/src/feeds.lisp +++ b/src/feeds.lisp @@ -5,16 +5,18 @@ (defclass feed (index) ((format :initform nil :initarg :format :accessor feed-format))) -(defmethod discover ((doc-type (eql (find-class 'feed)))) +(defclass standard-feed (feed) ()) + +(defmethod discover ((doc-type (eql (find-class 'standard-feed)))) (let ((content (by-date (find-all 'post)))) (dolist (format '(rss atom)) - (let ((feed (make-instance 'feed :format format + (let ((feed (make-instance 'standard-feed :format format :content (take-up-to 10 content) :slug (format nil "~(~a~)" format)))) (add-document feed))))) -(defmethod publish ((doc-type (eql (find-class 'feed)))) - (dolist (feed (find-all 'feed)) +(defmethod publish ((doc-type (eql (find-class 'standard-feed)))) + (dolist (feed (find-all 'standard-feed)) (write-document feed (theme-fn (feed-format feed) "feeds")))) ;;; Tag Feeds From 4630a7a224277d31e02e277d032dafe7ec52381e Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Mon, 14 Jul 2014 13:40:59 -0400 Subject: [PATCH 02/44] Remove scaffolded parallel plugin. --- plugins/parallel.lisp | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 plugins/parallel.lisp diff --git a/plugins/parallel.lisp b/plugins/parallel.lisp deleted file mode 100644 index 18e4a13..0000000 --- a/plugins/parallel.lisp +++ /dev/null @@ -1,16 +0,0 @@ -(eval-when (:compile-toplevel :load-toplevel) - (ql:quickload 'lparallel)) - -(defpackage :coleslaw-parallel - (:use :cl) - (:export #:enable)) - -(in-package :coleslaw-parallel) - -;; TODO: The bulk of the speedup here should come from parallelizing discover. -;; Publish will also benefit. Whether it's better to spin off threads for each -;; content type/index type or the operations *within* discover/publish is not -;; known, the higher granularity of doing it at the iterating over types level -;; is certainly easier to prototype though. - -(defun enable ()) From 567c4473bbff3b024a3d1a402e69e97cd00221f5 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Mon, 14 Jul 2014 16:15:08 -0400 Subject: [PATCH 03/44] Tentatively factor deploy method into git-hook plugin. TODO follows... * Deploy :after plugins probably need revision now, and coleslaw-heroku. * README, HACKING need updates. Plugin-api.md too. * NEWS needs a carefully worded entry. Is that what we want? --- examples/example.coleslawrc | 13 ++++++++----- examples/example.post-receive | 2 +- plugins/git-hook.lisp | 22 ++++++++++++++++++++++ src/coleslaw.lisp | 25 ++++++++----------------- src/config.lisp | 2 +- src/packages.lisp | 5 +++-- tests/tests.lisp | 2 +- 7 files changed, 44 insertions(+), 27 deletions(-) create mode 100644 plugins/git-hook.lisp diff --git a/examples/example.coleslawrc b/examples/example.coleslawrc index 73016af..5db5ee1 100644 --- a/examples/example.coleslawrc +++ b/examples/example.coleslawrc @@ -2,12 +2,13 @@ :deploy-dir "/home/git/blog/" :domain "http://blog.redlinernotes.com" :feeds ("lisp") - :plugins ((mathjax) - (sitemap) - (static-pages) + :plugins ((analytics :tracking-code "foo") (disqus :shortname "my-site-name") - (analytics :tracking-code "foo")) - :repo "/home/git/tmp/improvedmeans/" + ; (git-hook) ;; *Remove comment to enable git push deploys. + ; (incremental) ;; *Remove comment enable incremental builds. + (mathjax) + (sitemap) + (static-pages)) :routing ((:post "posts/~a") (:tag-index "tag/~a") (:month-index "date/~a") @@ -22,3 +23,5 @@ :staging-dir "/tmp/coleslaw/" :title "Improved Means for Achieving Deteriorated Ends" :theme "hyde") + +;; * Prerequisites described in plugin docs. \ No newline at end of file diff --git a/examples/example.post-receive b/examples/example.post-receive index 32398e7..c89c3f4 100644 --- a/examples/example.post-receive +++ b/examples/example.post-receive @@ -1,6 +1,5 @@ ########## CONFIGURATION VALUES ########## -# TMP_GIT_CLONE _must_ match the :repo argument in your .coleslawrc! TMP_GIT_CLONE=$HOME/tmp/improvedmeans/ # Set LISP to your preferred implementation. The following @@ -30,6 +29,7 @@ while read oldrev newrev refname; do elif [ $LISP = ccl ]; then ccl -e "(ql:quickload 'coleslaw) (coleslaw:main \"$TMP_GIT_CLONE\" \"$oldrev\") (coleslaw::exit)" else + echo -e "$LISP is not a supported lisp dialect at this time. Exiting.\n" exit 1 fi fi diff --git a/plugins/git-hook.lisp b/plugins/git-hook.lisp new file mode 100644 index 0000000..a27d6d4 --- /dev/null +++ b/plugins/git-hook.lisp @@ -0,0 +1,22 @@ +(defpackage :coleslaw-git-hook + (:use :cl) + (:import-from :coleslaw #:*config* + #:deploy-dir + #:rel-path + #:run-program + #:update-symlink)) + +(in-package :coleslaw-git-hook) + +(defmethod coleslaw:deploy (staging) + (let* ((dest (deploy-dir *config*)) + (new-build (rel-path dest "generated/~a" (get-universal-time))) + (prev (rel-path dest ".prev")) + (curr (rel-path dest ".curr"))) + (ensure-directories-exist new-build) + (run-program "mv ~a ~a" staging new-build) + (when (and (probe-file prev) (truename prev)) + (run-program "rm -r ~a" (truename prev))) + (when (probe-file curr) + (update-symlink prev (truename curr))) + (update-symlink curr new-build))) diff --git a/src/coleslaw.lisp b/src/coleslaw.lisp index e92616e..d65f360 100644 --- a/src/coleslaw.lisp +++ b/src/coleslaw.lisp @@ -3,12 +3,12 @@ (defvar *last-revision* nil "The git revision prior to the last push. For use with GET-UPDATED-FILES.") -(defun main (&optional (repo-dir "") oldrev) - "Load the user's config file, then compile and deploy the site. Optionally, -REPO-DIR is the location of the blog repo and OLDREV is the revision prior to -the last push." - (setf *last-revision* oldrev) +(defun main (repo-dir &optional oldrev) + "Load the user's config file, then compile and deploy the blog stored +in REPO-DIR. Optionally, OLDREV is the revision prior to the last push." (load-config repo-dir) + (setf (repo *config*) repo-dir + *last-revision* oldrev) (load-content) (compile-theme (theme *config*)) (let ((dir (staging-dir *config*))) @@ -40,19 +40,10 @@ the last push." (update-symlink "index.html" "1.html"))) (defgeneric deploy (staging) - (:documentation "Deploy the STAGING dir, updating the .prev and .curr symlinks.") + (:documentation "Deploy the STAGING build to the directory specified in the config.") (:method (staging) - (let* ((dest (deploy-dir *config*)) - (new-build (rel-path dest "generated/~a" (get-universal-time))) - (prev (rel-path dest ".prev")) - (curr (rel-path dest ".curr"))) - (ensure-directories-exist new-build) - (run-program "mv ~a ~a" staging new-build) - (when (and (probe-file prev) (truename prev)) - (run-program "rm -r ~a" (truename prev))) - (when (probe-file curr) - (update-symlink prev (truename curr))) - (update-symlink curr new-build)))) + (let ((destination (deploy-dir *config*))) + (run-program "rsync --delete -avz ~a ~a" staging destination)))) (defun update-symlink (path target) "Update the symlink at PATH to point to TARGET." diff --git a/src/config.lisp b/src/config.lisp index ae30c30..4d61e4c 100644 --- a/src/config.lisp +++ b/src/config.lisp @@ -61,7 +61,7 @@ doesn't exist, use the .coleslawrc in the home directory." repo-config (rel-path (user-homedir-pathname) ".coleslawrc")))) -(defun load-config (&optional repo-dir) +(defun load-config (&optional (repo-dir "")) "Find and load the coleslaw configuration from .coleslawrc. REPO-DIR will be preferred over the home directory if provided." (with-open-file (in (discover-config-path repo-dir) :external-format '(:utf-8)) diff --git a/src/packages.lisp b/src/packages.lisp index a9a3786..7bc0a65 100644 --- a/src/packages.lisp +++ b/src/packages.lisp @@ -22,12 +22,13 @@ #:title-of #:author-of #:find-content-by-path - ;; Plugin API + Theming + ;; Theming + Plugin API + #:theme-fn #:plugin-conf-error #:render-text #:add-injection #:get-updated-files - #:theme-fn + #:deploy ;; The Document Protocol #:discover #:publish diff --git a/tests/tests.lisp b/tests/tests.lisp index 50d5071..891c9c4 100644 --- a/tests/tests.lisp +++ b/tests/tests.lisp @@ -10,4 +10,4 @@ (deftest sanity-test "A blog should compile and deploy correctly." - (is (zerop (coleslaw:main)))) + (is (zerop (coleslaw:main "")))) From c82fe20382f965034c2bb87daf1bad7ca473b83d Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Mon, 11 Aug 2014 16:44:36 -0400 Subject: [PATCH 04/44] Incremental compilation is done, note some new goals. --- docs/hacking.md | 62 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index 2cffba6..1e0de6f 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -219,6 +219,47 @@ PAGE, a content type for static page support, is available as a plugin. ## Areas for Improvement +### Scripting Conveniences/Basic Install + +Right now, we assume that you want to set up a bare repo and use git push +for site deploys. However good a system this may be, we don't need to have +it as a baked in assumption for coleslaw. The current deploy system should +be moved into a plugin and replaced with something braindead +(e.g. `mv` staging-dir to deploy-dir). + +This leads to the following: + +1. Simplify the getting started process for new users. + They should just be able to do + `(progn + (ql:quickload :coleslaw) + (coleslaw:main "/path/to/my/blog-repo"))`. + We'll still require a git repo for now. This does necessitate updating + `get-updated-files` for the case where no revision is passed. +2. We could also add command-line tools/scripts to run coleslaw, set up + the db for incremental builds, scaffold a new post, etc. for new users. + Xach's buildapp may be useful here. frog and hakyll are good points of + inspiration here. + +We may need a fair amount of error-handling improvements to make this +work well but it would be a huge improvement to user-friendliness and +help "expand our audience". Did I just say that? Ugh. It will also be +important to communicate this change to our users loud and clear. + +### Plugin Constraints + +There is no system for determining what plugins work together or +enforcing the requirements or constraints of any particular +plugin. That is to say, the plugins are not actually modular. They are +closer to controlled monkey-patching. + +While adding a [real module system to common lisp][asdf3] is probably +out of scope, we might be able to add some kind of [contract library][qpq] +for implementing this functionality. At the very least, a way to check +some assertions and error out at plugin load time if they fail should be +doable. I might not be able to [make illegal states unrepresentable][misu], +but I can sure as hell make them harder to construct than they are now. + ### New Content Type: Shouts! I've also toyed with the idea of a content type called a SHOUT, which @@ -249,24 +290,6 @@ Unfortunately, this does not solve: Content Types it includes or the CONTENT which indexes it appears on is not yet clear. -### Incremental Compilation - -Incremental compilation is doable, even straightforward if you ignore -indexes. It is also preferable to building the site in parallel as -avoiding work is better than using more workers. Moreover, being -able to determine (and expose) what files just changed enables new -functionality such as plugins that cross-post to tumblr. - -This is a cool project and the effects are far reaching. Among other -things the existing deployment model would not work as it involves -rebuilding the entire site. In all likelihood we would want to update -the site 'in-place'. How to replace the compilation and deployment -model via a plugin has not yet been explored. Atomicity of filesystem -operations would be a reasonable concern. Also, every numbered INDEX -would have to be regenerated along with any tag or month indexes -matching the modified files. If incremental compilation is a goal, -simply disabling the indexes may be appropriate for certain users. - [post_receive_hook]: https://github.com/redline6561/coleslaw/blob/master/examples/example.post-receive [closure_template]: https://github.com/archimag/cl-closure-template [api_docs]: https://github.com/redline6561/coleslaw/blob/master/docs/plugin-api.md @@ -274,3 +297,6 @@ simply disabling the indexes may be appropriate for certain users. [clrz]: https://github.com/redline6561/colorize [pyg]: http://pygments.org/ [incf]: https://github.com/redline6561/coleslaw/blob/master/plugins/incremental.lisp +[asdf3]: https://github.com/fare/asdf3-2013 +[qpq]: https://github.com/sellout/quid-pro-quo +[misu]: https://blogs.janestreet.com/effective-ml-revisited/ From 99e87c061f299b296c6785abce9b6d47256d9969 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 13 Aug 2014 08:55:25 -0400 Subject: [PATCH 05/44] Add some error-handling notes. --- docs/hacking.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index 1e0de6f..977b3b9 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -219,6 +219,23 @@ PAGE, a content type for static page support, is available as a plugin. ## Areas for Improvement +### Real Error Handling + +One reason Coleslaw's code base is so small is probably the +omission of any serious error handling. Trying to debug +coleslaw if there's a problem during a build is unpleasant +at best, especially for anyone not coming from the lisp world. + +We need to start handling errors and reporting errors in ways +that are useful to the user. The two most obvious areas seem to be: + +1. Loading of Content. If `read-content` fails to parse a file, we + should tell the user what file failed and why. +2. Custom themes that try to access non-existent properties of content + do not currently error. They just wind up returning whitespace. + When the theme compiles, we should alert the user to any obvious + issues with it. + ### Scripting Conveniences/Basic Install Right now, we assume that you want to set up a bare repo and use git push @@ -241,11 +258,6 @@ This leads to the following: Xach's buildapp may be useful here. frog and hakyll are good points of inspiration here. -We may need a fair amount of error-handling improvements to make this -work well but it would be a huge improvement to user-friendliness and -help "expand our audience". Did I just say that? Ugh. It will also be -important to communicate this change to our users loud and clear. - ### Plugin Constraints There is no system for determining what plugins work together or From c2920901faf6aa543080a4894d5149847b3b437e Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 15 Aug 2014 17:02:06 -0400 Subject: [PATCH 06/44] More notes. --- docs/hacking.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/hacking.md b/docs/hacking.md index 977b3b9..475565a 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -227,7 +227,7 @@ coleslaw if there's a problem during a build is unpleasant at best, especially for anyone not coming from the lisp world. We need to start handling errors and reporting errors in ways -that are useful to the user. The two most obvious areas seem to be: +that are useful to the user. Example errors users have encountered: 1. Loading of Content. If `read-content` fails to parse a file, we should tell the user what file failed and why. @@ -235,6 +235,14 @@ that are useful to the user. The two most obvious areas seem to be: do not currently error. They just wind up returning whitespace. When the theme compiles, we should alert the user to any obvious issues with it. +3. Trying to load content from the bare repo instead of the clone. + i.e. Specifying the `:repo` in .coleslawrc as the bare repo. + The README should clarify this point and the need for posts to be + ".post" files. +4. `:staging-dir`, `:repo`, `:deploy-dir` should all have trailing + slashes. This is a subtle, unenforced issue not mentioned in + documentation. This is exactly the sort of thing the next release + should strive to fix. (Also, `:repo` can probably be deprecated...) ### Scripting Conveniences/Basic Install From ab12f8ed6d5c8040c305a9c8dbd7584a464aeef8 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 22 Aug 2014 15:01:24 -0400 Subject: [PATCH 07/44] Some TODO notes. --- docs/hacking.md | 42 +++++++++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index 475565a..e2c36a6 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -219,6 +219,36 @@ PAGE, a content type for static page support, is available as a plugin. ## Areas for Improvement +### Unified Routing + +Right now, the templates break if you use a custom routing scheme +in your config. This is flatly ridiculous. The templates should be +updated to use a url field stored on each object which will store +the unified domain/path of the object. This can be done transparently +to users, though must be handled with care and may involve refactoring +in the document protocol. Test carefully. If we cheat by hardcoding +the sitemap and/or feeds, that's probably okay for the moment. + +### Clean up Slugs vs Paths + +Currently, we track uniqueness in the site by using a hashtable +keyed on `page-url`, i.e. relative paths. The paths are mostly +constructed from slugs. Sometimes we use the slugs for sorting +or other purposes. It would be nice if we could build the paths +at instance creation time and scrap the slugs altogether. +How feasible is this? + +### Finish up Basic Deploy + +The deploy method has been simplified and a git-hook plugin added. +Documentation and testing for the other deploy plugins is next. + +### Deprecate :repo config option? + +Coleslaw must at this point *always* be called with `coleslaw:main` +passing the repo directory as an argument. Given this, there is no +need for users to specify a repo in their config. + ### Real Error Handling One reason Coleslaw's code base is so small is probably the @@ -230,7 +260,9 @@ We need to start handling errors and reporting errors in ways that are useful to the user. Example errors users have encountered: 1. Loading of Content. If `read-content` fails to parse a file, we - should tell the user what file failed and why. + should tell the user what file failed and why. We also should + probably enforce more constraints about metadata. E.g. Empty + metadata is not allowed/meaningful. Trailing space after separator, etc. 2. Custom themes that try to access non-existent properties of content do not currently error. They just wind up returning whitespace. When the theme compiles, we should alert the user to any obvious @@ -239,10 +271,6 @@ that are useful to the user. Example errors users have encountered: i.e. Specifying the `:repo` in .coleslawrc as the bare repo. The README should clarify this point and the need for posts to be ".post" files. -4. `:staging-dir`, `:repo`, `:deploy-dir` should all have trailing - slashes. This is a subtle, unenforced issue not mentioned in - documentation. This is exactly the sort of thing the next release - should strive to fix. (Also, `:repo` can probably be deprecated...) ### Scripting Conveniences/Basic Install @@ -263,8 +291,8 @@ This leads to the following: `get-updated-files` for the case where no revision is passed. 2. We could also add command-line tools/scripts to run coleslaw, set up the db for incremental builds, scaffold a new post, etc. for new users. - Xach's buildapp may be useful here. frog and hakyll are good points of - inspiration here. + Xach's buildapp or cl-launch would be useful here. frog and hakyll are + good points of inspiration as well. ### Plugin Constraints From 04c1ed9aeec84a2a6e3fe73a710b000f36f84f93 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 22 Aug 2014 16:23:30 -0400 Subject: [PATCH 08/44] Bugfixes. --- examples/example.coleslawrc | 2 +- src/config.lisp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/example.coleslawrc b/examples/example.coleslawrc index 5db5ee1..fb18145 100644 --- a/examples/example.coleslawrc +++ b/examples/example.coleslawrc @@ -13,7 +13,7 @@ (:tag-index "tag/~a") (:month-index "date/~a") (:numeric-index "~d") - (:feed "~a.xml") + (:standard-feed "~a.xml") (:tag-feed "tag/~a.xml")) :sitenav ((:url "http://redlinernotes.com/" :name "Home") (:url "http://twitter.com/redline6561" :name "Twitter") diff --git a/src/config.lisp b/src/config.lisp index 4d61e4c..cc3e7e9 100644 --- a/src/config.lisp +++ b/src/config.lisp @@ -10,7 +10,7 @@ (license :initarg :license :reader license) (page-ext :initarg :page-ext :reader page-ext) (plugins :initarg :plugins :reader plugins) - (repo :initarg :repo :reader repo) + (repo :initarg :repo :accessor repo) (routing :initarg :routing :reader routing) (separator :initarg :separator :reader separator) (sitenav :initarg :sitenav :reader sitenav) From ae96c069c4d0f1bd7cbb1848bcd2bc5800fe4db7 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 22 Aug 2014 17:29:47 -0400 Subject: [PATCH 09/44] Half-way commit to getting hardcoded paths out of templates. --- docs/hacking.md | 10 ++++++++-- src/content.lisp | 3 ++- src/documents.lisp | 10 +++++++--- src/indexes.lisp | 12 ++++++++---- src/posts.lisp | 9 +++++---- themes/atom.tmpl | 2 +- themes/hyde/index.tmpl | 10 +++++----- themes/hyde/post.tmpl | 6 +++--- themes/readable/index.tmpl | 6 +++--- themes/readable/post.tmpl | 6 +++--- themes/rss.tmpl | 4 ++-- 11 files changed, 47 insertions(+), 31 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index e2c36a6..44069a8 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -224,11 +224,17 @@ PAGE, a content type for static page support, is available as a plugin. Right now, the templates break if you use a custom routing scheme in your config. This is flatly ridiculous. The templates should be updated to use a url field stored on each object which will store -the unified domain/path of the object. This can be done transparently -to users, though must be handled with care and may involve refactoring +the path of the object. This can be done transparently to users, +though must be handled with care and may involve refactoring in the document protocol. Test carefully. If we cheat by hardcoding the sitemap and/or feeds, that's probably okay for the moment. +Turns out this is even messier than I thought. Links are built from +scratch in the templates *all over the place*. Tags in posts, +taglinks and monthlinks in indexes, and prev/next links in numeric +indexes. I'm also doing two `find-all` calls in the base `render` +method for index. So I should profile and/or memoize that if needed. + ### Clean up Slugs vs Paths Currently, we track uniqueness in the site by using a hashtable diff --git a/src/content.lisp b/src/content.lisp index aa7cf72..90891eb 100644 --- a/src/content.lisp +++ b/src/content.lisp @@ -33,8 +33,9 @@ (defclass content () ((file :initarg :file :reader content-file) (date :initarg :date :reader content-date) + (path :initarg :path :accessor path-of) + (slug :initarg :slug :accessor slug-of) (tags :initarg :tags :accessor content-tags) - (slug :initarg :slug :accessor content-slug) (text :initarg :text :accessor content-text)) (:default-initargs :tags nil :date nil :slug nil)) diff --git a/src/documents.lisp b/src/documents.lisp index 3c398dd..82f0c66 100644 --- a/src/documents.lisp +++ b/src/documents.lisp @@ -29,14 +29,14 @@ (:documentation "The url to the DOCUMENT without the domain.") (:method (document) (let* ((class-name (class-name (class-of document))) - (route (assoc (make-keyword class-name) (routing *config*)))) + (route (get-route class-name))) (if route - (format nil (second route) (slot-value document 'slug)) + (format nil route (slug-of document)) (error "No routing method found for: ~A" class-name))))) (defmethod page-url :around ((document t)) (let* ((result (call-next-method)) - (type (or (pathname-type result) "html"))) + (type (or (pathname-type result) (page-ext *config*)))) (make-pathname :type type :defaults result))) (defgeneric render (document &key &allow-other-keys) @@ -64,6 +64,10 @@ use it as the template passing any RENDER-ARGS." (url (namestring (page-url document)))) (write-file (rel-path (staging-dir *config*) url) html))) +(defun get-route (doc-type) + "Return the route format string for DOC-TYPE." + (second (assoc (make-keyword doc-type) (routing *config*)))) + (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* diff --git a/src/indexes.lisp b/src/indexes.lisp index eaf0807..cb07a21 100644 --- a/src/indexes.lisp +++ b/src/indexes.lisp @@ -6,13 +6,17 @@ "The list of tags which content has been tagged with.") (defclass index () - ((slug :initarg :slug :reader index-slug) + ((path :initarg :path :accessor path-of) + (slug :initarg :slug :reader slug-of) (title :initarg :title :reader title-of) (content :initarg :content :reader index-content))) +(defmethod initialize-instance :after ((object index) &key) + (setf (path-of object) (page-url object))) + (defmethod render ((object index) &key prev next) - (funcall (theme-fn 'index) (list :tags *all-tags* - :months *all-months* + (funcall (theme-fn 'index) (list :tags (find-all 'tag-index) + :months (find-all 'month-index) :config *config* :index object :prev prev @@ -24,7 +28,7 @@ (defmethod discover ((doc-type (eql (find-class 'tag-index)))) (let ((content (by-date (find-all 'post)))) - (dolist (tag (all-tags)) + (dolist (tag *all-tags*) (add-document (index-by-tag tag content))))) (defun index-by-tag (tag content) diff --git a/src/posts.lisp b/src/posts.lisp index ebfc2c2..ac30554 100644 --- a/src/posts.lisp +++ b/src/posts.lisp @@ -11,10 +11,11 @@ (author author-of) (format post-format) (text content-text)) object - (setf (content-slug object) (slugify title) - format (make-keyword (string-upcase format)) - text (render-text text format) - author (or author (author *config*))))) + (setf (slug-of object) (slugify title) + (path-of object) (page-url object) + format (make-keyword (string-upcase format)) + text (render-text text format) + author (or author (author *config*))))) (defmethod render ((object post) &key prev next) (funcall (theme-fn 'post) (list :config *config* diff --git a/themes/atom.tmpl b/themes/atom.tmpl index 761f684..6061503 100644 --- a/themes/atom.tmpl +++ b/themes/atom.tmpl @@ -14,7 +14,7 @@ {foreach $post in $content.content} - + {$post.title} {$post.date} {$post.date} diff --git a/themes/hyde/index.tmpl b/themes/hyde/index.tmpl index 9100fdf..954eeb8 100644 --- a/themes/hyde/index.tmpl +++ b/themes/hyde/index.tmpl @@ -4,20 +4,20 @@

{$index.title}

{foreach $obj in $index.content} {/foreach}
- {if $prev} Previous {/if} - {if $next} Next {/if} + {if $prev} Previous {/if} + {if $next} Next {/if}
{if $tags}

This blog covers {foreach $tag in $tags} - {$tag.name}{nil} + {$tag.name}{nil} {if not isLast($tag)},{sp}{/if} {/foreach}

@@ -26,7 +26,7 @@

View content from {foreach $month in $months} - {$month}{nil} + {$month}{nil} {if not isLast($month)},{sp}{/if} {/foreach}

diff --git a/themes/hyde/post.tmpl b/themes/hyde/post.tmpl index 8400fcb..d66e8e9 100644 --- a/themes/hyde/post.tmpl +++ b/themes/hyde/post.tmpl @@ -6,7 +6,7 @@
{\n} {if $post.tags} Tagged as {foreach $tag in $post.tags} - {$tag.name}{nil} + {$tag.name}{nil} {if not isLast($tag)},{sp}{/if} {/foreach} {/if} @@ -21,7 +21,7 @@ {$post.text |noAutoescape}
{\n}
{\n} - {if $prev} Previous
{/if}{\n} - {if $next} Next
{/if}{\n} + {if $prev} Previous
{/if}{\n} + {if $next} Next
{/if}{\n}
{\n} {/template} diff --git a/themes/readable/index.tmpl b/themes/readable/index.tmpl index 8abe85f..5cf5514 100644 --- a/themes/readable/index.tmpl +++ b/themes/readable/index.tmpl @@ -4,7 +4,7 @@

{$index.title}

{foreach $obj in $index.content}
-

{$obj.title}

+

{$obj.title}

posted on {$obj.date}

{$obj.text |noAutoescape}
@@ -13,7 +13,7 @@

This blog covers {foreach $tag in $tags} - {$tag.name}{nil} + {$tag.name}{nil} {if not isLast($tag)},{sp}{/if} {/foreach}

@@ -23,7 +23,7 @@

View content from {foreach $month in $months} - {$month}{nil} + {$month}{nil} {if not isLast($month)},{sp}{/if} {/foreach}

diff --git a/themes/readable/post.tmpl b/themes/readable/post.tmpl index 15843f1..76bb24a 100644 --- a/themes/readable/post.tmpl +++ b/themes/readable/post.tmpl @@ -6,7 +6,7 @@

{if $post.tags} Tagged as {foreach $tag in $post.tags} - {$tag.name}{nil} + {$tag.name}{nil} {if not isLast($tag)},{sp}{/if} {/foreach} {/if} @@ -20,8 +20,8 @@ {$post.text |noAutoescape}

{\n} {/template} diff --git a/themes/rss.tmpl b/themes/rss.tmpl index a21682e..bcd4460 100644 --- a/themes/rss.tmpl +++ b/themes/rss.tmpl @@ -13,10 +13,10 @@ {foreach $post in $content.content} {$post.title} - {$config.domain}/posts/{$post.slug}.{$config.pageExt} + {$config.domain}/{$post.path} {$post.date} {$config.author} - {$config.domain}/posts/{$post.slug}.{$config.pageExt} + {$config.domain}/{$post.path} {foreach $tag in $post.tags} {/foreach} From ab03544600746392d0787ed0b4077a1b21e30a86 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Mon, 25 Aug 2014 13:47:43 -0400 Subject: [PATCH 10/44] Tracking TODOs in the Hacking file now. --- TODO | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 TODO diff --git a/TODO b/TODO deleted file mode 100644 index 95ca4af..0000000 --- a/TODO +++ /dev/null @@ -1,5 +0,0 @@ -TODO: -Coleslaw.next -; See if there are any good ideas we can steal from [Frog](https://github.com/greghendershott/frog) -;; needs: shout template/render function. Twitter\Disqus integration with shouts? -; Incremental compilation: only "touched" posts+tags+months and by-n. -> 1.0 From f512ab7a48e50b6b66081b4ace8b3dc20b2aed56 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Tue, 26 Aug 2014 15:29:47 -0400 Subject: [PATCH 11/44] More TODO notes. --- docs/hacking.md | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index 44069a8..e82a615 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -235,14 +235,34 @@ taglinks and monthlinks in indexes, and prev/next links in numeric indexes. I'm also doing two `find-all` calls in the base `render` method for index. So I should profile and/or memoize that if needed. -### Clean up Slugs vs Paths - +We should also not have a **slug** _and_ **path** slot due to this. Currently, we track uniqueness in the site by using a hashtable keyed on `page-url`, i.e. relative paths. The paths are mostly constructed from slugs. Sometimes we use the slugs for sorting -or other purposes. It would be nice if we could build the paths -at instance creation time and scrap the slugs altogether. -How feasible is this? +or other purposes. We should be able to build the paths at instance +creation time and scrap the slugs altogether. Note that this will +require changes to how we sort `NUMERIC-INDEX`. + +### Immutable Data + +Currently, most of our content classes (and the config class) have +*accessor* slots in addition to *reader* slots. There should be no +need for accessors as our data doesn't change after +initialization. They are computed from the git repo, dumped into +templates, and that's it. This is really a question of how we +initialize things. I settled on the current `load-content` scheme, +flawed as it is, because it is simple and lightweight. + +Content files in the git repo have a file extension denoting the +content type, a header section with metadata and a text body. That +gets transformed into a `construct` call (and ultimately +`make-instance`) to the class matching the file extension, and a bunch +of initargs. We avoid code duplication for lots of unique +constructors, but we lose out some in error handling, separation of +interface and implementation, etc. + +Keene's Object Oriented Programming in CL has good advice on this +situation, specifically on page 162, Separation of Initargs and Slot Names. ### Finish up Basic Deploy From ed886b57e75f19d18c0a667f978e83bff7715a98 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Tue, 26 Aug 2014 17:24:29 -0400 Subject: [PATCH 12/44] Set blog repo in "the right place". --- src/coleslaw.lisp | 3 +-- src/config.lisp | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coleslaw.lisp b/src/coleslaw.lisp index d65f360..36642ea 100644 --- a/src/coleslaw.lisp +++ b/src/coleslaw.lisp @@ -7,8 +7,7 @@ "Load the user's config file, then compile and deploy the blog stored in REPO-DIR. Optionally, OLDREV is the revision prior to the last push." (load-config repo-dir) - (setf (repo *config*) repo-dir - *last-revision* oldrev) + (setf *last-revision* oldrev) (load-content) (compile-theme (theme *config*)) (let ((dir (staging-dir *config*))) diff --git a/src/config.lisp b/src/config.lisp index cc3e7e9..14e3dae 100644 --- a/src/config.lisp +++ b/src/config.lisp @@ -66,5 +66,6 @@ doesn't exist, use the .coleslawrc in the home directory." preferred over the home directory if provided." (with-open-file (in (discover-config-path repo-dir) :external-format '(:utf-8)) (let ((config-form (read in))) - (setf *config* (construct 'blog config-form)))) + (setf *config* (construct 'blog config-form) + (repo *config*) repo-dir))) (load-plugins (plugins *config*))) From ab34400307fdf405314f81f3fcc9653059e15e2f Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 27 Aug 2014 11:51:12 -0400 Subject: [PATCH 13/44] Use with-slots for initializers, readers only please! --- docs/hacking.md | 21 --------------------- plugins/static-pages.lisp | 5 +++-- src/content.lisp | 13 ++++++------- src/documents.lisp | 21 ++++++++++----------- src/feeds.lisp | 2 +- src/indexes.lisp | 10 +++++----- src/posts.lisp | 14 +++++--------- 7 files changed, 30 insertions(+), 56 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index e82a615..8979d4f 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -243,27 +243,6 @@ or other purposes. We should be able to build the paths at instance creation time and scrap the slugs altogether. Note that this will require changes to how we sort `NUMERIC-INDEX`. -### Immutable Data - -Currently, most of our content classes (and the config class) have -*accessor* slots in addition to *reader* slots. There should be no -need for accessors as our data doesn't change after -initialization. They are computed from the git repo, dumped into -templates, and that's it. This is really a question of how we -initialize things. I settled on the current `load-content` scheme, -flawed as it is, because it is simple and lightweight. - -Content files in the git repo have a file extension denoting the -content type, a header section with metadata and a text body. That -gets transformed into a `construct` call (and ultimately -`make-instance`) to the class matching the file extension, and a bunch -of initargs. We avoid code duplication for lots of unique -constructors, but we lose out some in error handling, separation of -interface and implementation, etc. - -Keene's Object Oriented Programming in CL has good advice on this -situation, specifically on page 162, Separation of Initargs and Slot Names. - ### Finish up Basic Deploy The deploy method has been simplified and a git-hook plugin added. diff --git a/plugins/static-pages.lisp b/plugins/static-pages.lisp index 19fdfd1..eb38033 100644 --- a/plugins/static-pages.lisp +++ b/plugins/static-pages.lisp @@ -20,8 +20,9 @@ (defmethod initialize-instance :after ((object page) &key) ;; Expect all static-pages to be written in Markdown for now. - (with-accessors ((text content-text)) object - (setf text (render-text text :md)))) + (with-slots (url text) object + (setf url (make-pathname :defaults url) + text (render-text text :md)))) (defmethod render ((object page) &key next prev) ;; For the time being, we'll re-use the normal post theme. diff --git a/src/content.lisp b/src/content.lisp index 90891eb..2c24f42 100644 --- a/src/content.lisp +++ b/src/content.lisp @@ -31,16 +31,15 @@ ;; Content Types (defclass content () - ((file :initarg :file :reader content-file) + ((url :initarg :url :reader page-url) (date :initarg :date :reader content-date) - (path :initarg :path :accessor path-of) - (slug :initarg :slug :accessor slug-of) - (tags :initarg :tags :accessor content-tags) - (text :initarg :text :accessor content-text)) - (:default-initargs :tags nil :date nil :slug nil)) + (file :initarg :file :reader content-file) + (tags :initarg :tags :reader content-tags) + (text :initarg :text :reader content-text)) + (:default-initargs :tags nil :date nil)) (defmethod initialize-instance :after ((object content) &key) - (with-accessors ((tags content-tags)) object + (with-slots (tags) object (when (stringp tags) (setf tags (mapcar #'make-tag (cl-ppcre:split "," tags)))))) diff --git a/src/documents.lisp b/src/documents.lisp index 82f0c66..bd7516e 100644 --- a/src/documents.lisp +++ b/src/documents.lisp @@ -26,18 +26,17 @@ ;; Instance Methods (defgeneric page-url (document) - (:documentation "The url to the DOCUMENT without the domain.") - (:method (document) - (let* ((class-name (class-name (class-of document))) - (route (get-route class-name))) - (if route - (format nil route (slug-of document)) - (error "No routing method found for: ~A" class-name))))) + (:documentation "The relative URL to the DOCUMENT.")) -(defmethod page-url :around ((document t)) - (let* ((result (call-next-method)) - (type (or (pathname-type result) (page-ext *config*)))) - (make-pathname :type type :defaults result))) +(defun compute-url (document unique-id) + "Compute the relative URL for a DOCUMENT based on its UNIQUE-ID." + (let* ((class-name (class-name (class-of document))) + (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)))) (defgeneric render (document &key &allow-other-keys) (:documentation "Render the given DOCUMENT to HTML.")) diff --git a/src/feeds.lisp b/src/feeds.lisp index a1236c1..7d273fc 100644 --- a/src/feeds.lisp +++ b/src/feeds.lisp @@ -3,7 +3,7 @@ ;;; Atom and RSS Feeds (defclass feed (index) - ((format :initform nil :initarg :format :accessor feed-format))) + ((format :initform nil :initarg :format :reader feed-format))) (defclass standard-feed (feed) ()) diff --git a/src/indexes.lisp b/src/indexes.lisp index cb07a21..463b02e 100644 --- a/src/indexes.lisp +++ b/src/indexes.lisp @@ -6,13 +6,13 @@ "The list of tags which content has been tagged with.") (defclass index () - ((path :initarg :path :accessor path-of) - (slug :initarg :slug :reader slug-of) - (title :initarg :title :reader title-of) + ((url :initarg :url :reader page-url) + (title :initarg :title :reader title-of) (content :initarg :content :reader index-content))) -(defmethod initialize-instance :after ((object index) &key) - (setf (path-of object) (page-url object))) +(defmethod initialize-instance :after ((object index) &key slug) + (with-slots (url) object + (setf url (compute-url object slug)))) (defmethod render ((object index) &key prev next) (funcall (theme-fn 'index) (list :tags (find-all 'tag-index) diff --git a/src/posts.lisp b/src/posts.lisp index ac30554..d8fe29d 100644 --- a/src/posts.lisp +++ b/src/posts.lisp @@ -1,18 +1,14 @@ (in-package :coleslaw) (defclass post (content) - ((title :initarg :title :reader title-of) - (author :initarg :author :accessor author-of) - (format :initarg :format :accessor post-format)) + ((title :initarg :title :reader title-of) + (author :initarg :author :reader author-of) + (format :initarg :format :reader post-format)) (:default-initargs :author nil)) (defmethod initialize-instance :after ((object post) &key) - (with-accessors ((title title-of) - (author author-of) - (format post-format) - (text content-text)) object - (setf (slug-of object) (slugify title) - (path-of object) (page-url object) + (with-slots (url title author format text) object + (setf url (compute-url object (slugify title)) format (make-keyword (string-upcase format)) text (render-text text format) author (or author (author *config*))))) From ef378b9b56786120165c2b499494dbc5c3e1e3e6 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 27 Aug 2014 14:08:48 -0400 Subject: [PATCH 14/44] Finish the routing/slugs overhaul. --- src/content.lisp | 7 ++++++- src/documents.lisp | 23 ++++++++++++----------- src/indexes.lisp | 13 +++++++------ themes/atom.tmpl | 2 +- themes/hyde/index.tmpl | 10 +++++----- themes/hyde/post.tmpl | 6 +++--- themes/readable/index.tmpl | 6 +++--- themes/readable/post.tmpl | 6 +++--- themes/rss.tmpl | 4 ++-- 9 files changed, 42 insertions(+), 35 deletions(-) diff --git a/src/content.lisp b/src/content.lisp index 2c24f42..7b40945 100644 --- a/src/content.lisp +++ b/src/content.lisp @@ -4,7 +4,12 @@ (defclass tag () ((name :initarg :name :reader tag-name) - (slug :initarg :slug :reader tag-slug))) + (slug :initarg :slug :reader tag-slug) + (url :initarg :url))) + +(defmethod initialize-instance :after ((tag tag) &key) + (with-slots (url slug) tag + (setf url (compute-url nil slug 'tag-index)))) (defun make-tag (str) "Takes a string and returns a TAG instance with a name and slug." diff --git a/src/documents.lisp b/src/documents.lisp index bd7516e..0d62f48 100644 --- a/src/documents.lisp +++ b/src/documents.lisp @@ -28,9 +28,15 @@ (defgeneric page-url (document) (:documentation "The relative URL to the DOCUMENT.")) -(defun compute-url (document unique-id) - "Compute the relative URL for a DOCUMENT based on its UNIQUE-ID." - (let* ((class-name (class-name (class-of document))) +(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)))) (route (get-route class-name))) (unless route (error "No routing method found for: ~A" class-name)) @@ -38,10 +44,9 @@ (type (or (pathname-type result) (page-ext *config*)))) (make-pathname :type type :defaults result)))) -(defgeneric render (document &key &allow-other-keys) - (:documentation "Render the given DOCUMENT to HTML.")) - -;; Helper Functions +(defun get-route (doc-type) + "Return the route format string for DOC-TYPE." + (second (assoc (make-keyword doc-type) (routing *config*)))) (defun add-document (document) "Add DOCUMENT to the in-memory database. Error if a matching entry is present." @@ -63,10 +68,6 @@ use it as the template passing any RENDER-ARGS." (url (namestring (page-url document)))) (write-file (rel-path (staging-dir *config*) url) html))) -(defun get-route (doc-type) - "Return the route format string for DOC-TYPE." - (second (assoc (make-keyword doc-type) (routing *config*)))) - (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* diff --git a/src/indexes.lisp b/src/indexes.lisp index 463b02e..40b295c 100644 --- a/src/indexes.lisp +++ b/src/indexes.lisp @@ -7,6 +7,7 @@ (defclass index () ((url :initarg :url :reader page-url) + (name :initarg :name :reader index-name) (title :initarg :title :reader title-of) (content :initarg :content :reader index-content))) @@ -33,7 +34,7 @@ (defun index-by-tag (tag content) "Return an index of all CONTENT matching the given TAG." - (make-instance 'tag-index :slug (tag-slug tag) + (make-instance 'tag-index :slug (tag-slug tag) :name (tag-name tag) :content (remove-if-not (lambda (x) (tag-p tag x)) content) :title (format nil "Content tagged ~a" (tag-name tag)))) @@ -52,7 +53,7 @@ (defun index-by-month (month content) "Return an index of all CONTENT matching the given MONTH." - (make-instance 'month-index :slug month + (make-instance 'month-index :slug month :name month :content (remove-if-not (lambda (x) (month-p month x)) content) :title (format nil "Content from ~a" month))) @@ -72,15 +73,15 @@ (defun index-by-n (i content) "Return the index for the Ith page of CONTENT in reverse chronological order." (let ((content (subseq content (* 10 i)))) - (make-instance 'numeric-index :slug (1+ i) + (make-instance 'numeric-index :slug (1+ i) :name (1+ i) :content (take-up-to 10 content) :title "Recent Content"))) (defmethod publish ((doc-type (eql (find-class 'numeric-index)))) - (let ((indexes (sort (find-all 'numeric-index) #'< :key #'index-slug))) + (let ((indexes (sort (find-all 'numeric-index) #'< :key #'index-name))) (dolist (index indexes) - (let ((prev (1- (index-slug index))) - (next (1+ (index-slug index)))) + (let ((prev (1- (index-name index))) + (next (1+ (index-name index)))) (write-document index nil :prev (when (plusp prev) prev) :next (when (<= next (length indexes)) next)))))) diff --git a/themes/atom.tmpl b/themes/atom.tmpl index 6061503..52e4ee4 100644 --- a/themes/atom.tmpl +++ b/themes/atom.tmpl @@ -14,7 +14,7 @@ {foreach $post in $content.content} - + {$post.title} {$post.date} {$post.date} diff --git a/themes/hyde/index.tmpl b/themes/hyde/index.tmpl index 954eeb8..326e517 100644 --- a/themes/hyde/index.tmpl +++ b/themes/hyde/index.tmpl @@ -4,20 +4,20 @@

{$index.title}

{foreach $obj in $index.content} {/foreach}
- {if $prev} Previous {/if} - {if $next} Next {/if} + {if $prev} Previous {/if} + {if $next} Next {/if}
{if $tags}

This blog covers {foreach $tag in $tags} - {$tag.name}{nil} + {$tag.name}{nil} {if not isLast($tag)},{sp}{/if} {/foreach}

@@ -26,7 +26,7 @@

View content from {foreach $month in $months} - {$month}{nil} + {$month.name}{nil} {if not isLast($month)},{sp}{/if} {/foreach}

diff --git a/themes/hyde/post.tmpl b/themes/hyde/post.tmpl index d66e8e9..f7b81bc 100644 --- a/themes/hyde/post.tmpl +++ b/themes/hyde/post.tmpl @@ -6,7 +6,7 @@
{\n} {if $post.tags} Tagged as {foreach $tag in $post.tags} - {$tag.name}{nil} + {$tag.name}{nil} {if not isLast($tag)},{sp}{/if} {/foreach} {/if} @@ -21,7 +21,7 @@ {$post.text |noAutoescape}
{\n}
{\n} - {if $prev} Previous
{/if}{\n} - {if $next} Next
{/if}{\n} + {if $prev} Previous
{/if}{\n} + {if $next} Next
{/if}{\n}
{\n} {/template} diff --git a/themes/readable/index.tmpl b/themes/readable/index.tmpl index 5cf5514..1061f93 100644 --- a/themes/readable/index.tmpl +++ b/themes/readable/index.tmpl @@ -4,7 +4,7 @@

{$index.title}

{foreach $obj in $index.content}
-

{$obj.title}

+

{$obj.title}

posted on {$obj.date}

{$obj.text |noAutoescape}
@@ -13,7 +13,7 @@

This blog covers {foreach $tag in $tags} - {$tag.name}{nil} + {$tag.name}{nil} {if not isLast($tag)},{sp}{/if} {/foreach}

@@ -23,7 +23,7 @@

View content from {foreach $month in $months} - {$month}{nil} + {$month.name}{nil} {if not isLast($month)},{sp}{/if} {/foreach}

diff --git a/themes/readable/post.tmpl b/themes/readable/post.tmpl index 76bb24a..051750b 100644 --- a/themes/readable/post.tmpl +++ b/themes/readable/post.tmpl @@ -6,7 +6,7 @@

{if $post.tags} Tagged as {foreach $tag in $post.tags} - {$tag.name}{nil} + {$tag.name}{nil} {if not isLast($tag)},{sp}{/if} {/foreach} {/if} @@ -20,8 +20,8 @@ {$post.text |noAutoescape}

{\n} {/template} diff --git a/themes/rss.tmpl b/themes/rss.tmpl index bcd4460..7a4ecb5 100644 --- a/themes/rss.tmpl +++ b/themes/rss.tmpl @@ -13,10 +13,10 @@ {foreach $post in $content.content} {$post.title} - {$config.domain}/{$post.path} + {$config.domain}/{$post.url} {$post.date} {$config.author} - {$config.domain}/{$post.path} + {$config.domain}/{$post.url} {foreach $tag in $post.tags} {/foreach} From 702aaf5b4bfb2e45785fe4794f62a3be32eff2a4 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 27 Aug 2014 14:45:08 -0400 Subject: [PATCH 15/44] Closure Template/Esrap's error reporting leaves something to be desired. --- themes/hyde/index.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/themes/hyde/index.tmpl b/themes/hyde/index.tmpl index 326e517..31a4fce 100644 --- a/themes/hyde/index.tmpl +++ b/themes/hyde/index.tmpl @@ -17,7 +17,7 @@

This blog covers {foreach $tag in $tags} - {$tag.name}{nil} + {$tag.name}{nil} {if not isLast($tag)},{sp}{/if} {/foreach}

From 78f1fc9fa05c4134e8fe9cd2069350da8ebf6458 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 27 Aug 2014 14:46:14 -0400 Subject: [PATCH 16/44] Fix sitemap plugin breaking in INDEX :after method. It's a one off. --- plugins/sitemap.lisp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/sitemap.lisp b/plugins/sitemap.lisp index a869c89..ea4a0ea 100644 --- a/plugins/sitemap.lisp +++ b/plugins/sitemap.lisp @@ -13,7 +13,7 @@ (in-package :coleslaw-sitemap) -(defclass sitemap (index) +(defclass sitemap () ((urls :initarg :urls :reader urls))) (defmethod page-url ((object sitemap)) "sitemap.xml") From b3acf878e14dadc8ae771962fec5fc5e9b2bb519 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 27 Aug 2014 14:47:02 -0400 Subject: [PATCH 17/44] Update TODO notes in Hacking. --- docs/hacking.md | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index 8979d4f..cb6dc67 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -219,34 +219,13 @@ PAGE, a content type for static page support, is available as a plugin. ## Areas for Improvement -### Unified Routing +### Minor Cleanups/Tasks -Right now, the templates break if you use a custom routing scheme -in your config. This is flatly ridiculous. The templates should be -updated to use a url field stored on each object which will store -the path of the object. This can be done transparently to users, -though must be handled with care and may involve refactoring -in the document protocol. Test carefully. If we cheat by hardcoding -the sitemap and/or feeds, that's probably okay for the moment. - -Turns out this is even messier than I thought. Links are built from -scratch in the templates *all over the place*. Tags in posts, -taglinks and monthlinks in indexes, and prev/next links in numeric -indexes. I'm also doing two `find-all` calls in the base `render` -method for index. So I should profile and/or memoize that if needed. - -We should also not have a **slug** _and_ **path** slot due to this. -Currently, we track uniqueness in the site by using a hashtable -keyed on `page-url`, i.e. relative paths. The paths are mostly -constructed from slugs. Sometimes we use the slugs for sorting -or other purposes. We should be able to build the paths at instance -creation time and scrap the slugs altogether. Note that this will -require changes to how we sort `NUMERIC-INDEX`. - -### Finish up Basic Deploy - -The deploy method has been simplified and a git-hook plugin added. -Documentation and testing for the other deploy plugins is next. +* Try to get tag-index urls out of the tags. Post templates use them. +* Profile/memoize find-all calls in **INDEX** `render` method. +* Test basic deploy, ensure links from refactor work. +* Lots of docs updates for next release. At a minimum, we have broken + custom themes. Config/plugin changes are definitely necessary too. ### Deprecate :repo config option? From b23a398dd5adb35ec4b1cb765b51f0ec928d2592 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 27 Aug 2014 15:45:29 -0400 Subject: [PATCH 18/44] Assorted bugfixes and cleanups. --- src/coleslaw.lisp | 3 +-- src/feeds.lisp | 7 +++---- src/indexes.lisp | 8 ++------ 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/src/coleslaw.lisp b/src/coleslaw.lisp index 36642ea..e50df1d 100644 --- a/src/coleslaw.lisp +++ b/src/coleslaw.lisp @@ -41,8 +41,7 @@ in REPO-DIR. Optionally, OLDREV is the revision prior to the last push." (defgeneric deploy (staging) (:documentation "Deploy the STAGING build to the directory specified in the config.") (:method (staging) - (let ((destination (deploy-dir *config*))) - (run-program "rsync --delete -avz ~a ~a" staging destination)))) + (run-program "mv ~a ~a" staging (deploy-dir *config*)))) (defun update-symlink (path target) "Update the symlink at PATH to point to TARGET." diff --git a/src/feeds.lisp b/src/feeds.lisp index 7d273fc..a0aaeef 100644 --- a/src/feeds.lisp +++ b/src/feeds.lisp @@ -2,10 +2,9 @@ ;;; Atom and RSS Feeds -(defclass feed (index) - ((format :initform nil :initarg :format :reader feed-format))) +(defclass feed () ((format :initarg :format :reader feed-format))) -(defclass standard-feed (feed) ()) +(defclass standard-feed (index feed) ()) (defmethod discover ((doc-type (eql (find-class 'standard-feed)))) (let ((content (by-date (find-all 'post)))) @@ -21,7 +20,7 @@ ;;; Tag Feeds -(defclass tag-feed (feed) ()) +(defclass tag-feed (index feed) ()) (defmethod discover ((doc-type (eql (find-class 'tag-feed)))) (let ((content (by-date (find-all 'post)))) diff --git a/src/indexes.lisp b/src/indexes.lisp index 40b295c..6cbf056 100644 --- a/src/indexes.lisp +++ b/src/indexes.lisp @@ -79,12 +79,8 @@ (defmethod publish ((doc-type (eql (find-class 'numeric-index)))) (let ((indexes (sort (find-all 'numeric-index) #'< :key #'index-name))) - (dolist (index indexes) - (let ((prev (1- (index-name index))) - (next (1+ (index-name index)))) - (write-document index nil - :prev (when (plusp prev) prev) - :next (when (<= next (length indexes)) next)))))) + (loop for (next index prev) on (append '(nil) indexes) + while index do (write-document index nil :prev prev :next next)))) ;;; Helper Functions From 148c1a0c529acd1719f820dee5f65f6f8f95951c Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 27 Aug 2014 15:59:35 -0400 Subject: [PATCH 19/44] Switch back to using rsync --delete -avz in deploy. --- docs/hacking.md | 11 ++++------- src/coleslaw.lisp | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index cb6dc67..f71c019 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -223,15 +223,9 @@ PAGE, a content type for static page support, is available as a plugin. * Try to get tag-index urls out of the tags. Post templates use them. * Profile/memoize find-all calls in **INDEX** `render` method. -* Test basic deploy, ensure links from refactor work. * Lots of docs updates for next release. At a minimum, we have broken custom themes. Config/plugin changes are definitely necessary too. - -### Deprecate :repo config option? - -Coleslaw must at this point *always* be called with `coleslaw:main` -passing the repo directory as an argument. Given this, there is no -need for users to specify a repo in their config. + Note that the `:repo` config option is ignored now/sourced from `main`. ### Real Error Handling @@ -255,6 +249,9 @@ that are useful to the user. Example errors users have encountered: i.e. Specifying the `:repo` in .coleslawrc as the bare repo. The README should clarify this point and the need for posts to be ".post" files. +4. Dear Lord it was miserable even debugging a transposed character error + in one of the templates. "${foo}" instead of "{$foo}". But fuck supporting + multiple templating backends I have enough problems. ### Scripting Conveniences/Basic Install diff --git a/src/coleslaw.lisp b/src/coleslaw.lisp index e50df1d..d3f5d98 100644 --- a/src/coleslaw.lisp +++ b/src/coleslaw.lisp @@ -41,7 +41,7 @@ in REPO-DIR. Optionally, OLDREV is the revision prior to the last push." (defgeneric deploy (staging) (:documentation "Deploy the STAGING build to the directory specified in the config.") (:method (staging) - (run-program "mv ~a ~a" staging (deploy-dir *config*)))) + (run-program "rsync --delete -avz ~a ~a" staging (deploy-dir *config*)))) (defun update-symlink (path target) "Update the symlink at PATH to point to TARGET." From 7b516ccc9ccf2770588d3ce5017488a487cec17a Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 27 Aug 2014 16:07:13 -0400 Subject: [PATCH 20/44] A few notes tweaks. --- docs/hacking.md | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index f71c019..401ae40 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -219,7 +219,7 @@ PAGE, a content type for static page support, is available as a plugin. ## Areas for Improvement -### Minor Cleanups/Tasks +### Assorted Cleanups * Try to get tag-index urls out of the tags. Post templates use them. * Profile/memoize find-all calls in **INDEX** `render` method. @@ -251,26 +251,16 @@ that are useful to the user. Example errors users have encountered: ".post" files. 4. Dear Lord it was miserable even debugging a transposed character error in one of the templates. "${foo}" instead of "{$foo}". But fuck supporting - multiple templating backends I have enough problems. + multiple templating backends I have enough problems. What can we do? -### Scripting Conveniences/Basic Install +### Scripting Conveniences -Right now, we assume that you want to set up a bare repo and use git push -for site deploys. However good a system this may be, we don't need to have -it as a baked in assumption for coleslaw. The current deploy system should -be moved into a plugin and replaced with something braindead -(e.g. `mv` staging-dir to deploy-dir). - -This leads to the following: - -1. Simplify the getting started process for new users. - They should just be able to do +1. The getting started process has been simplified for new users. + They are able to just place a config in $HOME or their repo and do `(progn (ql:quickload :coleslaw) (coleslaw:main "/path/to/my/blog-repo"))`. - We'll still require a git repo for now. This does necessitate updating - `get-updated-files` for the case where no revision is passed. -2. We could also add command-line tools/scripts to run coleslaw, set up +2. We may also add command-line tools/scripts to run coleslaw, set up the db for incremental builds, scaffold a new post, etc. for new users. Xach's buildapp or cl-launch would be useful here. frog and hakyll are good points of inspiration as well. From 2bd647207a5e2624e1c2183c258a6900f57c9a78 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 29 Aug 2014 12:36:52 -0400 Subject: [PATCH 21/44] Some 0.9.6 preparations. --- NEWS.md | 21 +++++++++++++++++++ docs/hacking.md | 25 +++++++++++++++++++---- examples/example.coleslawrc | 9 ++++---- plugins/{git-hook.lisp => versioned.lisp} | 4 ++-- 4 files changed, 49 insertions(+), 10 deletions(-) rename plugins/{git-hook.lisp => versioned.lisp} (91%) diff --git a/NEWS.md b/NEWS.md index aa0b152..d1e6886 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,24 @@ +## Changes for 0.9.6 (2014-09-07): + +* **SITE-BREAKING CHANGE**: Coleslaw now defaults to a "basic" deploy + instead of the previous symlinked, timestamped deploy strategy. + To retain the previous behavior, add `(versioned)` to your config's + `:plugins` list. +* **SITE-BREAKING CHANGE**: Custom themes will be broken by a change + to URL handling. Previously, we were hand-constructed URLs in the + templates. All site objects now store their URL in an instance slot. + In general, hrefs should be of the form `href="{$config.domain}/{$obj.url}"> ...`. + Additionally, `{$month}` should become `{$month.name}`. + Correspondingly, changes to `:routing` would previously break links in + the templates but now work seamlessly. +* The config `:repo` option is now deprecated as its value has become + a required argument to `coleslaw:main`. The value passed to `main` + will override the config value going forward. +* Improved handling of directories and error-reporting when they + don't exist is available thanks to @PuercoPop. +* The templates are now HTML5 valid thanks to @Ferada. +* Fixed a bug where RSS/Atom tag feeds were being published multiple times. + ## Changes for 0.9.5 (2014-06-13): * A plugin for Incremental builds, cutting runtime for generating diff --git a/docs/hacking.md b/docs/hacking.md index 401ae40..9d31492 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -219,13 +219,24 @@ PAGE, a content type for static page support, is available as a plugin. ## Areas for Improvement +### TODO for 0.9.6 + +* Serious updates to README and themes.md. Skim hacking.md but it's probably okay. +* A real test suite with Stefil that at *least* tests: + * `read-content` + * `load-config` + * Content Discovery + * Theme Compilation + * Content Publishing + * Common Plugins including Injections +* Merge @PuercoPop's twitter summary cards plugin in some form. +* Make read-content/load-content more robust. Improving template debugging will be part of 0.9.7. +* Some minor scripting conveniences with cl-launch? (Scaffold a post/page, Enable incremental, Build, etc). + ### Assorted Cleanups * Try to get tag-index urls out of the tags. Post templates use them. * Profile/memoize find-all calls in **INDEX** `render` method. -* Lots of docs updates for next release. At a minimum, we have broken - custom themes. Config/plugin changes are definitely necessary too. - Note that the `:repo` config option is ignored now/sourced from `main`. ### Real Error Handling @@ -237,7 +248,7 @@ at best, especially for anyone not coming from the lisp world. We need to start handling errors and reporting errors in ways that are useful to the user. Example errors users have encountered: -1. Loading of Content. If `read-content` fails to parse a file, we +1. Loading of Content. If `read-content` fails to parse a file, wen should tell the user what file failed and why. We also should probably enforce more constraints about metadata. E.g. Empty metadata is not allowed/meaningful. Trailing space after separator, etc. @@ -279,6 +290,11 @@ some assertions and error out at plugin load time if they fail should be doable. I might not be able to [make illegal states unrepresentable][misu], but I can sure as hell make them harder to construct than they are now. +@PuercoPop has suggested looking into how [wookie does plugins][wookie]. +It's much more heavyweight but might be worth looking into. If we go that +route, the plugin support code will be almost half the coleslaw core. +Weigh the tradeoffs carefully. + ### New Content Type: Shouts! I've also toyed with the idea of a content type called a SHOUT, which @@ -319,3 +335,4 @@ Unfortunately, this does not solve: [asdf3]: https://github.com/fare/asdf3-2013 [qpq]: https://github.com/sellout/quid-pro-quo [misu]: https://blogs.janestreet.com/effective-ml-revisited/ +[wookie]: https://github.com/orthecreedence/wookie/blob/master/plugin.lisp#L181 diff --git a/examples/example.coleslawrc b/examples/example.coleslawrc index fb18145..6a3691a 100644 --- a/examples/example.coleslawrc +++ b/examples/example.coleslawrc @@ -4,11 +4,12 @@ :feeds ("lisp") :plugins ((analytics :tracking-code "foo") (disqus :shortname "my-site-name") - ; (git-hook) ;; *Remove comment to enable git push deploys. - ; (incremental) ;; *Remove comment enable incremental builds. + ; (incremental) ;; *Remove comment to enable incremental builds. (mathjax) (sitemap) - (static-pages)) + (static-pages) + ; (versioned) ;; *Remove comment to enable symlinked, timestamped deploys. + ) :routing ((:post "posts/~a") (:tag-index "tag/~a") (:month-index "date/~a") @@ -24,4 +25,4 @@ :title "Improved Means for Achieving Deteriorated Ends" :theme "hyde") -;; * Prerequisites described in plugin docs. \ No newline at end of file +;; * Prerequisites described in plugin docs. diff --git a/plugins/git-hook.lisp b/plugins/versioned.lisp similarity index 91% rename from plugins/git-hook.lisp rename to plugins/versioned.lisp index a27d6d4..cbd781f 100644 --- a/plugins/git-hook.lisp +++ b/plugins/versioned.lisp @@ -1,4 +1,4 @@ -(defpackage :coleslaw-git-hook +(defpackage :coleslaw-versioned (:use :cl) (:import-from :coleslaw #:*config* #:deploy-dir @@ -6,7 +6,7 @@ #:run-program #:update-symlink)) -(in-package :coleslaw-git-hook) +(in-package :coleslaw-versioned) (defmethod coleslaw:deploy (staging) (let* ((dest (deploy-dir *config*)) From 2ed12c5bce861ecb3fd5964f719fc48ab7b20cc2 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 3 Sep 2014 14:34:53 -0400 Subject: [PATCH 22/44] Factor out parse-metadata in preparation for real error-handling. --- docs/hacking.md | 2 +- src/content.lisp | 34 +++++++++++++++++++--------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index 9d31492..8e32cc5 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -248,7 +248,7 @@ at best, especially for anyone not coming from the lisp world. We need to start handling errors and reporting errors in ways that are useful to the user. Example errors users have encountered: -1. Loading of Content. If `read-content` fails to parse a file, wen +1. Loading of Content. If `read-content` fails to parse a file, we should tell the user what file failed and why. We also should probably enforce more constraints about metadata. E.g. Empty metadata is not allowed/meaningful. Trailing space after separator, etc. diff --git a/src/content.lisp b/src/content.lisp index 7b40945..2033e48 100644 --- a/src/content.lisp +++ b/src/content.lisp @@ -48,27 +48,31 @@ (when (stringp tags) (setf tags (mapcar #'make-tag (cl-ppcre:split "," tags)))))) +(defun parse-metadata (stream) + "Given a STREAM, parse metadata from it or signal an appropriate condition." + (flet ((parse-field (str) + (nth-value 1 (cl-ppcre:scan-to-strings "[a-zA-Z]+:\\s+(.*)" str))) + (field-name (line) + (make-keyword (string-upcase (subseq line 0 (position #\: line)))))) + (unless (string= (read-line stream nil) (separator *config*)) + (error "The provided file lacks the expected header.")) + (loop for line = (read-line stream nil) + until (string= line (separator *config*)) + appending (list (field-name line) + (aref (parse-field line) 0))))) + (defun read-content (file) - "Returns a plist of metadata from FILE with :text holding the content as a string." + "Returns a plist of metadata from FILE with :text holding the content." (flet ((slurp-remainder (stream) (let ((seq (make-string (- (file-length stream) (file-position stream))))) (read-sequence seq stream) - (remove #\Nul seq))) - (parse-field (str) - (nth-value 1 (cl-ppcre:scan-to-strings "[a-zA-Z]+:\\s+(.*)" str))) - (field-name (line) - (make-keyword (string-upcase (subseq line 0 (position #\: line)))))) + (remove #\Nul seq)))) (with-open-file (in file :external-format '(:utf-8)) - (unless (string= (read-line in) (separator *config*)) - (error "The provided file lacks the expected header.")) - (let ((meta (loop for line = (read-line in nil) - until (string= line (separator *config*)) - appending (list (field-name line) - (aref (parse-field line) 0)))) - (filepath (enough-namestring file (repo *config*))) - (content (slurp-remainder in))) - (append meta (list :text content :file filepath)))))) + (let ((metadata (parse-metadata in)) + (content (slurp-remainder in)) + (filepath (enough-namestring file (repo *config*)))) + (append metadata (list :text content :file filepath)))))) ;; Helper Functions From 78e0f9eceaeccba2df62233c436c1b4335973aef Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Wed, 3 Sep 2014 15:38:28 -0400 Subject: [PATCH 23/44] Split out some 0.9.7 items. --- docs/hacking.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index 8e32cc5..e44b5d8 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -222,15 +222,21 @@ PAGE, a content type for static page support, is available as a plugin. ### TODO for 0.9.6 * Serious updates to README and themes.md. Skim hacking.md but it's probably okay. +* Make read-content/load-content more robust. * A real test suite with Stefil that at *least* tests: * `read-content` * `load-config` +* Merge @PuercoPop's twitter summary cards plugin in some form. + +### TODO for 0.9.7 + +* Test suite improvements: * Content Discovery * Theme Compilation * Content Publishing * Common Plugins including Injections -* Merge @PuercoPop's twitter summary cards plugin in some form. -* Make read-content/load-content more robust. Improving template debugging will be part of 0.9.7. +* Improved template debugging? "${" instead of "{$", static checks for valid slots, etc. + At least a serious investigation into how such things might be provided. * Some minor scripting conveniences with cl-launch? (Scaffold a post/page, Enable incremental, Build, etc). ### Assorted Cleanups From 46e6915ba20a0dcae3e5e26474dca5d93e7edfee Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 5 Sep 2014 16:08:49 -0400 Subject: [PATCH 24/44] Make parse-metadata suck a little less. Many docs updates. --- README.md | 86 +++++++++++++++++++++++++++++++++++++----------- docs/config.md | 37 +++++++++++++++++++++ docs/hacking.md | 8 +++-- src/content.lisp | 22 ++++++++----- 4 files changed, 123 insertions(+), 30 deletions(-) create mode 100644 docs/config.md diff --git a/README.md b/README.md index 3265222..cdd0402 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Coleslaw is Flexible Lisp Blogware similar to [Frog](https://github.com/greghendershott/frog), [Jekyll](http://jekyllrb.com/), or [Hakyll](http://jaspervdj.be/hakyll/). ## Features + * Git for storage * RSS and Atom feeds * Markdown Support with Code Highlighting provided by [colorize](http://www.cliki.net/colorize) @@ -30,32 +31,68 @@ Coleslaw is Flexible Lisp Blogware similar to [Frog](https://github.com/greghend * There is also a [Heroku buildpack](https://github.com/jsmpereira/coleslaw-heroku) maintained by Jose Pereira. ## Example Sites - * [redlinernotes](http://redlinernotes.com/blog/) - * [chip the glasses](http://chiptheglasses.com) - * [kenan-bolukbasi.log](http://kenanb.com/) - * [Nothing Really Matters](http://ironhead.xs4all.nl/) - * [A year and a smile](http://blog.sjas.de) + +See the [wiki][blogroll] for a list of coleslaw-powered blogs. + +[blogroll]: https://github.com/redline6561/coleslaw/wiki/Blog roll ## Hacking -A core goal of *coleslaw* is to be both pleasant to read and easy to hack on and extend. If you want to understand the internals and bend *coleslaw* to do new and interesting things, I strongly encourage you to read the [Hacker's Guide to Coleslaw](https://github.com/redline6561/coleslaw/blob/master/docs/hacking.md). +A core goal of *coleslaw* is to be both pleasant to read and easy to +hack on and extend. If you want to understand the internals and bend +*coleslaw* to do new and interesting things, I strongly encourage you +to read the [Hacker's Guide to Coleslaw][hackers]. You'll find some +current **TODO** items towards the bottom. + +[hackers]: https://github.com/redline6561/coleslaw/blob/master/docs/hacking.md ## Installation -This software should be portable to any conforming Common Lisp implementation but testing is primarily done on [SBCL](http://www.sbcl.org/) and [CCL](http://ccl.clozure.com/). -Server side setup: -1. Setup git and create a bare repo as shown [here](http://git-scm.com/book/en/Git-on-the-Server-Setting-Up-the-Server). -2. Install Lisp (we recommend SBCL) and [Quicklisp](http://quicklisp.org/). -3. ```wget -c https://raw.github.com/redline6561/coleslaw/master/examples/example.coleslawrc -O ~/.coleslawrc``` # and edit as necessary -4. ```wget -c https://raw.github.com/redline6561/coleslaw/master/examples/example.post-receive -O your-blog.git/hooks/post-receive``` # and edit as necessary -5. ```chmod +x your-blog/.git/hooks/post-receive``` -6. Create or clone your blog repo locally. Add your server as a remote with ```git remote add prod git@my-host.com:path/to/repo.git``` -7. Point the web server of your choice at the symlink /path/to/deploy-dir/.curr/ +Coleslaw should run on any conforming Common Lisp implementation but +testing is primarily done on [SBCL](http://www.sbcl.org/) and +[CCL](http://ccl.clozure.com/). -Now whenever you push a new commit to the server, coleslaw will update your blog automatically! You may need to `git push -u prod master` the first time. +Coleslaw can either be run **manually** on a local machine or +triggered **automatically** on git push to a server. If you want a +server install, run these commands on your server _after_ setting up a +[git bare repo](http://git-scm.com/book/en/Git-on-the-Server-Setting-Up-the-Server). +Otherwise, run the commands on your local machine. + +1. Install a Common Lisp implementation (we recommend SBCL) and + [Quicklisp](http://quicklisp.org/). +2. Place a config file for coleslaw in your `$HOME` directory. If you + want to run multiple blogs with coleslaw, you can keep each blog's + config file in that blog's repo. Feel free to copy and edit the + [example config][ex_config] or consult the [config docs][conf_docs] + to create one from scratch. +3. * Server Install: Copy and `chmod +x` the + [example post-receive hook][post_hook] to your blog's bare repo. + * Local Install: Just run the following commands in the + REPL whenever you're ready to regenerate your blog: + ``` + (ql:quickload :coleslaw) + ;; Note that the incremental plugin currently requires a "last git revision" argument. + (coleslaw:main "/path/to/my/blog/") + ``` +4. Optionally, point the web server of your liking at your config-specified + `:deploy-dir`. Or "deploy-dir/.curr" if the `versioned` plugin is enabled. + +Now just write posts, git commit and build by hand or by push. + +[ex_config]: https://github.com/redline6561/coleslaw/blob/master/examples/example.coleslawrc +[conf_docs]: https://github.com/redline6561/coleslaw/blob/master/docs/config.md +[post_hook]: https://github.com/redline6561/coleslaw/blob/master/examples/example.post-receive + +## The Content Format + +Coleslaw expects content to have a file extension matching the class +of the content. (I.e. `.post` for blog posts, `.page` for static +pages, etc.) + +There should also be a metadata header on all files +starting and ending with the config-specified `:separator`, ";;;;;" by +default. Example: -## The Post Format -Coleslaw expects post files to be formatted as follows: ``` ;;;;; title: foo @@ -66,5 +103,16 @@ format: html (for raw html) or md (for markdown) your post ``` +Posts require the `title:` and `format:` fields. +Pages require the `title:` and `url:` fields. + +To omit a field, simply do not have the line present, empty lines and +fields (e.g. "tags:" followed by whitespace) will be ignored. + ## Theming -Two themes are provided: hyde and readable (based on [bootswatch readable](http://bootswatch.com/readable/)). Hyde is the default. A guide to creating themes for coleslaw lives [here](https://github.com/redline6561/coleslaw/blob/master/docs/themes.md). + +Two themes are provided: hyde, the default, and readable (based on +[bootswatch readable](http://bootswatch.com/readable/)). + +A guide to creating themes for coleslaw lives +[here](https://github.com/redline6561/coleslaw/blob/master/docs/themes.md). diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 0000000..d2925c2 --- /dev/null +++ b/docs/config.md @@ -0,0 +1,37 @@ +# Configuration + +## Where + +Coleslaw needs a `.coleslawrc` file to operate properly. That file is usually located at +$HOME/.coleslawrc but may also be placed in the blog repo itself. + +## What + +The only *required* information in the config is: +* `:author` => to be placed on post pages and in the copyright/CC-BY-SA notice +* `:deploy-dir` => for Coleslaw's generated HTML to go in +* `:domain` => to generate absolute links to the site content +* `:routing` => to determine the URL scheme of content on the site +* `:title` => to provide a site title +* `:theme` => to select one of the themes in "coleslaw/themes/" + +**TODO** Make sure this is actually true. Maybe without `:feeds` the publish method blows up? Test! + +It is usually recommend to start from the [example config][ex_config] and pare down from there. + +[ex_config]: https://github.com/redline6561/coleslaw/blob/master/examples/example.coleslawrc + +## Extras + +There are also many *optional* config parameters such as: +* `:charset` => to set HTML attributes for international characters, default: "UTF-8" +* `:feeds` => to generate RSS and Atom feeds for certain tagged content +* `:lang` => to set HTML attributes indicating the site language, default: "en" +* `:license` => to override the displayed content license, the default is CC-BY-SA +* `:page-ext` => to set the suffix of generated files, default: "html" +* `:plugins` => to configure and enable coleslaw's [various plugins][plugin-use] +* `:separator` => to set the separator for content metadata, default: ";;;;;" +* `:sitenav` => to provide relevant links and ease navigation +* `:staging-dir` => for Coleslaw to do intermediate work, default: "/tmp/coleslaw" + +[plugin-use]: https://github.com/redline6561/coleslaw/blob/master/docs/plugin-use.md diff --git a/docs/hacking.md b/docs/hacking.md index e44b5d8..4c5b91e 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -221,8 +221,11 @@ PAGE, a content type for static page support, is available as a plugin. ### TODO for 0.9.6 -* Serious updates to README and themes.md. Skim hacking.md but it's probably okay. -* Make read-content/load-content more robust. +* Finish docs updates to: + * themes.md -> Note debugging issues. Valid slots. Changed URL handling. + * plugin-use.md -> Incremental plugin, Versioned plugin. Twitter summary cards. + * hacking.md -> Any changes to Document Protocol? + * config.md -> Test various empty config slots. * A real test suite with Stefil that at *least* tests: * `read-content` * `load-config` @@ -235,6 +238,7 @@ PAGE, a content type for static page support, is available as a plugin. * Theme Compilation * Content Publishing * Common Plugins including Injections +* Add proper errors to read-content/load-content. Not just ignoring bad data. Line info, etc. * Improved template debugging? "${" instead of "{$", static checks for valid slots, etc. At least a serious investigation into how such things might be provided. * Some minor scripting conveniences with cl-launch? (Scaffold a post/page, Enable incremental, Build, etc). diff --git a/src/content.lisp b/src/content.lisp index 2033e48..8c1d080 100644 --- a/src/content.lisp +++ b/src/content.lisp @@ -50,16 +50,20 @@ (defun parse-metadata (stream) "Given a STREAM, parse metadata from it or signal an appropriate condition." - (flet ((parse-field (str) - (nth-value 1 (cl-ppcre:scan-to-strings "[a-zA-Z]+:\\s+(.*)" str))) - (field-name (line) - (make-keyword (string-upcase (subseq line 0 (position #\: line)))))) - (unless (string= (read-line stream nil) (separator *config*)) - (error "The provided file lacks the expected header.")) - (loop for line = (read-line stream nil) + (labels ((get-next-line (input) + (string-trim '(#\Space #\Newline #\Tab) (read-line input nil))) + (parse-value (str) + (nth-value 1 (cl-ppcre:scan-to-strings "[a-zA-Z]+:\\s+(.*)" str))) + (parse-initarg-name (line) + (make-keyword (string-upcase (subseq line 0 (position #\: line))))) + (extract-initarg (line) + (list (parse-initarg-name line) (aref (parse-value line) 0)))) + (unless (string= (get-next-line stream) (separator *config*)) + (error "The file lacks the expected header: ~a" (separator *config*))) + (loop for line = (get-next-line stream) until (string= line (separator *config*)) - appending (list (field-name line) - (aref (parse-field line) 0))))) + when (parse-value line) + appending (extract-initarg line)))) (defun read-content (file) "Returns a plist of metadata from FILE with :text holding the content." From 439f3f35f7ac3047a1fccdd25b93a383b8b43015 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 5 Sep 2014 16:51:08 -0400 Subject: [PATCH 25/44] Push closer to 0.9.6. Just tests and a merge left now... --- NEWS.md | 5 ++- docs/config.md | 2 -- docs/hacking.md | 86 +++++++++++++++++++++---------------------------- src/config.lisp | 4 +++ 4 files changed, 45 insertions(+), 52 deletions(-) diff --git a/NEWS.md b/NEWS.md index d1e6886..5b119b3 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,12 +5,15 @@ To retain the previous behavior, add `(versioned)` to your config's `:plugins` list. * **SITE-BREAKING CHANGE**: Custom themes will be broken by a change - to URL handling. Previously, we were hand-constructed URLs in the + to URL handling. Previously, we were hand-constructing URLs in the templates. All site objects now store their URL in an instance slot. In general, hrefs should be of the form `href="{$config.domain}/{$obj.url}"> ...`. Additionally, `{$month}` should become `{$month.name}`. Correspondingly, changes to `:routing` would previously break links in the templates but now work seamlessly. +* **Docs**: Improved README. New Config File docs. +* Loading content is more robust when empty lines or metadata are passed. + Thanks to @PuercoPop for the bug report and preliminary fix. * The config `:repo` option is now deprecated as its value has become a required argument to `coleslaw:main`. The value passed to `main` will override the config value going forward. diff --git a/docs/config.md b/docs/config.md index d2925c2..5814c57 100644 --- a/docs/config.md +++ b/docs/config.md @@ -15,8 +15,6 @@ The only *required* information in the config is: * `:title` => to provide a site title * `:theme` => to select one of the themes in "coleslaw/themes/" -**TODO** Make sure this is actually true. Maybe without `:feeds` the publish method blows up? Test! - It is usually recommend to start from the [example config][ex_config] and pare down from there. [ex_config]: https://github.com/redline6561/coleslaw/blob/master/examples/example.coleslawrc diff --git a/docs/hacking.md b/docs/hacking.md index 4c5b91e..9d5c47c 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -38,21 +38,6 @@ reduced runtime to 1.36 seconds, almost cutting it in half. ## Core Concepts -### Data and Deployment - -**Coleslaw** is pretty fundamentally tied to the idea of git as both a -backing data store and a deployment method (via `git push`). The -consequence is that you need a bare repo somewhere with a post-recieve -hook. That post-recieve hook ([example][post_receive_hook]) -will checkout the repo to a **$TMPDIR** and call `(coleslaw:main $TMPDIR)`. - -It is then coleslaw's job to load all of your content, your config and -templates, and render the content to disk. Deployment is done by -moving the files to a location specified in the config and updating a -symlink. It is assumed a web server is set up to serve from that -symlink. However, there are plugins for deploying to Heroku, S3, and -Github Pages. - ### Plugins **Coleslaw** strongly encourages extending functionality via plugins. @@ -79,8 +64,22 @@ and return rendered HTML. **Coleslaw** defines a helper called there are RSS, ATOM, and sitemap templates *coleslaw* uses automatically. No need for individual themes to reimplement a standard, after all! +Unfortunately, it is not very pleasant to debug broken templates. +Efforts to remedy this are being pursued for the next release. +Two particular issues to note are transposed Closure commands, +e.g. "${foo}" instead of "{$foo}", and trying to use nonexistent +keys or slots which fails silently instead of producing an error. + ### The Lifecycle of a Page +- `(progn + (load-config "/my/blog/repo/path") + (compile-theme (theme *config*)))` + +Coleslaw first needs the config loaded and theme compiled, +as neither the blog location, the theme to use, and other +crucial information are not yet known. + - `(load-content)` A page starts, obviously, with a file. When *coleslaw* loads your @@ -102,10 +101,10 @@ reverse-chronological index. - `(deploy dir)` -Finally, we move the staging directory to a timestamped path under the -the config's `:deploy-dir`, delete the directory pointed to by the old -'.prev' symlink, point '.curr' at '.prev', and point '.curr' at our -freshly built site. +Finally, we move the staging directory to a path under the config's +`:deploy-dir`. If the versioned plugin is enabled, it is a timestamped +path and we delete the directory pointed to by the old '.prev' symlink, +point '.curr' at '.prev', and point '.curr' at our freshly built site. ### Blogs vs Sites @@ -116,13 +115,12 @@ INDEXes. Roughly speaking, a POST is a blog entry and an INDEX is a collection of POSTs or other content. An INDEX really only serves to group a set of content objects on a page, it isn't content itself. -This isn't ideal if you're looking for a full-on static site -generator. Thankfully, Content Types were added in 0.8 as a step -towards making *coleslaw* suitable for more use cases. Any subclass of -CONTENT that implements the *document protocol* counts as a content -type. However, only POSTs are currently included in the basic INDEXes -since there isn't yet a formal relationship to determine which content -types should be included on which indexes. Users may easily implement +Content Types were added in 0.8 as a step towards making *coleslaw* +suitable for more use cases. Any subclass of CONTENT that implements +the *document protocol* counts as a content type. However, only POSTs +are currently included in the bundled INDEXes since there isn't yet a +formal relationship to determine which content types should be +included on which indexes. It is straightforward for users to implement their own dedicated INDEX for new Content Types. ### The Document Protocol @@ -169,16 +167,14 @@ eql-specializing on the class, e.g. **Instance Methods**: -- `page-url`: Generate a relative path for the object on the site, - usually sans file extension. If there is no extension, an :around - method adds "html" later. The `slug` slot on the instance is - conventionally used to hold a portion of the path that corresponds - to a unique Primary Key or Object ID. +- `page-url`: Retrieve the relative path for the object on the site. + The implementation of `page-url` is not fully specified. For most + content types, we compute and store the path on the instance at + initialization time making `page-url` just a reader method. - `render`: A method that calls the appropriate template with `theme-fn`, passing it any needed arguments and returning rendered HTML. - **Invariants**: - Any Content Types (subclasses of CONTENT) are expected to be stored in @@ -224,8 +220,6 @@ PAGE, a content type for static page support, is available as a plugin. * Finish docs updates to: * themes.md -> Note debugging issues. Valid slots. Changed URL handling. * plugin-use.md -> Incremental plugin, Versioned plugin. Twitter summary cards. - * hacking.md -> Any changes to Document Protocol? - * config.md -> Test various empty config slots. * A real test suite with Stefil that at *least* tests: * `read-content` * `load-config` @@ -262,29 +256,24 @@ that are useful to the user. Example errors users have encountered: should tell the user what file failed and why. We also should probably enforce more constraints about metadata. E.g. Empty metadata is not allowed/meaningful. Trailing space after separator, etc. -2. Custom themes that try to access non-existent properties of content - do not currently error. They just wind up returning whitespace. - When the theme compiles, we should alert the user to any obvious - issues with it. -3. Trying to load content from the bare repo instead of the clone. +2. Trying to load content from the bare repo instead of the clone. i.e. Specifying the `:repo` in .coleslawrc as the bare repo. The README should clarify this point and the need for posts to be ".post" files. +3. Custom themes that try to access non-existent properties of content + do not currently error. They just wind up returning whitespace. + When the theme compiles, we should alert the user to any obvious + issues with it. 4. Dear Lord it was miserable even debugging a transposed character error in one of the templates. "${foo}" instead of "{$foo}". But fuck supporting multiple templating backends I have enough problems. What can we do? ### Scripting Conveniences -1. The getting started process has been simplified for new users. - They are able to just place a config in $HOME or their repo and do - `(progn - (ql:quickload :coleslaw) - (coleslaw:main "/path/to/my/blog-repo"))`. -2. We may also add command-line tools/scripts to run coleslaw, set up - the db for incremental builds, scaffold a new post, etc. for new users. - Xach's buildapp or cl-launch would be useful here. frog and hakyll are - good points of inspiration as well. +It would be convenient to add command-line tools/scripts to run coleslaw, +set up the db for incremental builds, scaffold a new post, etc. for new users. +Xach's buildapp or Fare's cl-launch would be useful here. frog and hakyll are +reasonable points of inspiration for commands to offer. ### Plugin Constraints @@ -335,7 +324,6 @@ Unfortunately, this does not solve: Content Types it includes or the CONTENT which indexes it appears on is not yet clear. -[post_receive_hook]: https://github.com/redline6561/coleslaw/blob/master/examples/example.post-receive [closure_template]: https://github.com/archimag/cl-closure-template [api_docs]: https://github.com/redline6561/coleslaw/blob/master/docs/plugin-api.md [clmd]: https://github.com/gwkkwg/cl-markdown diff --git a/src/config.lisp b/src/config.lisp index 14e3dae..d73a98a 100644 --- a/src/config.lisp +++ b/src/config.lisp @@ -18,6 +18,10 @@ (theme :initarg :theme :reader theme) (title :initarg :title :reader title)) (:default-initargs + :feeds nil + :license nil + :plugins nil + :sitenav nil :charset "UTF-8" :lang "en" :page-ext "html" From 41186fb41c97259ef2a07319898ad15d32cca9ac Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 5 Sep 2014 17:05:43 -0400 Subject: [PATCH 26/44] Fixes to theming docs. --- NEWS.md | 2 +- docs/hacking.md | 1 - docs/themes.md | 45 ++++++++++++++++++++------------------------- 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/NEWS.md b/NEWS.md index 5b119b3..42bed3b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -11,7 +11,7 @@ Additionally, `{$month}` should become `{$month.name}`. Correspondingly, changes to `:routing` would previously break links in the templates but now work seamlessly. -* **Docs**: Improved README. New Config File docs. +* **Docs**: Improved README and Theming docs. New Config File docs. * Loading content is more robust when empty lines or metadata are passed. Thanks to @PuercoPop for the bug report and preliminary fix. * The config `:repo` option is now deprecated as its value has become diff --git a/docs/hacking.md b/docs/hacking.md index 9d5c47c..94abc47 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -218,7 +218,6 @@ PAGE, a content type for static page support, is available as a plugin. ### TODO for 0.9.6 * Finish docs updates to: - * themes.md -> Note debugging issues. Valid slots. Changed URL handling. * plugin-use.md -> Incremental plugin, Versioned plugin. Twitter summary cards. * A real test suite with Stefil that at *least* tests: * `read-content` diff --git a/docs/themes.md b/docs/themes.md index 6ab70d9..178dc93 100644 --- a/docs/themes.md +++ b/docs/themes.md @@ -32,7 +32,7 @@ Every page other than those in the `posts/` directory is an `index`. **Every** page uses the `base.tmpl` and fills in the content using either the `post` or `index` templates. No important logic should be -in *any* template, they are only used to give provide consistent layout. +in *any* template, they are only used to provide a consistent layout. * `base.tmpl` This template generates the outer shell of the HTML. It keeps a consistent look and feel for all pages in the blog. The @@ -64,7 +64,7 @@ simplest to either modify the existing default theme, `hyde`, or copy it in entirety and then tweak only the CSS of your new theme. A large amount of visual difference can be had with a minimum of (or no) template hacking. There is plenty of advice on CSS styling on the web. -I'm no expert but feel free to send pull requests modifying theme's +I'm no expert but feel free to send pull requests modifying a theme's CSS or improving this section, perhaps by recommending a CSS resource. ## Creating a Theme from Scratch (with code) @@ -114,10 +114,15 @@ The templating language is documented [elsewhere][clt]. However as a short primer: * Everything is output literally, except template commands. -* Template commands are enclosed in `{` and `}` +* Template commands are enclosed in `{` and `}`. * Variables, which are provided by coleslaw, can be referenced inside a template command. So to use a variable you have to say `{$variable}` or `{$variable.key}`. + **WARNING**: At present, cl-closure-template does not have great debugging. + If you typo this, e.g. `${variable}`, you will receive an *uninformative* + and apparently unrelated error. Also, attempted access of non-existent keys + fails silently. We are exploring options for making debugging easier in a + future release. * If statements are written as `{if ...} ... {else} ... {/if}`. Typical examples are: `{if $injections.body} ... {/if}` or `{if not isLast($link)} ... {/if}`. @@ -139,19 +144,16 @@ The variable that should be available to all templates is: #### Index Template Variables - **tags** A list containing all the tags, each with keys - `.name` and `.slug`. -- **months** A list of all months with posts as `yyyy-mm` strings. + `name` and `url`. +- **months** A list of all the content months, each with keys + `name` and `url`. - **index** This is the meat of the content. This variable has the following keys: - - `id`, the name of the page that will be rendered - `content`, a list of content (see below) - - `title`, a string title to display to the user -- **prev** If this index file is part of a chain, the `id` - of the previous index html in the chain. - If this is the first file, the value will be empty. -- **next** If this index file is part of a chain, the `id` - of the next index html in the chain. - If this is the last file, the value will be empty. + - `name`, a name to use in links or href tags + - `title`, a title to use in H1 or header tags +- **prev** Nil or the previous index with keys: `url` and `title`. +- **next** Nil or the next index with keys: `url` and `title`. #### Post Template Variable @@ -160,8 +162,8 @@ The variable that should be available to all templates is: - **post** All these variables are post objects. **prev** and **next** are the adjacent posts when put in chronological order. Each post has the following keys: - - `tags`, a list of tags (each with keys `name` and `slug`) - - `slug`, the slug of the post + - `url`, the relative url of the post + - `tags`, a list of tags (each with keys `name` and `url`) - `date`, the date of posting - `text`, the HTML of the post's body - `title`, the title of the post @@ -190,8 +192,8 @@ A simple `index.tmpl` looks like this: {namespace coleslaw.theme.trivial} {template index} {foreach $obj in $index.content} -

{$obj.title}

- {$obj.text |noAutoescape} +

{$object.title}

+ {$object.text |noAutoescape} {/foreach} {/template} ``` @@ -209,14 +211,7 @@ And a simple `post.tmpl` is similarly: All of the files are now populated with content. There are still no links between the pages so navigation is cumbersome but adding links is simple. -Good luck! - -## Note on adding links - -As mentioned earlier, most files have a file name which is a slug of -some sort. So if you want to create a link to a tag file you should -do something like this: -`{$tag.name}`. +Just do: `{$object.name}`. [clt]: https://developers.google.com/closure/templates/ [ovr]: https://github.com/redline6561/coleslaw/blob/master/docs/overview.md From bb2b6e49a4cfbd9b3970959237b69f2ba57b0c92 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 5 Sep 2014 17:10:51 -0400 Subject: [PATCH 27/44] README markdown tweaks. --- README.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index cdd0402..9e55d3e 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,7 @@ Coleslaw is Flexible Lisp Blogware similar to [Frog](https://github.com/greghend ## Example Sites -See the [wiki][blogroll] for a list of coleslaw-powered blogs. - -[blogroll]: https://github.com/redline6561/coleslaw/wiki/Blog roll +See the [wiki](https://github.com/redline6561/coleslaw/wiki/Blog roll) for a list of coleslaw-powered blogs. ## Hacking @@ -65,7 +63,8 @@ Otherwise, run the commands on your local machine. config file in that blog's repo. Feel free to copy and edit the [example config][ex_config] or consult the [config docs][conf_docs] to create one from scratch. -3. * Server Install: Copy and `chmod +x` the +3. This step depends on whether you're setting up a local or server install. + * Server Install: Copy and `chmod +x` the [example post-receive hook][post_hook] to your blog's bare repo. * Local Install: Just run the following commands in the REPL whenever you're ready to regenerate your blog: From 60302d5f471519896b99f8fe5128b0669af3f1cd Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 5 Sep 2014 17:11:33 -0400 Subject: [PATCH 28/44] Fix blogroll link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9e55d3e..e3a5a28 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Coleslaw is Flexible Lisp Blogware similar to [Frog](https://github.com/greghend ## Example Sites -See the [wiki](https://github.com/redline6561/coleslaw/wiki/Blog roll) for a list of coleslaw-powered blogs. +See the [wiki](https://github.com/redline6561/coleslaw/wiki/Blogroll) for a list of coleslaw-powered blogs. ## Hacking From b2082175e32930811d49aa4ec551f286e1d3994b Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 12 Sep 2014 16:21:50 -0400 Subject: [PATCH 29/44] Little more doc work, fix versioned plugin thinko. --- docs/hacking.md | 2 -- docs/plugin-use.md | 22 ++++++++++++++++------ plugins/versioned.lisp | 2 ++ 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index 94abc47..076fad1 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -217,8 +217,6 @@ PAGE, a content type for static page support, is available as a plugin. ### TODO for 0.9.6 -* Finish docs updates to: - * plugin-use.md -> Incremental plugin, Versioned plugin. Twitter summary cards. * A real test suite with Stefil that at *least* tests: * `read-content` * `load-config` diff --git a/docs/plugin-use.md b/docs/plugin-use.md index 7164c61..b614a1f 100644 --- a/docs/plugin-use.md +++ b/docs/plugin-use.md @@ -1,8 +1,8 @@ # General Use -* Add a list with the plugin name and settings to the ```:plugins``` - section of your [.coleslawrc][config_file]. Plugin settings are - described below. +* To enable a plugin, add its name and settings to your + [.coleslawrc][config_file]. Plugin settings are described + below. Note that some plugins require additional setup. * Available plugins are listed below with usage descriptions and config examples. @@ -41,9 +41,9 @@ **Example**: `(incremental)` -**Setup**: -- You must run the `examples/dump_db.sh` script to generate a database dump - for your site before enabling the incremental plugin. +**Setup**: You must run the `examples/dump_db.sh` script to + generate a database dump for your site before enabling the + incremental plugin. ## LaTeX via Mathjax @@ -144,6 +144,16 @@ CL-USER> (chirp:complete-authentication "4173325") # ``` +## Versioned Deploys + +**Description**: Originally, this was Coleslaw's only deploy behavior. + Instead of deploying directly to `:deploy-dir`, creates `.curr` and + `.prev` symlinks in the *deploy-dir*, which point to timestamped + directories of the last two deploys of the site. Deploys prior to the + last two are automatically cleaned up. + +**Example**: `(versioned)` + ## Wordpress Importer **NOTE**: This plugin really should be rewritten to act as a diff --git a/plugins/versioned.lisp b/plugins/versioned.lisp index cbd781f..4818318 100644 --- a/plugins/versioned.lisp +++ b/plugins/versioned.lisp @@ -20,3 +20,5 @@ (when (probe-file curr) (update-symlink prev (truename curr))) (update-symlink curr new-build))) + +(defun enable ()) From 81cfcc469e5d8722111b910e063b9cb2fea7f8dd Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 12 Sep 2014 16:22:34 -0400 Subject: [PATCH 30/44] New release target. --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index 42bed3b..047f0dd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,4 +1,4 @@ -## Changes for 0.9.6 (2014-09-07): +## Changes for 0.9.6 (2014-09-17): * **SITE-BREAKING CHANGE**: Coleslaw now defaults to a "basic" deploy instead of the previous symlinked, timestamped deploy strategy. From 3ae3c208f72ba7e4200f37a4ef1e9857c9842e71 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 12 Sep 2014 16:33:57 -0400 Subject: [PATCH 31/44] Remove the unused fiveam dependency, get started with stefil! --- coleslaw.asd | 5 ++--- tests/packages.lisp | 3 --- tests/tests.lisp | 13 ------------- 3 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 tests/packages.lisp delete mode 100644 tests/tests.lisp diff --git a/coleslaw.asd b/coleslaw.asd index 40ab023..1de39e7 100644 --- a/coleslaw.asd +++ b/coleslaw.asd @@ -31,11 +31,10 @@ (intern "COLESLAW-TESTS" :coleslaw-tests)))) (defsystem #:coleslaw-tests - :depends-on (coleslaw fiveam) + :depends-on (coleslaw stefil) :pathname "tests/" :serial t - :components ((:file "packages") - (:file "tests"))) + :components ()) (defmethod operation-done-p ((op test-op) (c (eql (find-system :coleslaw)))) diff --git a/tests/packages.lisp b/tests/packages.lisp deleted file mode 100644 index 6661770..0000000 --- a/tests/packages.lisp +++ /dev/null @@ -1,3 +0,0 @@ -(defpackage :coleslaw-tests - (:use :cl :fiveam) - (:export #:run!)) diff --git a/tests/tests.lisp b/tests/tests.lisp deleted file mode 100644 index 891c9c4..0000000 --- a/tests/tests.lisp +++ /dev/null @@ -1,13 +0,0 @@ -(in-package :coleslaw-tests) - -(defmacro deftest (name docstring &body body) - `(test ,name - ,docstring - ,@body)) - -(def-suite coleslaw-tests) -(in-suite coleslaw-tests) - -(deftest sanity-test - "A blog should compile and deploy correctly." - (is (zerop (coleslaw:main "")))) From 33e81104b851d8a3eaa2b013833b481aaa82c171 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Tue, 16 Sep 2014 11:30:14 -0400 Subject: [PATCH 32/44] Tweaks to NEWS, README. --- NEWS.md | 7 +++---- README.md | 5 +---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/NEWS.md b/NEWS.md index 047f0dd..e66d0b8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,10 +7,9 @@ * **SITE-BREAKING CHANGE**: Custom themes will be broken by a change to URL handling. Previously, we were hand-constructing URLs in the templates. All site objects now store their URL in an instance slot. - In general, hrefs should be of the form `href="{$config.domain}/{$obj.url}"> ...`. - Additionally, `{$month}` should become `{$month.name}`. - Correspondingly, changes to `:routing` would previously break links in - the templates but now work seamlessly. + In general, hrefs should be of the form ` ...`. +* Changes to `:routing` would previously break links in the templates + but now work seamlessly due to the updated URL handling. * **Docs**: Improved README and Theming docs. New Config File docs. * Loading content is more robust when empty lines or metadata are passed. Thanks to @PuercoPop for the bug report and preliminary fix. diff --git a/README.md b/README.md index e3a5a28..72bfb4b 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,12 @@ Coleslaw is Flexible Lisp Blogware similar to [Frog](https://github.com/greghend * Incremental builds * Analytics via Google * Comments via [Disqus](http://disqus.com/) - * Hosting via [Github Pages](https://pages.github.com/), [Heroku](http://heroku.com/), or [Amazon S3](http://aws.amazon.com/s3/) + * Hosting via [Github Pages](https://pages.github.com/) or [Amazon S3](http://aws.amazon.com/s3/) * [Tweeting](http://twitter.com/) about new posts * Using LaTeX via [Mathjax](http://mathjax.org/) * Writing posts in ReStructured Text * Importing posts from [Wordpress](http://wordpress.org/) -* There is also a [Heroku buildpack](https://github.com/jsmpereira/coleslaw-heroku) maintained by Jose Pereira. - ## Example Sites See the [wiki](https://github.com/redline6561/coleslaw/wiki/Blogroll) for a list of coleslaw-powered blogs. @@ -70,7 +68,6 @@ Otherwise, run the commands on your local machine. REPL whenever you're ready to regenerate your blog: ``` (ql:quickload :coleslaw) - ;; Note that the incremental plugin currently requires a "last git revision" argument. (coleslaw:main "/path/to/my/blog/") ``` 4. Optionally, point the web server of your liking at your config-specified From f474db77b2c2d75a41cef9c1f2885d91caf8a950 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Mon, 22 Sep 2014 12:56:00 -0400 Subject: [PATCH 33/44] Factor parse-initarg out of parse-metadata. --- src/content.lisp | 20 ++++++++++---------- src/packages.lisp | 1 + 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/content.lisp b/src/content.lisp index 8c1d080..0774ead 100644 --- a/src/content.lisp +++ b/src/content.lisp @@ -48,22 +48,22 @@ (when (stringp tags) (setf tags (mapcar #'make-tag (cl-ppcre:split "," tags)))))) +(defun parse-initarg (line) + "Given a metadata header, LINE, parse an initarg name/value pair from it." + (let ((name (string-upcase (subseq line 0 (position #\: line)))) + (match (nth-value 1 (scan-to-strings "[a-zA-Z]+:\\s+(.*)" line)))) + (when match + (list (make-keyword name) (aref match 0))))) + (defun parse-metadata (stream) "Given a STREAM, parse metadata from it or signal an appropriate condition." - (labels ((get-next-line (input) - (string-trim '(#\Space #\Newline #\Tab) (read-line input nil))) - (parse-value (str) - (nth-value 1 (cl-ppcre:scan-to-strings "[a-zA-Z]+:\\s+(.*)" str))) - (parse-initarg-name (line) - (make-keyword (string-upcase (subseq line 0 (position #\: line))))) - (extract-initarg (line) - (list (parse-initarg-name line) (aref (parse-value line) 0)))) + (flet ((get-next-line (input) + (string-trim '(#\Space #\Newline #\Tab) (read-line input nil)))) (unless (string= (get-next-line stream) (separator *config*)) (error "The file lacks the expected header: ~a" (separator *config*))) (loop for line = (get-next-line stream) until (string= line (separator *config*)) - when (parse-value line) - appending (extract-initarg line)))) + appending (parse-initarg line)))) (defun read-content (file) "Returns a plist of metadata from FILE with :text holding the content." diff --git a/src/packages.lisp b/src/packages.lisp index 7bc0a65..e3c3924 100644 --- a/src/packages.lisp +++ b/src/packages.lisp @@ -5,6 +5,7 @@ #:make-keyword #:mappend) (:import-from :cl-fad #:file-exists-p) + (:import-from :cl-ppcre #:scan-to-strings) (:import-from :closure-template #:compile-template) (:import-from :local-time #:format-rfc1123-timestring) (:import-from :uiop #:getcwd From e44d0bde9ec81b1227901271257e374c9f3a78cc Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Mon, 22 Sep 2014 14:26:20 -0400 Subject: [PATCH 34/44] Make add-injection support more complex injections. Thanks @PuercoPop. In short, an injection is now a FUNCTION that takes a document and returns either a STRING to insert in the page (possibly with data from the document) or NIL. --- NEWS.md | 9 +++++++-- docs/hacking.md | 2 +- docs/plugin-api.md | 12 +++++------- plugins/analytics.lisp | 3 ++- plugins/disqus.lisp | 6 ++++-- plugins/mathjax.lisp | 7 ++++--- src/themes.lisp | 16 +++++----------- 7 files changed, 28 insertions(+), 27 deletions(-) diff --git a/NEWS.md b/NEWS.md index e66d0b8..cc776ae 100644 --- a/NEWS.md +++ b/NEWS.md @@ -4,13 +4,16 @@ instead of the previous symlinked, timestamped deploy strategy. To retain the previous behavior, add `(versioned)` to your config's `:plugins` list. -* **SITE-BREAKING CHANGE**: Custom themes will be broken by a change +* **Incompatible Change**: Custom themes will be broken by a change to URL handling. Previously, we were hand-constructing URLs in the templates. All site objects now store their URL in an instance slot. In general, hrefs should be of the form ` ...`. +* **Incompatible Change**: The interface of the `add-injection` function + has changed. If you have written a plugin which uses `add-injection` + you should update it to conform to the [new interface][add-inj-new]. +* **Docs**: Improved README and Theming docs. New Config File docs. * Changes to `:routing` would previously break links in the templates but now work seamlessly due to the updated URL handling. -* **Docs**: Improved README and Theming docs. New Config File docs. * Loading content is more robust when empty lines or metadata are passed. Thanks to @PuercoPop for the bug report and preliminary fix. * The config `:repo` option is now deprecated as its value has become @@ -21,6 +24,8 @@ * The templates are now HTML5 valid thanks to @Ferada. * Fixed a bug where RSS/Atom tag feeds were being published multiple times. +[add-inj-new]: https://github.com/redline6561/coleslaw/blob/master/docs/plugin-api.md#extension-points + ## Changes for 0.9.5 (2014-06-13): * A plugin for Incremental builds, cutting runtime for generating diff --git a/docs/hacking.md b/docs/hacking.md index 076fad1..effe1ea 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -213,7 +213,7 @@ order. Feeds exist to special case RSS and ATOM generation. Currently, there is only 1 content type: POST, for blog entries. PAGE, a content type for static page support, is available as a plugin. -## Areas for Improvement +## Areas for Improvement (i.e. The Roadmap) ### TODO for 0.9.6 diff --git a/docs/plugin-api.md b/docs/plugin-api.md index f2108aa..c7979b2 100644 --- a/docs/plugin-api.md +++ b/docs/plugin-api.md @@ -15,14 +15,12 @@ # Extension Points -* **New functionality via JS**, for example the Disqus and Mathjax - plugins. In this case, the plugin's `enable` function should call +* **New functionality via JS**, for example the Disqus and Mathjax plugins. + In this case, the plugin's `enable` function should call [`add-injection`](http://redlinernotes.com/docs/coleslaw.html#add-injection_func) - with an injection and a keyword. The injection itself is a list of - the string to insert and a lambda or function that can be called on - a content instance to determine whether the injection should be - included on the page. The keyword specifies whether the injected - text goes in the HEAD or BODY element. The + with an injection and a keyword. The injection is a function that takes a + *Document* and returns a string to insert in the page or nil. + The keyword specifies whether the injected text goes in the HEAD or BODY element. The [Disqus plugin](http://github.com/redline6561/coleslaw/blob/master/plugins/disqus.lisp) is a good example of this. diff --git a/plugins/analytics.lisp b/plugins/analytics.lisp index d72e5ec..987b660 100644 --- a/plugins/analytics.lisp +++ b/plugins/analytics.lisp @@ -21,4 +21,5 @@ ") (defun enable (&key tracking-code) - (add-injection (format nil *analytics-js* tracking-code) :head)) + (let ((snippet (format nil *analytics-js* tracking-code))) + (add-injection (constantly snippet) :head))) diff --git a/plugins/disqus.lisp b/plugins/disqus.lisp index 418320e..a2cab65 100644 --- a/plugins/disqus.lisp +++ b/plugins/disqus.lisp @@ -24,5 +24,7 @@ comments powered by Disqus") (defun enable (&key shortname) - (add-injection (list (format nil *disqus-header* shortname) - (lambda (x) (typep x 'post))) :body)) + (flet ((inject-p (x) + (when (typep x 'post) + (format nil *disqus-header* shortname)))) + (add-injection #'inject-p :body))) diff --git a/plugins/mathjax.lisp b/plugins/mathjax.lisp index df6bd80..31e7cc7 100644 --- a/plugins/mathjax.lisp +++ b/plugins/mathjax.lisp @@ -24,6 +24,7 @@ (defun enable (&key force config (preset "TeX-AMS-MML_HTMLorMML") (location "http://cdn.mathjax.org/mathjax/latest/MathJax.js")) - (flet ((plugin-p (x) (or force (mathjax-p x)))) - (let ((mathjax-header (format nil *mathjax-header* config location preset))) - (add-injection (list mathjax-header #'plugin-p) :head)))) + (flet ((inject-p (x) + (when (or force (mathjax-p x)) + (format nil *mathjax-header* config location preset)))) + (add-injection #'inject-p :head))) diff --git a/src/themes.lisp b/src/themes.lisp index 1d7947c..fded3ee 100644 --- a/src/themes.lisp +++ b/src/themes.lisp @@ -4,21 +4,15 @@ "A list that stores pairs of (string . predicate) to inject in the page.") (defun add-injection (injection location) - "Adds an INJECTION to a given LOCATION for rendering. The INJECTION should be -a string which will always be added or a (string . lambda). In the latter case, -the lambda takes a single argument, a content object, i.e. a POST or INDEX, and -any return value other than nil indicates the injection should be added." - (let ((result (etypecase injection - (string (list injection #'identity)) - (list injection)))) - (push result (getf *injections* location)))) + "Adds an INJECTION to a given LOCATION for rendering. The INJECTION should be a +function that takes a DOCUMENT and returns NIL or a STRING for template insertion." + (push result (getf *injections* location))) (defun find-injections (content) "Iterate over *INJECTIONS* collecting any that should be added to CONTENT." (flet ((injections-for (location) - (loop for (injection predicate) in (getf *injections* location) - when (funcall predicate content) - collect injection))) + (loop for injection in (getf *injections* location) + collecting (funcall injection content)))) (list :head (injections-for :head) :body (injections-for :body)))) From 8bea5f192755334141333f897ac6d2136b0f60e9 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Mon, 22 Sep 2014 14:32:27 -0400 Subject: [PATCH 35/44] TODO tweaks for 0.9.7. --- docs/hacking.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/hacking.md b/docs/hacking.md index effe1ea..6cf7cc1 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -215,21 +215,15 @@ PAGE, a content type for static page support, is available as a plugin. ## Areas for Improvement (i.e. The Roadmap) -### TODO for 0.9.6 - -* A real test suite with Stefil that at *least* tests: - * `read-content` - * `load-config` -* Merge @PuercoPop's twitter summary cards plugin in some form. - ### TODO for 0.9.7 * Test suite improvements: + * `load-content`/`read-content`/parsing * Content Discovery * Theme Compilation * Content Publishing * Common Plugins including Injections -* Add proper errors to read-content/load-content. Not just ignoring bad data. Line info, etc. +* Add proper errors to read-content/load-content? Not just ignoring bad data. Line info, etc. * Improved template debugging? "${" instead of "{$", static checks for valid slots, etc. At least a serious investigation into how such things might be provided. * Some minor scripting conveniences with cl-launch? (Scaffold a post/page, Enable incremental, Build, etc). From 0640ebc5cbe44dec80db62df52a08c42a8991688 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Mon, 22 Sep 2014 15:48:46 -0400 Subject: [PATCH 36/44] Some more NEWS updates. --- NEWS.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/NEWS.md b/NEWS.md index cc776ae..f5ef759 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,12 @@ +Legend: +* Site-Breaking Change: + A change that will break most config files or coleslaw installations. + It is expected to effect all users but should require only minor + user effort to resolve. +* Incompatible Change: + A change to Coleslaw's exported interface. Plugins or Themes that have + not been upstreamed are effected and may require minor effort to fix. + ## Changes for 0.9.6 (2014-09-17): * **SITE-BREAKING CHANGE**: Coleslaw now defaults to a "basic" deploy @@ -10,7 +19,7 @@ In general, hrefs should be of the form ` ...`. * **Incompatible Change**: The interface of the `add-injection` function has changed. If you have written a plugin which uses `add-injection` - you should update it to conform to the [new interface][add-inj-new]. + you should update it to conform to the [new interface][plg-api]. * **Docs**: Improved README and Theming docs. New Config File docs. * Changes to `:routing` would previously break links in the templates but now work seamlessly due to the updated URL handling. @@ -24,13 +33,11 @@ * The templates are now HTML5 valid thanks to @Ferada. * Fixed a bug where RSS/Atom tag feeds were being published multiple times. -[add-inj-new]: https://github.com/redline6561/coleslaw/blob/master/docs/plugin-api.md#extension-points - ## Changes for 0.9.5 (2014-06-13): -* A plugin for Incremental builds, cutting runtime for generating +* **New Plugin**: Incremental builds, cutting runtime for generating medium to large sites roughly in half! -* A Twitter plugin to tweet about your new posts. Thanks to @PuercoPop! +* **New Plugin**: A Twitter plugin to tweet about your new posts. Thanks to @PuercoPop! * Config options for the HTML lang and charset attributes. Thanks to @ryumei! * Coleslaw now exports a `get-updated-files` function which can be used to get a list of file-status/file-name pairs that were changed @@ -132,3 +139,5 @@ [hacking_guide]: https://github.com/redline6561/coleslaw/blob/master/docs/hacking.md [theming_guide]: https://github.com/redline6561/coleslaw/blob/master/docs/themes.md [example.rc]: https://github.com/redline6561/coleslaw/blob/master/examples/example.coleslawrc +[plg-use]: https://github.com/redline6561/coleslaw/blob/master/docs/plugin-use.md +[plg-api]: https://github.com/redline6561/coleslaw/blob/master/docs/plugin-api.md#extension-points From 47a17508e87d979feeec396126401e0bf940fd6d Mon Sep 17 00:00:00 2001 From: Javier Olaechea Date: Fri, 15 Aug 2014 19:29:25 -0500 Subject: [PATCH 37/44] Add twitter meta-data to posts This depends on a pending change to add-injection --- plugins/twitter-summary-card.lisp | 22 ++++++++++++++++++++++ src/packages.lisp | 3 ++- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 plugins/twitter-summary-card.lisp diff --git a/plugins/twitter-summary-card.lisp b/plugins/twitter-summary-card.lisp new file mode 100644 index 0000000..8a13889 --- /dev/null +++ b/plugins/twitter-summary-card.lisp @@ -0,0 +1,22 @@ +(defpackage :coleslaw-twitter-summary-card + (:use :cl) + (:export #:enable)) + +(in-package :coleslaw-twitter-summary-card) + +(defun summary-card (post twitter-handle) + "TODO: Figure if and how to include twitter:url meta property." + (format nil " +~@[~] + +" + twitter-handle + (title-of post) + (content-text post))) + +(defun enable (&key twitter-handle) + (add-injection + (lambda (x) + (when (typep x 'post) + (summary-card x twitter-handle))) + :head)) diff --git a/src/packages.lisp b/src/packages.lisp index e3c3924..93eedcd 100644 --- a/src/packages.lisp +++ b/src/packages.lisp @@ -39,4 +39,5 @@ #:purge-all #:add-document #:delete-document - #:write-document)) + #:write-document + #:content-text)) From a9d73e3b2665ba29f2fa124aae0f100189f512b1 Mon Sep 17 00:00:00 2001 From: Javier Olaechea Date: Fri, 15 Aug 2014 20:18:40 -0500 Subject: [PATCH 38/44] Bind content-text & add quotes to HTML attributes --- plugins/twitter-summary-card.lisp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/plugins/twitter-summary-card.lisp b/plugins/twitter-summary-card.lisp index 8a13889..77aff6f 100644 --- a/plugins/twitter-summary-card.lisp +++ b/plugins/twitter-summary-card.lisp @@ -1,5 +1,5 @@ (defpackage :coleslaw-twitter-summary-card - (:use :cl) + (:use :cl :coleslaw) (:export #:enable)) (in-package :coleslaw-twitter-summary-card) @@ -7,12 +7,15 @@ (defun summary-card (post twitter-handle) "TODO: Figure if and how to include twitter:url meta property." (format nil " -~@[~] - -" +~@[~] + +" twitter-handle (title-of post) - (content-text post))) + (let ((text (content-text post))) + (if (< 200 (length text)) + (subseq text 0 199) + text)))) (defun enable (&key twitter-handle) (add-injection From c286fc1e337c03b246b960b58f236c9bd8cef03d Mon Sep 17 00:00:00 2001 From: Javier Olaechea Date: Fri, 15 Aug 2014 20:20:11 -0500 Subject: [PATCH 39/44] Add tests as a PoC. Do not for merge. --- tests/plugins/twitter-summary-card.lisp | 59 +++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tests/plugins/twitter-summary-card.lisp diff --git a/tests/plugins/twitter-summary-card.lisp b/tests/plugins/twitter-summary-card.lisp new file mode 100644 index 0000000..7bb607f --- /dev/null +++ b/tests/plugins/twitter-summary-card.lisp @@ -0,0 +1,59 @@ +(stefil:define-test-package :twitter-summary-card-tests + (:use :cl :coleslaw + :cl-ppcre)) + +(in-package :twitter-summary-card-tests) +;;;; +;; To run tests first eval the plugin code inside this package and then execute +;; (twitter-summary-card-test:run-package-tests) +;;; + + +(defvar *short-post* + (make-instance 'post :title "hai" :text "very greetings" :format "html")) + +(defvar *long-post* + (make-instance 'post :title "What a Wonderful World" + :text "I see trees of green, red roses too. I see them +bloom, for me and you. And I think to myself, what a wonderful world. + +I see skies of blue, +And clouds of white. +The bright blessed day, +The dark sacred night. +And I think to myself, +What a wonderful world. + +The colors of the rainbow, +So pretty in the sky. +Are also on the faces, +Of people going by, +I see friends shaking hands. +Saying, \"How do you do?\" +They're really saying, +\"I love you\". + +I hear babies cry, +I watch them grow, +They'll learn much more, +Than I'll ever know. +And I think to myself, +What a wonderful world. + +Yes, I think to myself, +What a wonderful world. " :format "html")) + +(deftest summary-card-sans-twitter-handle () + (let ((summary-card (summary-card *short-post* nil))) + (is (null (scan "twitter:author" summary-card))))) + +(deftest summary-card-with-twitter-handle () + (let ((summary-card (summary-card *short-post* "@PuercoPop"))) + (is (scan "twitter:author" summary-card)))) + +(deftest summary-card-trims-long-post () + (let ((summary-card (summary-card *long-post* nil))) + ;; (scan "twitter:description\" content=\"(.*)\"" summary-card) + summary-card)) + + From c7fc38b73c95c65ca4beb6fcaf7fda26d5b24ad6 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Tue, 23 Sep 2014 11:47:46 -0400 Subject: [PATCH 40/44] Some last tweaks for Summary Cards before 0.9.6. --- NEWS.md | 5 +- docs/plugin-use.md | 7 +++ tests/plugins/twitter-summary-card.lisp | 76 +++++++++++++------------ 3 files changed, 50 insertions(+), 38 deletions(-) diff --git a/NEWS.md b/NEWS.md index f5ef759..ac55843 100644 --- a/NEWS.md +++ b/NEWS.md @@ -7,7 +7,7 @@ Legend: A change to Coleslaw's exported interface. Plugins or Themes that have not been upstreamed are effected and may require minor effort to fix. -## Changes for 0.9.6 (2014-09-17): +## Changes for 0.9.6 (2014-09-27): * **SITE-BREAKING CHANGE**: Coleslaw now defaults to a "basic" deploy instead of the previous symlinked, timestamped deploy strategy. @@ -20,6 +20,8 @@ Legend: * **Incompatible Change**: The interface of the `add-injection` function has changed. If you have written a plugin which uses `add-injection` you should update it to conform to the [new interface][plg-api]. +* **New Plugin**: Support for [twitter summary cards][ts-cards] on blog + posts has been added thanks to @PuercoPop. * **Docs**: Improved README and Theming docs. New Config File docs. * Changes to `:routing` would previously break links in the templates but now work seamlessly due to the updated URL handling. @@ -141,3 +143,4 @@ Legend: [example.rc]: https://github.com/redline6561/coleslaw/blob/master/examples/example.coleslawrc [plg-use]: https://github.com/redline6561/coleslaw/blob/master/docs/plugin-use.md [plg-api]: https://github.com/redline6561/coleslaw/blob/master/docs/plugin-api.md#extension-points +[ts-cards]: https://dev.twitter.com/cards/types/summary diff --git a/docs/plugin-use.md b/docs/plugin-use.md index b614a1f..1387ef5 100644 --- a/docs/plugin-use.md +++ b/docs/plugin-use.md @@ -144,6 +144,13 @@ CL-USER> (chirp:complete-authentication "4173325") # ``` +## Twitter Summary Cards + +**Description**: Add Summary Card metadata to blog posts + to enhance twitter links to that content. + +**Example**: `(twitter-summary-card :twitter-handle "@redline6561") + ## Versioned Deploys **Description**: Originally, this was Coleslaw's only deploy behavior. diff --git a/tests/plugins/twitter-summary-card.lisp b/tests/plugins/twitter-summary-card.lisp index 7bb607f..e5faf60 100644 --- a/tests/plugins/twitter-summary-card.lisp +++ b/tests/plugins/twitter-summary-card.lisp @@ -1,59 +1,61 @@ -(stefil:define-test-package :twitter-summary-card-tests - (:use :cl :coleslaw - :cl-ppcre)) +(defpackage :summary-card-tests + (:use :cl :coleslaw :stefil)) -(in-package :twitter-summary-card-tests) -;;;; -;; To run tests first eval the plugin code inside this package and then execute -;; (twitter-summary-card-test:run-package-tests) -;;; +(in-package :summary-card-tests) +(defsuite summary-cards) +(in-suite summary-cards) + +;; TODO: Create a fixture to either load a mocked config or load set of plugins. +;; Then wrap these tests to use that fixture. Then add these to defsystem, setup +;; general test run with other packages. + +(coleslaw::enable-plugin :twitter-summary-card) (defvar *short-post* - (make-instance 'post :title "hai" :text "very greetings" :format "html")) + (make-instance 'post :title "hai" :text "very greetings" :format "html")) -(defvar *long-post* - (make-instance 'post :title "What a Wonderful World" - :text "I see trees of green, red roses too. I see them +(defvar *long-post* + (make-instance 'post :title "What a Wonderful World" + :text "I see trees of green, red roses too. I see them bloom, for me and you. And I think to myself, what a wonderful world. -I see skies of blue, -And clouds of white. -The bright blessed day, -The dark sacred night. -And I think to myself, -What a wonderful world. +I see skies of blue, +And clouds of white. +The bright blessed day, +The dark sacred night. +And I think to myself, +What a wonderful world. -The colors of the rainbow, -So pretty in the sky. -Are also on the faces, -Of people going by, -I see friends shaking hands. -Saying, \"How do you do?\" -They're really saying, -\"I love you\". +The colors of the rainbow, +So pretty in the sky. +Are also on the faces, +Of people going by, +I see friends shaking hands. +Saying, \"How do you do?\" +They're really saying, +\"I love you\". -I hear babies cry, -I watch them grow, -They'll learn much more, -Than I'll ever know. -And I think to myself, -What a wonderful world. +I hear babies cry, +I watch them grow, +They'll learn much more, +Than I'll ever know. +And I think to myself, +What a wonderful world. -Yes, I think to myself, +Yes, I think to myself, What a wonderful world. " :format "html")) (deftest summary-card-sans-twitter-handle () (let ((summary-card (summary-card *short-post* nil))) - (is (null (scan "twitter:author" summary-card))))) + (is (null (cl-ppcre:scan "twitter:author" summary-card))))) (deftest summary-card-with-twitter-handle () (let ((summary-card (summary-card *short-post* "@PuercoPop"))) - (is (scan "twitter:author" summary-card)))) + (is (cl-ppcre:scan "twitter:author" summary-card)))) (deftest summary-card-trims-long-post () (let ((summary-card (summary-card *long-post* nil))) + (multiple-value-bind ()) ;; (scan "twitter:description\" content=\"(.*)\"" summary-card) summary-card)) - - From f27172a1f7ba4211312f12836944ae47edd30153 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Tue, 23 Sep 2014 17:27:21 -0400 Subject: [PATCH 41/44] Last minute backwards compatibility fix for people on very old SBCLs. --- examples/example.post-receive | 4 ++-- src/util.lisp | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/examples/example.post-receive b/examples/example.post-receive index c89c3f4..698ae72 100644 --- a/examples/example.post-receive +++ b/examples/example.post-receive @@ -25,9 +25,9 @@ while read oldrev newrev refname; do if [ $LISP = sbcl ]; then sbcl --eval "(ql:quickload 'coleslaw)" \ --eval "(coleslaw:main \"$TMP_GIT_CLONE\" \"$oldrev\")" \ - --eval "(coleslaw::exit)" + --eval "(uiop:quit)" elif [ $LISP = ccl ]; then - ccl -e "(ql:quickload 'coleslaw) (coleslaw:main \"$TMP_GIT_CLONE\" \"$oldrev\") (coleslaw::exit)" + ccl -e "(ql:quickload 'coleslaw) (coleslaw:main \"$TMP_GIT_CLONE\" \"$oldrev\") (uiop:quit)" else echo -e "$LISP is not a supported lisp dialect at this time. Exiting.\n" exit 1 diff --git a/src/util.lisp b/src/util.lisp index 1811bde..1862ec3 100644 --- a/src/util.lisp +++ b/src/util.lisp @@ -54,13 +54,9 @@ an UNWIND-PROTECT, then change back to the current directory." (setf (getcwd) ,old))))) (defun exit () + ;; KLUDGE: Just call UIOP for now. Don't want users updating scripts. "Exit the lisp system returning a 0 status code." - #+sbcl (sb-ext:exit) - #+ccl (ccl:quit) - #+ecl (si:quit) - #+cmucl (ext:quit) - #+clisp (ext:quit) - #-(or sbcl ccl ecl cmucl clisp) (error "Not implemented yet.")) + (uiop:quit)) (defun fmt (fmt-str args) "A convenient FORMAT interface for string building." From 1289706b4646e84160e15d9c50e2aabb73ff12b4 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Tue, 23 Sep 2014 17:32:48 -0400 Subject: [PATCH 42/44] Quick bugfix to add-injection. --- src/themes.lisp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/themes.lisp b/src/themes.lisp index fded3ee..dce2138 100644 --- a/src/themes.lisp +++ b/src/themes.lisp @@ -6,7 +6,7 @@ (defun add-injection (injection location) "Adds an INJECTION to a given LOCATION for rendering. The INJECTION should be a function that takes a DOCUMENT and returns NIL or a STRING for template insertion." - (push result (getf *injections* location))) + (push injection (getf *injections* location))) (defun find-injections (content) "Iterate over *INJECTIONS* collecting any that should be added to CONTENT." From 1b3acd107ec9825576c78a07211954861a5ecb24 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Tue, 23 Sep 2014 17:38:59 -0400 Subject: [PATCH 43/44] Fix feed handling bug backwards compatibly. No name changes. --- examples/example.coleslawrc | 2 +- src/feeds.lisp | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/example.coleslawrc b/examples/example.coleslawrc index 6a3691a..25d0f3f 100644 --- a/examples/example.coleslawrc +++ b/examples/example.coleslawrc @@ -14,7 +14,7 @@ (:tag-index "tag/~a") (:month-index "date/~a") (:numeric-index "~d") - (:standard-feed "~a.xml") + (:feed "~a.xml") (:tag-feed "tag/~a.xml")) :sitenav ((:url "http://redlinernotes.com/" :name "Home") (:url "http://twitter.com/redline6561" :name "Twitter") diff --git a/src/feeds.lisp b/src/feeds.lisp index a0aaeef..4bebcb0 100644 --- a/src/feeds.lisp +++ b/src/feeds.lisp @@ -2,25 +2,25 @@ ;;; Atom and RSS Feeds -(defclass feed () ((format :initarg :format :reader feed-format))) +(defclass base-feed () ((format :initarg :format :reader feed-format))) -(defclass standard-feed (index feed) ()) +(defclass feed (index base-feed) ()) -(defmethod discover ((doc-type (eql (find-class 'standard-feed)))) +(defmethod discover ((doc-type (eql (find-class 'feed)))) (let ((content (by-date (find-all 'post)))) (dolist (format '(rss atom)) - (let ((feed (make-instance 'standard-feed :format format + (let ((feed (make-instance 'feed :format format :content (take-up-to 10 content) :slug (format nil "~(~a~)" format)))) (add-document feed))))) -(defmethod publish ((doc-type (eql (find-class 'standard-feed)))) - (dolist (feed (find-all 'standard-feed)) +(defmethod publish ((doc-type (eql (find-class 'feed)))) + (dolist (feed (find-all 'feed)) (write-document feed (theme-fn (feed-format feed) "feeds")))) ;;; Tag Feeds -(defclass tag-feed (index feed) ()) +(defclass tag-feed (index base-feed) ()) (defmethod discover ((doc-type (eql (find-class 'tag-feed)))) (let ((content (by-date (find-all 'post)))) From 62cc5e91f4256ad443d2a75cd910aab6dfba5af3 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Mon, 22 Sep 2014 15:51:24 -0400 Subject: [PATCH 44/44] Release: 0.9.6! --- coleslaw.asd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coleslaw.asd b/coleslaw.asd index 1de39e7..5257163 100644 --- a/coleslaw.asd +++ b/coleslaw.asd @@ -1,7 +1,7 @@ (defsystem #:coleslaw :name "coleslaw" :description "Flexible Lisp Blogware" - :version "0.9.5-dev" + :version "0.9.6" :license "BSD" :author "Brit Butler " :pathname "src/"