site.fabricate.api
Namespace
Fabricate's API is meant to be used in 3 steps, each specified by a function and a corresponding multimethod.
The rest of this this document explains each of these steps.
plan!
- Function
Execute all the given
setup-tasks
, thencollect
the list of entries from each source.This list of entries will be appended to any entries passed in as a component of the
site
argument.- Arguments
- setup-tasks
- {:keys [site.fabricate.api/entries
site.fabricate.api/options]
:or {entries []}
:as site}
- Returns
- site: a map with two keys:
:site.fabricate.api/entries
with the list of entries, and:site.fabricate.api/options
, containing site-wide options. - Source
site/fabricate/api.clj
The plan step executes a list of setup functions, and then collects the initial list of entries that will be assembled during the build step. Each implementation of the collect multimethod specifies a source location that entries will be collected from - typically a string indicating a file path - and a function that uses that location to produce a list of entries.
Each source can produce one or more entries. See below for an example of how Fabricate uses a glob pattern string to generate a sequence of entries.
collect
- Multimethod
Generate the input entries from a source.
- Arguments
- Entry map
- Options map
- Returns
- A sequence of entries, specified as maps.
- Source
site/fabricate/api.clj
Example
(defmethod api/collect "pages/**.fab"
[src options]
(mapv (fn path->entry [p] ...)
(fs/glob (System/getProperty "user.dir") src)))
The entries collected from all of the sources are flattened into a single vector at the end of the plan step and returned with the site.
assemble
- Function
Prepare the entries for
produce!
by callingbuild
on each entry, then runningtasks
on the results.- Arguments
- tasks
- {:keys [site.fabricate.api/entries
site.fabricate.api/options]
:as site}
- Returns
- A site, specified as a map.
- Source
site/fabricate/api.clj
The assemble step builds structured data for each entry generated by the plan step. Each entry gets passed to the build multimethod.
After this, each function in the sequence of tasks passed as an argument gets called on the resulting site, in order. This lets you do things like assemble additional entries - like a table of contents, index, or site map - from the contents of the entries after they get built.
build
- Multimethod
Generate structured (EDN) document content for an entry from a source format. Takes an entry and returns a document (entry).
- Arguments
- Entry map
- Options map
- Returns
- An entry with the
:site.fabricate.document/data
key added. - Source
site/fabricate/api.clj
Example
(defmethod api/build [:clojure/v0 :hiccup]
[{:as entry
source-location :site.fabricate.source/location} opts]
(println "generating hiccup from" (str source-location))
(clj-entry->hiccup entry))
Build dispatches on two keys for each entry:
:site.fabricate.source/format
:site.fabricate.document/format
Because of this, each implementation of collect should define a format for each entry it produces.
When built, the entry will have a key - :site.fabricate.document/data
- containing the document after its conversion into Clojure data. Fabricate's default build methods return Hiccup from one of Fabricate's 2 built-in sources: Fabricate templates, Clojure source code.
However, because it dispatches on any keyword, your implementation of build can extend Fabricate to any method of representing structured information in Clojure, or override these defaults.
construct!
- Function
Run the tasks necessary to complete the website. Execute
produce
on every page, then runtasks
.- Arguments
- tasks
- {:keys [site.fabricate.api/entries
site.fabricate.api/options]
:as init-site}
- Returns
- Source
site/fabricate/api.clj
The construct! function uses the structured content assembled in the previous step to produce output pages for the site.
By default, they are HTML. Any other output format could be generated from the entries by specifying the appropriate produce! implementation.
produce!
- Multimethod
Produce the content of a file from the results of the
build
operation and write it to disk. Takes an entry and returns an entry.- Arguments
- Entry map
- Options map
- Returns
- An entry with the
:site.fabricate.page/location
key added. - Source
site/fabricate/api.clj
The produce! multimethod creates output for the entry passed as an argument. It dispatches on two keys:
:site.fabricate.document/format
:site.fabricate.page/format
The implementation will generate output of the given page format from data of the given document format. By default, this is a HTML file, generated from the Hiccup data built in the assemble step.
The produce! multimethod returns an entry with a key added: :site.fabricate.page/location
. This key indicates the URL or file path of a generated page.
In use
The API provides an elegant combination of extensibility and ease of use. This example is simplified from Fabricate's own page generation code.
(require [site.fabricate.api :as api]
[site.fabricate.dev.styles :as styles]
[site.fabricate.prototype.time :as time]
[site.fabricate.prototype.read :as read]
[site.fabricate.prototype.read.grammar :as grammar]
[site.fabricate.prototype.hiccup :as hiccup]
[dev.onionpancakes.chassis.core :as c])
Setting up with defmethod
Before running the API's 3 main functions, the build process defines methods for each of these multimethods.
1. api/collect
This implementation generates entries from each Fabricate template.
(defmethod api/collect "docs/**.fab"
[src options]
(mapv
(fn path->entry [p]
{:site.fabricate.document/format :hiccup
:site.fabricate.source/format :site.fabricate.read/v0
:site.fabricate.source/created (time/file-created p)
:site.fabricate.page/outputs
[{:site.fabricate.page/format :html
:site.fabricate.page/location
(fs/file (:site.fabricate.page/publish-dir
options))}]
:site.fabricate.source/modified (time/file-modified p)
:site.fabricate.api/source src
:site.fabricate.source/location (fs/file p)})
(fs/glob (System/getProperty "user.dir") src)))
2. api/build
This implementation of the build multimethod evaluates Fabricate's templates and produces Hiccup from the results.
(defmethod api/build [:site.fabricate.read/v0 :hiccup]
([{loc :site.fabricate.source/location :as entry} _opts]
(try (fabricate-v0->hiccup entry)
(catch Exception ex
(throw (ex-info (ex-message ex)
(assoc
(Throwable->map
ex
:site.fabricate.source/location
loc))))))))
3. api/produce!
This implementation of the produce! generates HTML from Hiccup elements.
(defmethod api/produce! [:hiccup :html]
[entry opts]
(hiccup->html entry opts))
Evaluating using the main API functions
Fabricate defines a few setup functions:
(def setup-tasks
[create-publish-dirs! get-css! copy-fonts!])
And some options:
(def options
"Options for building Fabricate's own documentation."
(let [d "html"] {:site.fabricate.page/publish-dir d}))
With all of that defined, this is all that Fabricate needs to do to generate its own documentation:
(->> {:site.fabricate.api/options options}
(api/plan! setup-tasks)
(api/assemble [])
(api/construct! []))