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
|
||||
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
|
||||
|
|
|
@ -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