Go through HACKING docs with a fine tooth comb.
This commit is contained in:
parent
4ae9634c52
commit
96d85c8e69
2 changed files with 112 additions and 130 deletions
186
docs/hacking.md
186
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
|
a week. Though it has evolved considerably since it's inception, like
|
||||||
any software some mess remains.
|
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
|
## Core Concepts
|
||||||
|
|
||||||
### Data and Deployment
|
### 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
|
**Coleslaw** is pretty fundamentally tied to the idea of git as both a
|
||||||
backing data store and a deployment method (via `git push`). The
|
backing data store and a deployment method (via `git push`). The
|
||||||
consequence is that you need a bare repo somewhere with a post-recieve
|
consequence is that you need a bare repo somewhere with a post-recieve
|
||||||
hook. That post-recieve hook
|
hook. That post-recieve hook ([example][post_receive_hook])
|
||||||
([example](https://github.com/redline6561/coleslaw/blob/master/examples/example.post-receive))
|
|
||||||
will checkout the repo to a **$TMPDIR** and call `(coleslaw:main $TMPDIR)`.
|
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
|
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
|
symlink. However, there are plugins for deploying to Heroku, S3, and
|
||||||
Github Pages.
|
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
|
### Blogs vs Sites
|
||||||
|
|
||||||
**Coleslaw** is blogware. When I designed it, I only cared that it
|
**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.
|
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
|
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
|
generator. Thankfully, Content Types were added in 0.8 as a step
|
||||||
*coleslaw* suitable for more use cases but still have some
|
towards making *coleslaw* suitable for more use cases. Any subclass of
|
||||||
limitations. Any subclass of CONTENT that implements the *document
|
CONTENT that implements the *document protocol* counts as a content
|
||||||
protocol* counts as a content type. However, only POSTs are currently
|
type. However, only POSTs are currently included in the basic INDEXes
|
||||||
included on INDEXes since their isn't yet a formal relationship to
|
since there isn't yet a formal relationship to determine which content
|
||||||
determine what content types should be included on which indexes.
|
types should be included on which indexes. Users may easily implement
|
||||||
|
their own dedicated INDEX for new Content Types.
|
||||||
|
|
||||||
### The Document Protocol
|
### 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
|
There are also 4 helper functions provided that should prove useful in
|
||||||
implementing new content types.
|
implementing new content types.
|
||||||
|
|
||||||
|
|
||||||
**Class Methods**:
|
**Class Methods**:
|
||||||
|
|
||||||
Since Common Lisp doesn't have explicit support for class methods, we
|
Class Methods don't *really* exist in Common Lisp, as methods are
|
||||||
implement them by eql-specializing on the class, e.g.
|
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
|
```lisp
|
||||||
(defmethod foo ((doc-type (eql (find-class 'bar))))
|
(defmethod foo ((doc-type (eql (find-class 'bar))))
|
||||||
... )
|
... )
|
||||||
```
|
```
|
||||||
|
|
||||||
- `discover`: Create instances for documents of the class and put them in
|
- `discover`: Create instances for documents of the class and put them
|
||||||
in-memory database with `add-document`. If your class is a subclass of
|
in the in-memory database with `add-document`.
|
||||||
CONTENT, there is a default method for this.
|
|
||||||
- `publish`: Iterate over all objects of the class
|
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**:
|
**Instance Methods**:
|
||||||
|
|
||||||
- `page-url`: Generate a unique, relative path for the object on the site
|
- `page-url`: Generate a relative path for the object on the site,
|
||||||
sans file extension. An :around method adds that later. The `slug` slot
|
usually sans file extension. If there is no extension, an :around
|
||||||
on the object is conventionally used to hold a portion of the unique
|
method adds "html" later. The `slug` slot on the instance is
|
||||||
identifier. i.e. `(format nil "posts/~a" (content-slug object))`.
|
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`,
|
- `render`: A method that calls the appropriate template with `theme-fn`,
|
||||||
passing it any needed arguments and returning rendered HTML.
|
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
|
database. It will error if the `page-url` of the document is not
|
||||||
unique. Such a hash collision represents content on the site being
|
unique. Such a hash collision represents content on the site being
|
||||||
shadowed/overwritten. This should be used in your `discover` method.
|
shadowed/overwritten. This should be used in your `discover` method.
|
||||||
|
|
||||||
- `write-document`: Write the document out to disk as HTML. It takes
|
- `write-document`: Write the document out to disk as HTML. It takes
|
||||||
an optional template name and render-args to pass to the template.
|
an optional template name and render-args to pass to the template.
|
||||||
This should be used in your `publish` method.
|
This should be used in your `publish` method.
|
||||||
|
|
||||||
- `find-all`: Return a list of all documents of the requested class.
|
- `find-all`: Return a list of all documents of the requested class.
|
||||||
This is often used in the `publish` method to iterate over documents
|
This is often used in the `publish` method to iterate over documents
|
||||||
of a given type.
|
of a given type.
|
||||||
|
|
||||||
- `purge-all`: Remove all instances of the requested class from the DB.
|
- `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
|
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.
|
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
|
grouping content by tags, publishing date, and reverse chronological
|
||||||
order. Feeds exist to special case RSS and ATOM generation.
|
order. Feeds exist to special case RSS and ATOM generation.
|
||||||
Currently, there is only 1 content type: POST, for blog entries.
|
Currently, there is only 1 content type: POST, for blog entries.
|
||||||
|
PAGE, a content type for static page support, is available as a plugin.
|
||||||
### 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.
|
|
||||||
|
|
||||||
## Areas for Improvement
|
## 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
|
regenerated along with any tag or month indexes matching the
|
||||||
modified files. If incremental compilation is a goal, simply
|
modified files. If incremental compilation is a goal, simply
|
||||||
disabling the indexes may be appropriate for certain users.
|
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
|
||||||
|
|
|
@ -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.
|
|
Loading…
Add table
Reference in a new issue