From 96d85c8e699a300cb88f485071b7131310a0a8c9 Mon Sep 17 00:00:00 2001 From: Brit Butler Date: Fri, 2 May 2014 16:25:39 -0400 Subject: [PATCH] Go through HACKING docs with a fine tooth comb. --- docs/hacking.md | 186 ++++++++++++++++++++++++++++------------------- docs/overview.md | 56 -------------- 2 files changed, 112 insertions(+), 130 deletions(-) delete mode 100644 docs/overview.md diff --git a/docs/hacking.md b/docs/hacking.md index f8adae3..33a3d3b 100644 --- a/docs/hacking.md +++ b/docs/hacking.md @@ -7,6 +7,22 @@ the idea to each complete their half-dreamed wordpress replacement in a week. Though it has evolved considerably since it's inception, like any software some mess remains. +## Overall Structure + +Conceptually, coleslaw processes a blog as follows: + +1. Coleslaw loads the user's config, then reads the blog repo loading + any `.post` files or other content. CONTENT and INDEX objects are + created from those files. + +2. The CONTENT and INDEX objects are then fed to the templating engine + to produce HTML files in a config-specified staging directory, + usually under `/tmp`. + +3. A deploy method (possibly customized via plugins) is called with the + staging directory. It does whatever work is needed to make the + generated HTML files (and any static content) visible to the web. + ## Core Concepts ### Data and Deployment @@ -14,8 +30,7 @@ any software some mess remains. **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](https://github.com/redline6561/coleslaw/blob/master/examples/example.post-receive)) +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 @@ -25,6 +40,60 @@ 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. +The Plugin API is well-documented and flexible enough for many use +cases. Do check the [API docs][api_docs] when contemplating a new +feature and see if a plugin would be appropriate. + +### Templates and Theming + +User configs are allowed to specify a theme. A theme consists of a +directory under "themes/" containing css, images, and at least +3 templates: Base, Index, and Post. + +**Coleslaw** uses [cl-closure-template][closure_template] +exclusively for templating. **cl-closure-template** is a well +documented CL implementation of Google's Closure Templates. Each +template file should contain a namespace like +`coleslaw.theme.theme-name`. + +Each template creates a lisp function in the theme's package when +loaded. These functions take a property list (or plist) as an argument +and return rendered HTML. **Coleslaw** defines a helper called +`theme-fn` for easy access to the template functions. Additionally, +there are RSS, ATOM, and sitemap templates *coleslaw* uses automatically. +No need for individual themes to reimplement a standard, after all! + +### The Lifecycle of a Page + +- `(load-content)` + +A page starts, obviously, with a file. When *coleslaw* loads your +content, it iterates over a list of content types (i.e. subclasses of +CONTENT). For each content type, it iterates over all files in the +repo with a matching extension, e.g. ".post" for POSTs. Objects of the +appropriate class are created from each matching file and inserted +into the an in-memory data store. Then the INDEXes are created from +the loaded content and added to the data store. + +- `(compile-blog dir)` + +Compilation starts by ensuring the staging directory (`/tmp/coleslaw/` +by default) exists, cd'ing there, and copying over any necessary theme +assets. Then *coleslaw* iterates over all the content types and index +classes, rendering all of their instances and writing the HTML to disk. +After this, an 'index.html' symlink is created pointing to the first +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. + ### Blogs vs Sites **Coleslaw** is blogware. When I designed it, I only cared that it @@ -35,12 +104,13 @@ 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. Content Types were added in 0.8 as a step towards making -*coleslaw* suitable for more use cases but still have some -limitations. Any subclass of CONTENT that implements the *document -protocol* counts as a content type. However, only POSTs are currently -included on INDEXes since their isn't yet a formal relationship to -determine what content types should be included on which indexes. +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 +their own dedicated INDEX for new Content Types. ### The Document Protocol @@ -55,27 +125,43 @@ It consists of 2 "class" methods, 2 instance methods, and an invariant. There are also 4 helper functions provided that should prove useful in implementing new content types. + **Class Methods**: -Since Common Lisp doesn't have explicit support for class methods, we -implement them by eql-specializing on the class, e.g. +Class Methods don't *really* exist in Common Lisp, as methods are +defined on generic functions and not on the class itself. But since +it's useful to think about a Class as being responsible for its +instances in the case of a blog, we implement class methods by +eql-specializing on the class, e.g. + ```lisp (defmethod foo ((doc-type (eql (find-class 'bar)))) ... ) ``` -- `discover`: Create instances for documents of the class and put them in - in-memory database with `add-document`. If your class is a subclass of - CONTENT, there is a default method for this. -- `publish`: Iterate over all objects of the class +- `discover`: Create instances for documents of the class and put them + in the in-memory database with `add-document`. + + For CONTENT, this means checking the blog repo for any files with a + matching extension and loading them from disk. If your class is a + subclass of CONTENT, it inherits a pleasant default method for this. + + For INDEXes, this means iterating over any relevant CONTENT in the + database, and creating INDEXes in the database that include that + content. + +- `publish`: Iterate over all instances of the class, rendering each + one to HTML and writing it out to the staging directory on disk. **Instance Methods**: -- `page-url`: Generate a unique, relative path for the object on the site - sans file extension. An :around method adds that later. The `slug` slot - on the object is conventionally used to hold a portion of the unique - identifier. i.e. `(format nil "posts/~a" (content-slug object))`. +- `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. + - `render`: A method that calls the appropriate template with `theme-fn`, passing it any needed arguments and returning rendered HTML. @@ -92,12 +178,15 @@ implement them by eql-specializing on the class, e.g. database. It will error if the `page-url` of the document is not unique. Such a hash collision represents content on the site being shadowed/overwritten. This should be used in your `discover` method. + - `write-document`: Write the document out to disk as HTML. It takes an optional template name and render-args to pass to the template. This should be used in your `publish` method. + - `find-all`: Return a list of all documents of the requested class. This is often used in the `publish` method to iterate over documents of a given type. + - `purge-all`: Remove all instances of the requested class from the DB. This is primarily used at the REPL or for debugging but it is also used in a `:before` method on `discover` to keep it idempotent. @@ -109,62 +198,7 @@ NUMERIC-INDEX, FEED, and TAG-FEED. Respectively, they support grouping content by tags, publishing date, and reverse chronological order. Feeds exist to special case RSS and ATOM generation. Currently, there is only 1 content type: POST, for blog entries. - -### Templates and Theming - -User configs are allowed to specify a theme, otherwise the default is -used. A theme consists of a directory under "themes/" containing css, -images, and at least 3 templates: Base, Index, and Post. - -**Coleslaw** uses -[cl-closure-template](https://github.com/archimag/cl-closure-template) -exclusively for templating. **cl-closure-template** is a well -documented CL implementation of Google's Closure Templates. Each -template file should contain a namespace like -`coleslaw.theme.theme-name`. - -Each template creates a lisp function in the theme's package when -loaded. These functions take a property list (or plist) as an argument -and return rendered HTML. **Coleslaw** defines a helper called -`theme-fn` for easy access to the template functions. Additionally, -there are RSS, ATOM, and sitemap templates *coleslaw* uses automatically. -No need for individual themes to reimplement a standard, after all! - -### Plugins - -**Coleslaw** also encourages extending functionality via plugins. The Plugin -API is well-documented and flexible enough for many use cases. Do check the -[API docs](https://github.com/redline6561/coleslaw/blob/master/docs/plugin-api.md) -when contemplating a new feature and see if a plugin would be appropriate. - -### The Lifecycle of a Page - -- `(load-content)` - -A page starts, obviously, with a file. When *coleslaw* loads your -content, it iterates over a list of content types (i.e. subclasses of -CONTENT). For each content type, it iterates over all files in the -repo with a matching extension, e.g. ".post" for POSTs. Objects of the -appropriate class are created from each matching file and inserted -into the an in-memory data store. Then the INDEXes are created by -iterating over the POSTs and inserted into the data store. - -- `(compile-blog dir)` - -Compilation starts by ensuring the staging directory (`/tmp/coleslaw/` -by default) exists, cd'ing there, and copying over any necessary theme -assets. Then *coleslaw* iterates over the content types and index -classes, calling the `publish` method on each one. Publish iterates -over the class instances, rendering each one and writing the result -out to disk with `write-file`. After this, an 'index.html' symlink is -created to point to the first 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. +PAGE, a content type for static page support, is available as a plugin. ## Areas for Improvement @@ -220,3 +254,7 @@ 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 diff --git a/docs/overview.md b/docs/overview.md deleted file mode 100644 index 9189d5e..0000000 --- a/docs/overview.md +++ /dev/null @@ -1,56 +0,0 @@ -## Overall Structure - -Conceptually, coleslaw processes a blog as follows: - -1. Coleslaw loads the user's config, then reads a directory containing - `.post` files and processes them into POST and INDEX objects. - -2. The POST and INDEX objects are then fed to the templating engine - to produce HTML files. - -3. A deploy method is called (possibly customized with plugins) to make - the resulting HTML files (and any static content) visible to the web. - -## What files are generated anyway? - -Before we dive into other details, it is useful to know the directory -structure of the generated content, so you know how the relative path -different content resides at. - -The blog's toplevel looks like this: -``` -index.html -posts/ -date/ -tag/ -css/ -static/ (optional) -``` - -### index.html - -This file is the blog homepage, as you'd expect. It contains a list of -the most recent posts and has links to the different archives. - -### posts directory - -This directory contains an `.html` file per post. The name of the file -is the post's `slug`. - -### date directory - -This directory contains an `.html` file per month, for each month with -published content. The name of the file is of the form `yyyy-mm.html`. - -### tag directory - -This directory contains an `.html` file per tag, each containing all -posts with that tag. The name of the file is the tag's `slug`. - -### css directory - -This directory is a copy of the `css/` folder of the theme. - -### static directory (optional) - -This directory is a copy of the `static/` directory of the blog's git repo.