Skip to content

fix(model): tableName("x") sets the table instead of silently no-opping - #3104

Closed
wheels-bot[bot] wants to merge 2 commits into
developfrom
fix/bot-3079-docs-model-guides-and-claude-md-use-non-existent-t
Closed

fix(model): tableName("x") sets the table instead of silently no-opping#3104
wheels-bot[bot] wants to merge 2 commits into
developfrom
fix/bot-3079-docs-model-guides-and-claude-md-use-non-existent-t

Conversation

@wheels-bot

@wheels-bot wheels-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Summary

tableName() on models was a zero-argument getter while the table setter is table(name). CFML silently accepts the extra positional argument, so the widely-documented tableName("my_table") form in config() did nothing — the model kept its convention table and the first finder threw Wheels.TableNotFound (or silently hit the wrong table). This makes tableName(name) act as a setter: when a name argument is passed it delegates to table() and returns the resolved name; the zero-argument getter behavior is unchanged. The alias follows the #2781 / #2803 precedent of accepting the form humans and agents keep reaching for.

This PR ships the framework (model + spec) half of the issue. The six docs sites and CLAUDE.md:261 that teach the broken setter form are handled separately by bot-update-docs.yml, so this lands as Refs rather than Fixes.

Refs #3079

Related Issue

Refs #3079

Type of Change

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

Feature Completeness Checklist

  • DCO sign-off -- commit carries Signed-off-by: (committed with git commit -s)
  • Tests -- vendor/wheels/tests/specs/model/miscellaneousSpec.cfc adds a setter-overload suite (failing → passing)
  • Framework Docs -- deferred to bot-update-docs.yml
  • AI Reference Docs -- deferred to bot-update-docs.yml
  • CLAUDE.md -- deferred to bot-update-docs.yml (the Model Quick Reference tableName("X") at CLAUDE.md:261 is the contamination source)
  • Changelog fragment -- changelog.d/3079-tablename-setter.fixed.md
  • Test runner passes -- bash tools/test-local.sh model: before the fix 894 passed, 2 failed; after the fix 896 passed (0 failed, 0 errors) on Lucee 7 + SQLite

Test Plan

  1. bash tools/test-local.sh model
  2. New specs in miscellaneousSpec.cfc ("Tests that tableName acts as a setter when given a name - issue 3079") assert:
    • tableName("tbl_authors_override") then tableName() returns the override (delegates to table())
    • the setter call itself returns the resolved name
    • zero-argument tableName() still returns the current name
  3. Pre-fix the first two specs fail with Expected [tbl_authors_override] but received [c_o_r_e_authors], confirming the silent no-op; post-fix all pass with no regressions across the model layer.

The documented setter form tableName("my_table") in config() was a silent
no-op: tableName() was a zero-argument getter, so the extra positional
argument was ignored and the model kept its convention table (first finder
throws Wheels.TableNotFound). tableName() now accepts an optional name and
delegates to table() when given, returning the resolved name; the
zero-argument getter behavior is unchanged.

Refs #3079

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Signed-off-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
@wheels-bot

wheels-bot Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

Wheels Bot — Docs updated

Added a doc commit to this PR:

  • .ai/wheels/snippets/model-snippets.md — changed table("custom_table_name") to tableName("custom_table_name") to align the AI reference snippet with the form CLAUDE.md and all six user-guide examples already show; the PR fix makes that form work, so the inconsistency in the reference doc is the only thing remaining to clean up.

The six MDX guide pages and CLAUDE.md:261 that the issue identified as using tableName("X") are now correct as-is: the framework fix in this PR is what makes them correct, so no edits to those files were needed.

@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

TL;DR: This PR makes tableName(name) act as a setter (delegating to table()) instead of silently ignoring the argument, fixing the widely-documented-but-broken tableName("my_table") form from #3079. The implementation is minimal and correct, the new specs fail pre-fix and pass post-fix, and the changelog fragment and commit message follow repo conventions. Verdict: comment — no blocking findings, one minor test-hygiene nit.

Correctness

Verified, no issues:

  • table() at vendor/wheels/model/miscellaneous.cfc:35 assigns variables.wheels.class.tableName directly, so the delegation in tableName() (vendor/wheels/model/miscellaneous.cfc:182-185) genuinely takes effect, and the fall-through to the existing lowerCaseTableNames branch means the setter call returns the resolved name as the docblock claims.
  • The any name (non-required) signature matches table(required any name), so tableName(false) composes with the documented table(false) tableless-model escape hatch rather than fighting it.
  • No existing framework caller passes an argument to tableName()vendor/wheels/migrator/AutoMigrator.cfc:21,197, vendor/wheels/model/sql.cfc:1368, and $quotedTableName() (miscellaneous.cfc:198) are all zero-arg — so the overload cannot change behavior at any existing call site.

Cross-engine

No issues. The change is a plain optional argument plus StructKeyExists(arguments, "name"), which is portable across Lucee 5/6/7, Adobe 2018–2025, and BoxLang. The spec additions use the file's existing arrow-closure + unscoped-variable style and contain none of the known engine traps (no inline closures as constructor args, no local writes in catch/finally, no bare tag-in-script statements).

Tests

The three new specs in vendor/wheels/tests/specs/model/miscellaneousSpec.cfc:162-187 cover setter-takes-effect, setter-return-value, and getter-unchanged, and the PR body documents the pre-fix failure (Expected [tbl_authors_override] but received [c_o_r_e_authors]), confirming they exercise the bug.

