The component registry.
In stube, a component is a plain map of the form
{:component/id :auth/login
:component/doc "Prompt for credentials."
:component/init (fn [args] state-map)
:component/render (fn [self] hiccup)
:component/handle (fn [self event] [self' effects])
:component/keep #{:signal-keys}
:component/start (fn [self] [self' effects])
:component/stop (fn [self] [self' effects])
:component/wakeup (fn [self] [self' effects])
:component/children {:slot/x embed-spec}
:on-foo (fn [self answer-value] [self' effects])
…}
Behaviour lives in the values; the key under which the kernel finds them
is by namespaced convention. Resume keys (:on-foo, :on-step-3, …)
are looked up dynamically when an [:answer …] effect pops a child
frame: the parent's :instance/resume value names the function to call.
Author keys (:init, :render, :handle, :keep, :doc, :state,
:start, :stop, :wakeup, :children, :url, :styles,
:modules) are lifted to their :component/<name> homes by
register! so every cdef the kernel reads has a single uniform
namespace.
:styles is an optional inline CSS string for this component; it is
scoped at head-emit time by replacing & with the component's
[data-stube-component="ns/name"] selector. :modules is an
optional vector of JS module ids (e.g. ["notes/zoom"]) that should
be loaded as <script type="module"> whenever this component is
registered.
Two kinds of keys, two rules. Lifecycle keys (:init, :render,
:handle, etc.) are a closed set: the framework owns them, and the
kernel only consults their :component/<name> form, so the
bare-form/lifted-form pair must agree. Resume keys (:on-foo,
:on-error-foo) are open: component authors invent new ones per
call site, and the kernel looks them up by exact name, so they pass
through verbatim with no namespacing. Adding a new colocated key
is a framework-author change — extend colocated-keys and the
AGENTS/docs entries together; user-author code never reaches for
this.
The registry maps :component/id to the component map. It is held in a
single atom so component definitions can be evaluated at namespace load
time the same way defmulti defmethods are.
The component registry.
In stube, a *component* is a plain map of the form
{:component/id :auth/login
:component/doc "Prompt for credentials."
:component/init (fn [args] state-map)
:component/render (fn [self] hiccup)
:component/handle (fn [self event] [self' effects])
:component/keep #{:signal-keys}
:component/start (fn [self] [self' effects])
:component/stop (fn [self] [self' effects])
:component/wakeup (fn [self] [self' effects])
:component/children {:slot/x embed-spec}
:on-foo (fn [self answer-value] [self' effects])
…}
Behaviour lives in the values; the key under which the kernel finds them
is by namespaced convention. Resume keys (`:on-foo`, `:on-step-3`, …)
are looked up dynamically when an `[:answer …]` effect pops a child
frame: the parent's `:instance/resume` value names the function to call.
Author keys (`:init`, `:render`, `:handle`, `:keep`, `:doc`, `:state`,
`:start`, `:stop`, `:wakeup`, `:children`, `:url`, `:styles`,
`:modules`) are lifted to their `:component/<name>` homes by
[[register!]] so every cdef the kernel reads has a single uniform
namespace.
`:styles` is an optional inline CSS string for this component; it is
scoped at head-emit time by replacing `&` with the component's
`[data-stube-component="ns/name"]` selector. `:modules` is an
optional vector of JS module ids (e.g. `["notes/zoom"]`) that should
be loaded as `<script type="module">` whenever this component is
registered.
Two kinds of keys, two rules. *Lifecycle* keys (`:init`, `:render`,
`:handle`, etc.) are a closed set: the framework owns them, and the
kernel only consults their `:component/<name>` form, so the
bare-form/lifted-form pair must agree. *Resume* keys (`:on-foo`,
`:on-error-foo`) are open: component authors invent new ones per
call site, and the kernel looks them up by exact name, so they pass
through verbatim with no namespacing. Adding a new colocated key
is a framework-author change — extend `colocated-keys` and the
AGENTS/docs entries together; user-author code never reaches for
this.
The registry maps `:component/id` to the component map. It is held in a
single atom so component definitions can be evaluated at namespace load
time the same way `defmulti` defmethods are.(all)Snapshot of every registered component, keyed by id. Handy from the REPL; not used internally.
Snapshot of every registered component, keyed by id. Handy from the REPL; not used internally.
(clear!)Drop every component from the registry. Intended for tests.
Drop every component from the registry. Intended for tests.
(help id)Return the docstring registered for component id, or nil.
Return the docstring registered for component `id`, or nil.
(lookup id)Return the component map for id, or nil if none is registered.
Return the component map for `id`, or nil if none is registered.
(lookup! id)Like lookup but throws if the component is unknown. Used by the
kernel where a missing component is an unrecoverable bug.
Like [[lookup]] but throws if the component is unknown. Used by the kernel where a missing component is an unrecoverable bug.
(register! cdef)Add or replace a component definition. Returns the registered map.
Colocated author keys (:init, :render, :handle, :keep, :doc,
:state, :start, :stop, :wakeup, :children) are lifted to
:component/<name> here, so the kernel can read every cdef under a
single namespace regardless of which entry point produced it
(defcomponent macro, register-component! function, or
decorate!).
Add or replace a component definition. Returns the registered map. Colocated author keys (`:init`, `:render`, `:handle`, `:keep`, `:doc`, `:state`, `:start`, `:stop`, `:wakeup`, `:children`) are lifted to `:component/<name>` here, so the kernel can read every cdef under a single namespace regardless of which entry point produced it (`defcomponent` macro, `register-component!` function, or `decorate!`).
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |