Skip to content

fix(cli): generate api-resource resolves objectName placeholders - #2566

Merged
bpamiri merged 1 commit into
developfrom
fix/bot-2468-wheels-generate-api-resource-leaves-unresolved-tem
May 11, 2026
Merged

fix(cli): generate api-resource resolves objectName placeholders#2566
bpamiri merged 1 commit into
developfrom
fix/bot-2468-wheels-generate-api-resource-leaves-unresolved-tem

Conversation

@wheels-bot

@wheels-bot wheels-bot Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor

Summary

wheels generate api-resource was emitting controllers containing literal #objectNamePlural# and #objectNameSingular# placeholders. The framework-level snippet at app/snippets/ApiControllerContent.txt (which takes precedence over the CLI-bundled template via Templates.findTemplate()) still used the legacy hash-token form. The CLI's Templates.processTemplate() only substitutes pipe-delimited tokens (|ObjectNameSingular|, |ObjectNamePlural|), so the hash tokens passed through untouched and landed verbatim in the generated controller. Aligning the framework snippet with the already-corrected CLI-bundled copy fixes the bug.

Related Issue

Closes #2468

Type of Change

  • Bug fix
  • New feature
  • Enhancement to existing feature
  • Documentation update
  • Refactoring

Feature Completeness Checklist

  • Tests -- vendor/wheels/tests/specs/cli/ApiControllerSnippetSpec.cfc reproduces the bug (legacy tokens present) and locks in the fix (pipe tokens present). Verified failing-then-passing locally.
  • Framework Docs -- Handled separately by bot-update-docs.yml if needed
  • AI Reference Docs -- Handled separately by bot-update-docs.yml if needed
  • CLAUDE.md -- Handled separately by bot-update-docs.yml if needed
  • CHANGELOG.md -- Entry added under [Unreleased] -> Fixed
  • Test runner passes -- Full core suite via curl /wheels/core/tests: 3430 pass, 0 fail, 0 error, 16 skipped across 193 bundles

Test Plan

  1. Failing-spec capture: with the legacy #objectNamePlural# / #objectNameSingular# tokens in app/snippets/ApiControllerContent.txt, the new spec fails with Snippet still contains legacy token #objectNamePlural# -- CLI Templates.processTemplate() doesn't substitute this form.
  2. After replacing the legacy tokens with |ObjectNamePlural| / |ObjectNameSingular|, the spec passes.
  3. Full core suite still passes (3430/3446 with 16 pre-existing skips).
  4. Manual: wheels generate api-resource Product name price:decimal sku:string now emits a controller with local.products = model("product").findAll(); instead of unresolved placeholders.

Screenshots / Output

$ curl -s "http://localhost:60007/wheels/core/tests?db=sqlite&format=json&directory=wheels.tests.specs.cli"
# pass=1 fail=0 error=0 (ApiControllerSnippetSpec)

@wheels-bot

wheels-bot Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — No doc updates

Reviewed this PR's diff and found no docs that need updating (purely internal bug fix — the snippet template at app/snippets/ApiControllerContent.txt used a legacy hash-token format that the CLI's Templates.processTemplate() does not substitute; replacing those tokens with the pipe-delimited form the processor understands is a mechanical correctness fix with no user-visible convention change, and the v4 guide already documents the intended — now correctly produced — generated controller output).

@wheels-bot wheels-bot Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wheels Bot — Reviewer A

TL;DR: This is a minimal, targeted bug fix that aligns the framework's app/snippets/ApiControllerContent.txt — which takes precedence over the CLI-bundled templates via Templates.findTemplate() — with the already-correct pipe-delimited token format that Templates.processTemplate() understands. The root cause, the fix, and the regression spec are all correct. I verified all three template copies (framework snippet, cli/lucli/templates/, and cli/src/templates/) now use |ObjectNamePlural| / |ObjectNameSingular| consistently. Verdict: comment (one minor spec nit, no blocking issues).


Correctness

The fix is accurate. Templates.processTemplate() handles |ObjectNamePlural| / |ObjectNameSingular| at lines 172-176 of cli/lucli/services/Templates.cfc:

processed = replace(processed, "|ObjectNameSingular|", lCase(modelName), "all");
processed = replace(processed, "|ObjectNamePlural|", lCase(variables.helpers.pluralize(modelName)), "all");

The hash-token form #objectNamePlural# is never substituted anywhere in the processor, leaving it verbatim in generated controllers exactly as issue #2468 reported. Replacing with pipe tokens eliminates the bug.

I verified cli/src/templates/ApiControllerContent.txt and cli/lucli/templates/app/app/snippets/ApiControllerContent.txt both already used pipe tokens; only the framework's own app/snippets/ copy lagged behind. The PR description's claim about root cause is accurate.


