ringroad-renderer (crates/ringroad-renderer/) is the rendering engine
for the Ringroad storefront. It is extracted from the monolithic API binary
so it can be tested, reused, and iterated on independently, without a
database connection.
Design constraints
- Zero database dependency. All data access flows through the
DataSourcestrait. The API binary (crates/ringroad) implements it with SeaORM queries against PostgreSQL; tests use a stub implementation with fixed fixtures. - One engine per API process.
Engineis built once at startup from athemes/directory, holding all loaded themes, the MiniJinja environment with every template pre-compiled, and the sharedDataSourceshandle. - Lenient by default. MiniJinja runs with
UndefinedBehavior::Lenient, so merchant-edited templates that reference missing variables render as empty strings instead of crashing. - Lazy data graph. The old two-pass metafield collector is replaced by
lazy
Objectwrappers: data loads on first property access, insidetokio::task::spawn_blocking, cached per request.
Module map
| Module | Purpose |
|---|---|
engine/ |
MiniJinja environment, filters, page rendering orchestration, template resolution, HTML/script emission |
section/ |
Node schemas, page configs, recursive node resolution and rendering, node objects |
schema/ |
The 36-type settings system, validation, defaults, color cascade |
theme/ |
theme.json manifest loading and the theme-directory walk (schema/stylesheet/javascript extraction) |
data/ |
Domain types, the DataSources trait, lazy Object wrappers, storefront context |
asset/ |
CSS variable generation from color settings, .jinja asset rendering |
cli/ |
serve (dev server), build (static assets), init (scaffold a theme) |
keys.rs |
Reserved config keys: @root:settings, @root:header-group, @root:footer-group |
error.rs |
Crate-wide Error enum and Result alias |
The Engine lifecycle
flowchart LR
D[themes/ dir] --> W[theme::loader::walk_themes]
W -->|per .jinja file| R[register template in MiniJinja]
W -->|"{% schema %}"| M[ThemeManifest.nodes]
W -->|stylesheet/js blocks| P[ThemeAssetPipeline]
W --> E[Engine]
DS[DataSources impl] --> E
E --> PG[render_page]
E --> FR[render_node_fragment]
E --> BA[build_assets]
Engine::new_from_dir(ds, themes_dir, global_scripts):
- creates the MiniJinja environment and sets
UndefinedBehavior::Lenient, - registers the four custom filters (
money,pluralize,newlines_to_br,render_text) and therender_nodefunction, - walks every theme subdirectory: registers all
.jinjatemplates under"{slug}/{dir}/{relpath}", extracts{% schema %}blocks into node schemas, and extracts{% stylesheet %}/{% javascript %}blocks into a per-theme asset pipeline, - validates every setting in every schema, a bad theme fails startup,
- stores manifests, pipelines, and the
DataSourceshandle.
The loader strips the extension blocks before MiniJinja compiles anything (see Theme loading).
What the crate is used for
- Storefront pages,
Engine::render_page()renders the full page (header-group + page nodes + footer-group + layout shell). - Preview / hot-swap,
render_node_fragment()renders one node’s subtree in isolation for the editor’s setting-change swap. - Static asset builds,
build_assets()emits content-hashedcombined.{hash}.css/.jsfor CDN caching. - CLI development,
ringroad-renderer serveboots a stub-context preview server that hot-reloads on theme edits.
The test suite (tests/) defends the contracts end to end: schema
parsing, page rendering, section trees, CSS subsetting, asset builds, data
sources, and a full smoke test over the real themes/default.