Usage Overview¶
kBars is one library, split into three modules by how you get your context data in:
graph TD
core["kbars<br/>(parser, renderer, transforms; KpaStruct context)"]
kxs["kbars-kxs<br/>(kotlinx.serialization JSON)"]
yamlkt["kbars-yamlkt<br/>(YamlKt YAML)"]
kxs --> core
yamlkt --> core
- You already have a
KpaStructcontext (perhaps built by hand, or via kPointer adapters for a format not listed below) → usekbarsdirectly. - You have JSON text → use
kbars-kxs. - You have YAML text → use
kbars-yamlkt.
Both format modules re-export kbars as a transitive api dependency, so declaring the one format
artifact is enough.
Adding a dependency¶
kBars is published on Maven Central under the com.commonsware.kbars group.
Then reference it from your module:
Rendering, in three steps¶
Whichever module you use, the shape is the same: build an environment, compile a template, render.
// 1. Build an environment from your data
val env = kbEnvironment.from("""{"name":"Alice"}""") // kbars-kxs
// 2. Compile a template
val template = kbTemplate.create("Hello {{ /name }}!")
// 3. Render — templates are reusable across renders (not thread-safe; reuse sequentially)
println(template.render(env)) // "Hello Alice!"
kbEnvironment holds the render context (your data), the registered transforms, the registered
@env properties, and an optional partial loader. kbTemplate is a compiled, reusable template: it
parses once, caching its AST and identifier classification, so rendering the same template many
times — each with a distinct kbEnvironment — re-parses nothing after the first render.
Building directly against a KpaStruct (no JSON/YAML parsing) looks like this:
import com.commonsware.kpointer.adapter.KpaStruct
val context: KpaStruct = /* your context */
val env = kbEnvironment.create(context)
val template = kbTemplate.create("Hello {{ /name }}!")
println(template.render(env)) // "Hello Alice!"
Threading, lifecycle, and cost model¶
The rest of this section is the short version of the questions that come up first in server-side use:
how long to keep a KbTemplate around, whether it's safe to share, and what actually costs time.
Compile once, render many. KbTemplate.create doesn't parse anything — parsing is deferred to the
first render call, and the resulting AST (plus per-identifier classification and compiled-pointer
caches) is retained on the KbTemplate instance afterward. A KbTemplate is meant to be built once
per distinct template source — at startup, or the first time a given CMS-authored template is used —
and reused for every subsequent render of that same source, each with its own KbEnvironment.
Re-create-ing a KbTemplate from the same source string on every request throws away that caching
for no benefit.
A KbTemplate is not thread-safe; reuse must be sequential. One render call must complete before
the next begins on the same instance — concurrent renders from multiple threads against a single
KbTemplate are not supported. If your server renders concurrently, either give each thread/coroutine
its own KbTemplate instance (cheap relative to a fresh parse, since parsing happens once regardless,
but still a separate cache) or serialize access to a shared instance; there is no internal locking to
fall back on.
A KbEnvironment is cheap and disposable — build a fresh one per render. Unlike KbTemplate,
KbEnvironment.create/fromJson/fromYaml are meant to be called once per render: they carry the
render-specific context data, so sharing one across concurrent renders would mean sharing that data
too. The registered transforms and @env properties are configured once per environment build (via
the configure lambda) — there's no separate "compile" step to amortize the way there is for a
template's parse.
Where the cost actually goes: parsing and per-identifier classification (once per distinct template
source, amortized across every render); building an environment (proportional to how much you register
in configure, done once per render); and the render itself (proportional to output size and how much
of the context the template walks — {{#each}} over a large list has no engine-imposed cap, so a
template's cost scales with the data it's handed). See Rendering Untrusted Content for
the resource-exhaustion angle on that last point if the template source or context size is untrusted.
Where to next¶
- Data Binding — feeding JSON, YAML, or a
@Serializableclass intokbars-kxs/kbars-yamlkt, and when to pick each. - Custom Transforms — writing and registering a
KbTransform. - Custom Partials — registering a
KbPartialLoader. - Environment Properties — registering
@envproperties and configuring thenowclock. - Error Handling — the exception surface for app authors, and turning a
KbRenderExceptioninto a good user-facing message. - Rendering Untrusted Content — recursion limits, output-size bounds, and other properties relevant when data or template source come from outside your app.
- Writing Templates — the template-authoring side of kBars, if you're also the one writing the templates.