Skip to content

Commit 5056ea4

Browse files
bpamiriPeter Amiriclaude
authored
docs(web/guides): document buttonTo input-prefix convention and schema cache (#3250)
Two v4 guide gaps surfaced while reviewing GitHub discussions: - buttonTo's input-prefixed attribute convention (inputClass/inputId/inputRel) was documented in v3 guides but dropped in the v4-0-0 restructure (#1352). Added a buttonTo entry to the view-helpers reference explaining that button attributes are input-prefixed because the button renders inside a <form>, and that siblings like linkTo take class/id directly. - The per-model schema/column-metadata cache in application.wheels.models — the cache that goes stale on a live ALTER TABLE and which $clearCache() cannot rebuild — was undocumented (#1481). Added a "Schema and model-metadata cache" section to the caching guide with reload/restart recovery options. Signed-off-by: Peter Amiri <petera@pai.com> Co-authored-by: Peter Amiri <petera@pai.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent fe5fc42 commit 5056ea4

2 files changed

Lines changed: 18 additions & 1 deletion

File tree

web/sites/guides/src/content/docs/v4-0-0/basics/views-layouts-partials.mdx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ Wheels 4.0 form helpers escape their input by default (see [Forms and Form Helpe
140140

141141
The helpers you'll reach for most often in view code:
142142

143-
- `linkTo(route=..., key=..., text=..., class=...)` — renders an `<a>` tag with the URL filled in from a named route.
143+
- `linkTo(route=..., key=..., text=..., class=...)` — renders an `<a>` tag with the URL filled in from a named route. `class`, `id`, and other HTML attributes pass straight through onto the anchor.
144+
- `buttonTo(route=..., key=..., text=..., method="delete")` — renders a `<form>` wrapping a single submit button. Reach for it when an action must POST/PUT/PATCH/DELETE rather than GET (e.g. a "Delete" button). Because the button lives *inside* a form, attributes for the **button itself** are prefixed with `input` — use `inputClass`, `inputId`, `inputRel`, **not** `class`/`id`, e.g. `buttonTo(text="Delete", route="post", key=post.id, method="delete", inputClass="btn btn-danger")`. Unprefixed `class`/`id` land on the wrapping `<form>`. (`buttonTo` is the only link helper with this split — siblings like `linkTo` take `class`/`id` directly.)
144145
- `urlFor(route=..., key=...)` — returns just the URL string. Use when you need the URL outside an anchor (JavaScript, form action, `Location:` header).
145146
- `imageTag("logo.png", alt="Logo")` — renders `<img>` with the path resolved against `public/images/`.
146147
- `styleSheetLinkTag("app.css")` — renders `<link rel="stylesheet">` against `public/stylesheets/`.

web/sites/guides/src/content/docs/v4-0-0/digging-deeper/caching.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,22 @@ For caches keyed by query arguments (the automatic `findAll(cache=N)` flavor), i
201201
Now the model callback can call `$removeFromCache(key="posts-listing")` surgically.
202202
3. **Bust the engine's query cache with a reload.** `$clearCache(category="query")` does **not** invalidate finder caches — as noted above, `findAll(cache=N)` results live in the CFML engine's native query cache, not in `application.wheels.cache`. The blunt instruments that actually work are `?reload=true&password=...` (which rotates the cache-key comment Wheels embeds in every cached query's SQL, invalidating all engine-cached results) or an application restart.
203203

204+
## Schema and model-metadata cache
205+
206+
Separate from `application.wheels.cache` — the store everything above talks about — Wheels keeps a second, longer-lived cache that `$clearCache()` cannot reach: each model's **table and column metadata**. The first time a model is used, Wheels runs `cfdbinfo` against its table and caches the column list, types, and defaults in `application.wheels.models` for the lifetime of the application. That's what lets `model("Post").new()` know its properties without a database round-trip on every request.
207+
208+
The flip side: a **live schema change goes stale instantly**. Drop or rename a column with `ALTER TABLE` against a running app and the next request fails with a "column not found" error (or silently ignores the new column) — the model is still working from the metadata it read at startup. `$clearCache()`, `clearQueryCacheOnReload`, and `clearTemplateCacheOnReload` do **not** rebuild it; only a reload or a restart re-runs `cfdbinfo`.
209+
210+
To pick up a schema change without killing the process, in order of preference:
211+
212+
1. **Migrate, then reload.** Apply the change through a [migration](/v4-0-0/basics/migrations/) and reload each node with `?reload=true&password=...` — the reload re-reads every model's schema. With more than one app server, reload them one at a time for a zero-downtime rollout.
213+
2. **Rolling load-balancer restart.** Drain and restart each node behind the load balancer in turn, so the fleet is never fully down.
214+
3. **Off-hours engine restart.** The blunt option: a full Lucee/CF restart clears everything. Fine when a maintenance window is acceptable.
215+
216+
<Aside type="caution">
217+
Don't reach into `application.wheels.models` and delete entries by hand to force a re-read — it is unsupported and races with in-flight requests. Use a reload; it rebuilds the metadata cleanly.
218+
</Aside>
219+
204220
## Per-user keys
205221

206222
A cached action is shared across every request for the same URL — which is a problem the moment the rendered output depends on the logged-in user. Use `appendToKey` to mix more identifiers into the cache key:

0 commit comments

Comments
 (0)