Tests

Minor nit — single it block bundles two independent assertions.

vendor/wheels/tests/specs/cli/ApiControllerSnippetSpec.cfc:17:

it("uses pipe-delimited tokens the CLI Templates processor understands", function() {
    expect(content contains legacyPlural).toBeFalse(...);
    expect(content contains legacySingular).toBeFalse(...);
    expect(content contains "|ObjectNamePlural|").toBeTrue(...);
    expect(content contains "|ObjectNameSingular|").toBeTrue(...);
});

Bundling the absence-of-legacy and presence-of-pipe assertions in one it means that if the plural legacy token is fixed but the singular is still present, the failure message is the only signal pointing at which specific assertion fired. Splitting into two it blocks (one for absence, one for presence) would produce cleaner test output. Not a blocker.

The BDD structure (extends wheels.WheelsTest, describe/it with closures) is correct, and placing the spec under vendor/wheels/tests/specs/cli/ means it is auto-discovered by the core test runner. The ## concatenation trick to build the legacy token string without CFML interpreting it as an expression delimiter is correct:

var legacyPlural = "##" & "objectNamePlural" & "##";   // -> #objectNamePlural# at runtime

Commits

Single commit: fix(cli): generate api-resource resolves objectName placeholders

Type fix, scope cli (commitlint allowlist), subject lowercase, under 100 chars. No issues.


Docs

CHANGELOG entry under [Unreleased] -> Fixed is present and accurate. No .ai/wheels/ doc changes are needed — this is a data file fix, not a behavioral API change.

@wheels-bot

wheels-bot Bot commented May 11, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Reviewer B (round 1)

A's review is accurate and well-reasoned. The root-cause analysis, the fix verification, and the spec assessment are all correct. One trivial line-number inaccuracy noted below, but nothing that changes the substance. Converging on approve — the single nit A raised is explicitly non-blocking and the fix itself is clearly correct.

Sycophancy

None detected. A gave a comment verdict (not approve) while citing only a non-blocking nit, which is appropriately conservative rather than a rubber stamp.

False positives

None detected, with one cosmetic note:

  • A cited Templates.processTemplate() replacements at "lines 172-176." The actual lines are 173-174 (singular/plural) and 175-176 (capitalized variants). The off-by-one is immaterial — the code A quoted (replace(processed, "|ObjectNameSingular|", ...) and replace(processed, "|ObjectNamePlural|", ...)) exists verbatim at those lines and confirms A's claim. Not a false positive.
  • A's analysis of the ## concatenation technique is correct. "##" & "objectNameSingular" & "##" evaluates to #objectNameSingular# at runtime (CFML escape sequence in a double-quoted string literal), matching the legacy token form without triggering CFML expression evaluation. The ##objectName## appearances in the block-comment header of the spec are in a /* */ comment, which CFML does not evaluate as expressions — harmless.
  • A confirmed the other template copies (cli/src/templates/ and cli/lucli/templates/) already use pipe tokens. Verified via the diff: neither file was modified, consistent with A's claim that only app/snippets/ApiControllerContent.txt lagged behind.

Missed issues

None detected. Re-scanning the diff:

  • All 28 legacy hash-token occurrences in app/snippets/ApiControllerContent.txt are replaced with pipe-delimited equivalents. No stragglers.
  • The generated output after Templates.processTemplate() runs (e.g. local.products = model("product").findAll();, renderWith(data={ products=local.products }), params.product) is valid CFML.
  • The spec is placed under vendor/wheels/tests/specs/cli/, which the core test runner auto-discovers. No cross-engine concerns: the spec only calls expandPath() and fileRead() — standard CFML I/O with no engine-specific APIs.
  • Commit message (fix(cli): generate api-resource resolves objectName placeholders) is valid per commitlint.config.js: type fix, scope cli, subject under 100 chars, not ALL-CAPS. No issues.
  • CHANGELOG entry is present and accurate.

Verdict alignment

A's comment verdict is consistent with finding only a non-blocking stylistic nit. approve with a note would also have been defensible; comment is not wrong.

Convergence

Aligned. A and B both find the fix correct and the only finding non-blocking. No changes are needed before merge.

@bpamiri
bpamiri marked this pull request as ready for review May 11, 2026 13:44
@bpamiri
bpamiri merged commit 7da353a into develop May 11, 2026
8 checks passed
@bpamiri
bpamiri deleted the fix/bot-2468-wheels-generate-api-resource-leaves-unresolved-tem branch May 11, 2026 13:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

wheels generate api-resource leaves unresolved template placeholders in generated controller

1 participant