Liking cljdoc? Tell your friends :D

stube as a Kit module

kit-clj projects can add stube the same way they add SQL, HTML, or any other capability: through the module system. This repo ships a :stube/kernel module so that one REPL call drops a working, server-driven stube widget into an existing Kit app — and one call takes it back out.

If you'd rather wire stube by hand (it's only three lines of system.edn), skip to The manual equivalent below; the module just automates exactly that.


What the module does

Installing :stube/kernel performs four idempotent changes to your Kit project:

TargetChange
deps.ednmerges dev.zeko/stube {:mvn/version "0.7.0"} into :deps
resources/system.ednmerges :stube/kernel and :reitit.routes/stube Integrant keys
src/clj/<proj>/core.cljappends requires for dev.zeko.stube.kit and <proj>.stube
src/clj/<proj>/stube.cljnew file — a starter counter component

:stube/kernel builds an isolated stube runtime (see dev.zeko.stube.embed/make-kernel). :reitit.routes/stube is contributed by stube's Integrant adapter, dev.zeko.stube.kit; because it derives from :reitit/routes, Kit's #ig/refset :reitit/routes picks it up automatically with no extra wiring.

The :mounts entry maps a page URL to a registered root component, so GET /stube returns a complete stube shell (HTML page + Datastar + the SSE bootstrap) out of the box.

resources/system.edn
─────────────────────────────────────────────────────────────
 :stube/kernel  { :base-path "/stube" }
       │  dev.zeko.stube.kit  →  embed/make-kernel
       ▼
 :reitit.routes/stube  { :kernel #ig/ref :stube/kernel
                         :mounts { "/stube" :<proj>/counter } }
       │  derive :reitit.routes/stube :reitit/routes
       ▼
 :router/routes  { :routes #ig/refset :reitit/routes }   ← auto-collected
       ▼
 :router/core → :handler/ring → :server/http

src/clj/<proj>/core.clj  (:require …)
─────────────────────────────────────────────────────────────
  [dev.zeko.stube.kit]   ; registers the two ig/init-keys
  [<proj>.stube]         ; registers the :<proj>/counter component

Quick start

1. Point your project at this repo

Kit reads module repositories from your project's kit.edn. Add the stube repo alongside the official one:

;; kit.edn
{:full-name "com.example/myapp"
 :ns-name   "com.example.myapp"
 :sanitized "com/example/myapp"
 :name      "myapp"
 :modules
 {:root "modules"
  :repositories
  [{:url  "https://github.com/kit-clj/modules.git" :tag "master" :name "kit-modules"}
   {:url  "https://github.com/zekzekus/stube.git"  :tag "master" :name "stube"}]}}

Kit clones this repo to modules/stube/ and finds the modules.edn at its root, which registers :stube/kernel and resolves its files under kit/stube/.

2. Sync and install from the REPL

Kit aliases its generator as kit in the user namespace:

user=> (kit/sync-modules)
:done

user=> (kit/list-modules)
;; … the official modules …
:stube/kernel - embeds a stube (Datastar) widget — kernel, Reitit routes, and a starter component mounted at /stube
:done

user=> (kit/install-module :stube/kernel)
updating file: deps.edn
updating file: resources/system.edn
updating file: src/clj/com/example/myapp/core.clj
applying
 action: :append-requires
 value: ["[dev.zeko.stube.kit]" "[com.example.myapp.stube]"]
stube installed. Restart the REPL, run (go), and open http://localhost:3000/stube
:stube/kernel installed successfully!
restart required!

3. Restart and visit

Because the module adds a Maven dependency, the JVM must be restarted:

user=> (go)   ; after a REPL restart

Open http://localhost:3000/stube — that's the starter counter, served entirely by stube. No JavaScript build step, no client/server contract to hand-maintain.


Building your own components

Edit the generated src/clj/<proj>/stube.clj. A component is a map of pure functions; the kernel is one fold over plain Clojure maps:

(s/defcomponent :myapp/greeting
  :init   (fn [_] {:name "world"})
  :render (fn [self]
            [:div (s/root-attrs self)
             [:input (s/bind self :name)]
             [:p "Hello, " (:name self) "!"]]))

Then mount it by adding to :reitit.routes/stube's :mounts map in resources/system.edn:

 :reitit.routes/stube
 {:kernel #ig/ref :stube/kernel
  :mounts {"/stube"  :myapp/counter
           "/hello"  :myapp/greeting}}

New components in other namespaces only need to be required somewhere that loads at startup (e.g. add them to the :require block in core.clj, next to [<proj>.stube]) so s/defcomponent registers them before ig/init runs.

See the tutorial for the full component model — call/answer, defflow, history, uploads, pub/sub — and the API reference for every public function.


Configuration

:stube/kernel accepts the full option set of embed/make-kernel. The defaults are deliberately complete (in-memory store, cookie-based stube_sid sessions, nil app context), so the module only sets :base-path. Common additions:

 :stube/kernel
 {:base-path     "/stube"
  ;; opaque per-request app value, read with (s/app):
  :context-fn    #ig/ref :app/context-fn
  ;; persisted identity, read with (s/principal):
  :principal-fn  #ig/ref :app/principal-fn
  ;; reuse your app's session id instead of stube's own cookie:
  :session-id-fn #ig/ref :app/session-id-fn}

Each #ig/ref points at an Integrant key you define in your own app that returns a function value. See the Application boundaries section of the API reference for the :context-fn / :principal-fn contract.

:base-path only prefixes stube's generated SSE/asset/event URLs; the page mount paths in :mounts are independent and can live anywhere in your route tree.

Rendering stube inside your own Kit pages

The :mounts shell is a standalone HTML page. To embed a widget inside a page your app already renders (e.g. a :kit/html Selmer page), drop the :mounts entry and instead, from a page handler, call embed/mint-conversation!, splice embed/shell-for into the body, and embed/head-tags into the <head>. The README's embedding section and examples/dev/zeko/stube/examples/embedded_ring.clj show the pattern. Non-chassis renderers (hiccup2/rum/reagent) must re-wrap the chassis RawString nodes head-tags returns — see Rendering head-tags through hiccup2 / rum / reagent.


Removing the module

user=> (kit/remove-module :stube/kernel)

Kit reverses the injections and deletes the asset it created (using the install-log checksums to avoid clobbering files you've since edited). If you customized stube.clj, Kit warns instead of deleting; remove it by hand if you no longer want it, then drop the dev.zeko/stube dep and the :stube/* keys if the remover left them.


The manual equivalent

The module is pure convenience. Wiring stube into a Kit/Integrant app by hand is three lines of system.edn plus one require:

;; resources/system.edn
{:stube/kernel
 {:base-path "/stube"}

 :reitit.routes/stube
 {:kernel #ig/ref :stube/kernel
  :mounts {"/stube" :myapp/counter}}}
;; src/clj/myapp/core.clj — require once so the ig multimethods install
(:require … [dev.zeko.stube.kit] [myapp.stube])

plus dev.zeko/stube in deps.edn. stube core has no Integrant dependency; dev.zeko.stube.kit is the only namespace that touches integrant.core, and Kit projects already depend on Integrant.

Can you improve this documentation?Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close