14  Developing Pocket

Last modified: 2026-02-08

This chapter is for contributors and curious readers. It describes how Pocket’s documentation is built, how tests are structured, and the conventions the notebooks follow.

(ns pocket-book.developing-pocket
  (:require
   ;; Annotating kinds of visualizations:
   [scicloj.kindly.v4.kind :as kind]
   ;; String utilities:
   [clojure.string :as str]))

Rendering notebooks

Pocket’s documentation is a Clay book — a collection of Clojure source files that Clay evaluates and renders as HTML (via Quarto) or as GitHub Flavored Markdown.

The chapter list lives in notebooks/chapters.edn:

(->> "notebooks/chapters.edn"
     slurp
     clojure.edn/read-string)
["getting_started"
 "configuration"
 "logging"
 "recursive_caching_in_pipelines"
 "usage_practices"
 "real_world_walkthrough"
 "concurrency"
 "extending_pocket"
 "ml_workflows"
 "pocket_model"
 "pocket_pipeline"
 "cache_keys"
 "developing_pocket"
 "api_reference"]

Each entry maps to a file under notebooks/pocket_book/. For example, "getting_started" corresponds to notebooks/pocket_book/getting_started.clj.

The rendering helpers live in notebooks/dev.clj. From a REPL with the :dev alias:

(require '[dev :as dev] :reload)

;; Render the full HTML book (opens a browser):
(dev/make-book!)

;; Render one or more chapters as GitHub Flavored Markdown
;; (useful for reviewing in a terminal or diff tool):
(dev/make-gfm! "pocket_book/getting_started.clj")

The Quarto configuration is in clay.edn at the project root.

Running tests

The test suite combines hand-written tests with tests generated from notebooks:

./run_tests.sh

This runs clojure -M:dev:test -m cognitect.test-runner, which scans the test/ directory recursively. It picks up:

  • test/scicloj/pocket_test.clj — hand-written unit tests
  • test/pocket_book/*_generated_test.clj — tests generated from notebook kind/test-last annotations (see below)

Literate testing with kind/test-last

Clay notebooks can embed inline assertions using kind/test-last. A form like:

(+ 1 2)

(kind/test-last [= 3])

asserts that the result of the previous form equals 3. The kind/test-last form is invisible in the rendered documentation — readers see only the code and its output.

When Clay renders the notebook, it generates a standard clojure.test file as a side effect. For example, a notebook notebooks/pocket_book/foo.clj produces test/pocket_book/foo_generated_test.clj.

Here is a live example:

(def demo-value (+ 10 20))
demo-value
30

The assertion above is invisible in the rendered output — but it generated a deftest that runs with every test suite execution.

The predicate can be any function. Common patterns:

(type demo-value)
java.lang.Long
(str "result is " demo-value)
"result is 30"

API Reference with kind/doc

The API Reference chapter uses kind/doc to render a var’s docstring as formatted documentation:

(kind/doc #'clojure.core/map)

map

[f]

[f coll]

[f c1 c2]

[f c1 c2 c3]

[f c1 c2 c3 & colls]

Returns a lazy sequence consisting of the result of applying f to the set of first items of each coll, followed by applying f to the set of second items in each coll, until any one of the colls is exhausted. Any remaining items in other colls are ignored. Function f should accept number-of-colls arguments. Returns a transducer when no collection is provided.

Each kind/doc call produces a documentation block from the var’s metadata (docstring, arglists, etc.). Live code examples follow naturally after each block.

The API Reference namespace uses a metadata hint to hide the kind/doc source code in the rendered output:

^{:kindly/hide-code true
  :kindly/options {:kinds-that-hide-code #{:kind/doc}}}
(ns pocket-book.api-reference ...)

This means readers see the formatted docstring but not the (kind/doc ...) call that produced it.

source: notebooks/pocket_book/developing_pocket.clj