One minor nit (non-blocking): the afterEach at miscellaneousSpec.cfc:165-168 restores the shared class state with a hardcoded literal:

g.model("author").table("c_o_r_e_authors")

The sibling primaryKeys describe block in the same file (lines 51-62) instead captures the original value in beforeEach and restores that, which can't drift if the Author fixture's mapping ever changes. The risk here is small — the third spec asserts the convention value and would surface drift — but a beforeEach capture (originalTable = g.model("author").tableName()) would match the established restore pattern and is cheap to adopt in a follow-up.

Docs

Handled honestly: the function docblock is updated in place, the changelog fragment changelog.d/3079-tablename-setter.fixed.md follows the fragment convention (no direct CHANGELOG.md edit), and the PR body explicitly defers the six doc sites + CLAUDE.md:261 to bot-update-docs.yml with unchecked checklist boxes rather than claiming completion.

Commits

fix(model): tableName("x") sets the table instead of silently no-opping — valid type, suggested scope, subject under 100 chars, body explains the why, DCO Signed-off-by present. Conforms to commitlint.config.js.

@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

TL;DR: Re-review at head eb0eb5f. The delta since the previous review round (9767d77) is a single docs commit aligning .ai/wheels/snippets/model-snippets.md to the now-working tableName("custom_table_name") setter form. The framework change itself (optional name argument on tableName() delegating to table(), fixing the silent no-op from #3079) re-verifies clean: the delegation writes the same class data the getter reads, no existing call site passes an argument, and the three new specs cover setter-takes-effect, setter-return-value, and getter-unchanged. Verdict: comment — no blocking findings; one minor test-hygiene nit carried over from the prior round, plus a trivial checklist-staleness note.

Correctness

Verified, no issues:

  • table() at vendor/wheels/model/miscellaneous.cfc:35-37 assigns variables.wheels.class.tableName directly, so the delegation at vendor/wheels/model/miscellaneous.cfc:183-185 genuinely takes effect, and falling through to the existing lowerCaseTableNames branch makes the setter call return the resolved name exactly as the docblock claims.
  • The optional any name signature is compatible with table(required any name), so tableName(false) composes with the documented table(false) tableless-model escape hatch rather than conflicting with it; the zero-argument getter path is byte-for-byte unchanged.
  • A repo-wide grep for tableName( with an argument finds no existing caller of the public function with args — only the new specs and the unrelated $tableName(list, action) adapter helper (vendor/wheels/databaseAdapters/Base.cfc:304). The overload cannot change behavior at any existing call site.

Conventions

The snippet change in .ai/wheels/snippets/model-snippets.md:11 (table(...)tableName(...)) is the right call: it aligns the AI reference with the form CLAUDE.md's Model Quick Reference teaches (tableName("tbl_users")), which is exactly the form this PR makes functional. The file has no other table( occurrences, so the doc is internally consistent.

Tests

The three specs at vendor/wheels/tests/specs/model/miscellaneousSpec.cfc:163-188 exercise the fix, and the PR body documents the pre-fix failure (Expected [tbl_authors_override] but received [c_o_r_e_authors]), confirming they fail without the change.

One minor nit, carried over unaddressed from the previous review round (still non-blocking): the afterEach at miscellaneousSpec.cfc:165-168 restores shared class state with a hardcoded literal:

g.model("author").table("c_o_r_e_authors")

The sibling primaryKeys block in the same file (lines 51-62) captures the original value in beforeEach and restores that, which can't drift if the Author fixture's mapping ever changes. The third spec asserting the convention value keeps the risk small, so this stays a nit — fine to take in a follow-up.

Docs

The changelog fragment changelog.d/3079-tablename-setter.fixed.md follows the fragment convention (no direct CHANGELOG.md edit), and the function docblock is updated in place. One trivial staleness note: the PR body's checklist still marks AI Reference Docs as "deferred to bot-update-docs.yml", but head commit eb0eb5f now updates .ai/wheels/snippets/model-snippets.md itself — worth ticking/annotating that box so the checklist reflects what shipped.

Commits

Both commits conform to commitlint.config.js:

  • fix(model): tableName("x") sets the table instead of silently no-opping — valid type, suggested scope, subject under 100 chars, body explains the why, DCO sign-off present.
  • docs: align model-snippets reference to use tableName() setter form — valid type, no scope (allowed), subject under 100 chars, DCO sign-off present and matching the committer.

@bpamiri

bpamiri commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Closing per maintainer design decision on issue #3079: the fail-loud guard ships instead of the setter overload — see #3134. Rationale: verified against the v2.5.1 source that tableName() has been a zero-arg getter (and table() the setter) unchanged since 2.5, so tableName("x") never worked in any era — the guard breaks nothing that ever functioned, keeps table() as the single canonical setter, and converts the always-silent mistake into a located development-mode error. This PR's two setter specs were verified non-tautological and their inverted shape is reused in #3134 with credit. The review's three criticals (docs re-canonicalization contradicting #3091, the CLAUDE.md truth-maintenance, docstring canonicality) are all mooted or handled by the guard approach.

@bpamiri bpamiri closed this Jun 12, 2026
@bpamiri
bpamiri deleted the fix/bot-3079-docs-model-guides-and-claude-md-use-non-existent-t branch June 12, 2026 15:35
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.

1 participant