Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions cli/lucli/templates/app/app/snippets/CRUDContent.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@
*/
function config() {
super.config();
// 404 rather than a 500 when the key matches nothing. findByKey returns a
// non-object for BOTH a missing key and a soft-deleted row, and the
// actions below then operate on that empty value — `post.title` in the
// view, or `.update()` here, throws.
//
// A BEFORE filter that deliberately does NOT assign the record: the
// actions keep their conventional single-finder body, which the
// `--belongsTo` parent wiring depends on — it only rewrites `show()`
// when the finder is the whole body, and skips a customized one.
// Loading twice is cheap and keeps that contract intact.
filters(through="requireRecord", only="show,edit,update,delete");
}

/**
Expand Down Expand Up @@ -81,4 +92,21 @@
redirectTo(route="|ObjectNamePlural|");
}

/**
* Guard: abort with a 404 when the key matches no record.
*
* The message is a plain string on purpose. The scaffold's parent wiring
* scans this file with ScaffoldSource, which refuses interpolated strings
* (`##...##`) wholesale — a single `##params.key##` here would silently turn
* off `include=` wiring for every `--belongsTo` child of this model.
**/
private function requireRecord() {
if (!IsObject(model("|ObjectNameSingularC|").findByKey(key=params.key))) {
Throw(
type = "Wheels.RecordNotFound",
message = "|ObjectNameSingularC| not found for the requested key."
);
}
}

}
19 changes: 19 additions & 0 deletions cli/lucli/tests/specs/commands/NewCommandTemplateSpec.cfc
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,25 @@ component extends="wheels.wheelstest.system.BaseSpec" {
expect(fileExists(templateRoot & "tests/specs/models/.gitkeep")).toBeTrue();
});

it("ships app/snippets/CRUDContent.txt identical to the bundled codegen template", () => {
// `wheels new` copies every codegen template into the app's
// app/snippets/, and Templates.cfc resolves THOSE first — they
// shadow the bundled copy. So a fix to templates/codegen/
// CRUDContent.txt is invisible to every freshly generated app
// unless the snippet copy moves with it. That is exactly how the
// 404 guard shipped in a release and then failed to appear in a
// stock `wheels new` app: the two files had silently diverged.
//
// Only this pair is pinned. Two other twins differ on purpose
// (the app copies read the reload password from .env), so a
// blanket "all snippets match codegen" rule would be wrong.
var bundled = fileRead(expandPath("/cli/lucli/templates/codegen/CRUDContent.txt"));
var shipped = fileRead(templateRoot & "app/snippets/CRUDContent.txt");
expect(compare(shipped, bundled)).toBe(0);
// And the shipped copy must actually carry the guard.
expect(shipped).toInclude('filters(through="requireRecord"');
});

});

}
Expand Down
Loading