From 6914bfa420e1b482bc90e1193acde17bec14b7f9 Mon Sep 17 00:00:00 2001 From: Peter Amiri Date: Tue, 23 Jun 2026 19:41:12 -0700 Subject: [PATCH] docs(web/guides): document buttonTo input-prefix convention and schema cache MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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
, 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. Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: Peter Amiri --- .../v4-0-0/basics/views-layouts-partials.mdx | 3 ++- .../docs/v4-0-0/digging-deeper/caching.mdx | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/web/sites/guides/src/content/docs/v4-0-0/basics/views-layouts-partials.mdx b/web/sites/guides/src/content/docs/v4-0-0/basics/views-layouts-partials.mdx index ad28cd49ac..a39a249c73 100644 --- a/web/sites/guides/src/content/docs/v4-0-0/basics/views-layouts-partials.mdx +++ b/web/sites/guides/src/content/docs/v4-0-0/basics/views-layouts-partials.mdx @@ -140,7 +140,8 @@ Wheels 4.0 form helpers escape their input by default (see [Forms and Form Helpe The helpers you'll reach for most often in view code: -- `linkTo(route=..., key=..., text=..., class=...)` — renders an `` tag with the URL filled in from a named route. +- `linkTo(route=..., key=..., text=..., class=...)` — renders an `` tag with the URL filled in from a named route. `class`, `id`, and other HTML attributes pass straight through onto the anchor. +- `buttonTo(route=..., key=..., text=..., method="delete")` — renders a `` 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 ``. (`buttonTo` is the only link helper with this split — siblings like `linkTo` take `class`/`id` directly.) - `urlFor(route=..., key=...)` — returns just the URL string. Use when you need the URL outside an anchor (JavaScript, form action, `Location:` header). - `imageTag("logo.png", alt="Logo")` — renders `` with the path resolved against `public/images/`. - `styleSheetLinkTag("app.css")` — renders `` against `public/stylesheets/`. diff --git a/web/sites/guides/src/content/docs/v4-0-0/digging-deeper/caching.mdx b/web/sites/guides/src/content/docs/v4-0-0/digging-deeper/caching.mdx index 02832d6618..b60dcba505 100644 --- a/web/sites/guides/src/content/docs/v4-0-0/digging-deeper/caching.mdx +++ b/web/sites/guides/src/content/docs/v4-0-0/digging-deeper/caching.mdx @@ -201,6 +201,22 @@ For caches keyed by query arguments (the automatic `findAll(cache=N)` flavor), i Now the model callback can call `$removeFromCache(key="posts-listing")` surgically. 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. +## Schema and model-metadata cache + +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. + +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`. + +To pick up a schema change without killing the process, in order of preference: + +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. +2. **Rolling load-balancer restart.** Drain and restart each node behind the load balancer in turn, so the fleet is never fully down. +3. **Off-hours engine restart.** The blunt option: a full Lucee/CF restart clears everything. Fine when a maintenance window is acceptable. + + + ## Per-user keys 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: