From 055b6ea17102777a5f07fbac025ee61c5b0c54a1 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 04:22:00 -0600 Subject: [PATCH 1/4] Surface missing methods inside qb callbacks --- models/QuickQB.cfc | 18 +++++++++++++++++- .../specs/integration/BaseEntity/ScopeSpec.cfc | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/models/QuickQB.cfc b/models/QuickQB.cfc index b1a15826..81f568f7 100644 --- a/models/QuickQB.cfc +++ b/models/QuickQB.cfc @@ -1007,7 +1007,23 @@ component return result; } - return super.onMissingMethod( argumentCollection = arguments ); + try { + return super.onMissingMethod( argumentCollection = arguments ); + } catch ( QBMissingMethod e ) { + throw( + type = "QuickMissingMethod", + message = arrayToList( + [ + "Quick couldn't figure out what to do with [#arguments.missingMethodName#].", + "The error returned was: #e.message#", + "We tried checking columns, aliases, scopes, and relationships locally.", + "We also forwarded the call on to qb to see if it could do anything with it, but it couldn't." + ], + " " + ), + extendedInfo = serializeJSON( e ) + ); + } } // override's super impl diff --git a/tests/specs/integration/BaseEntity/ScopeSpec.cfc b/tests/specs/integration/BaseEntity/ScopeSpec.cfc index ef9708c2..e42c2e7d 100644 --- a/tests/specs/integration/BaseEntity/ScopeSpec.cfc +++ b/tests/specs/integration/BaseEntity/ScopeSpec.cfc @@ -2,6 +2,22 @@ component extends="tests.resources.ModuleIntegrationSpec" { function run() { describe( "Scope Spec", function() { + it( "surfaces the missing method inside a when callback", function() { + var exception = {}; + try { + getInstance( "User" ).when( true, function( q ) { + q.missingScopeInsideWhen(); + } ); + } catch ( any e ) { + exception = e; + } + + expect( exception ).notToBeEmpty(); + expect( exception.type ).toBe( "QuickMissingMethod" ); + expect( exception.message ).toInclude( "[missingScopeInsideWhen]" ); + expect( exception.message ).notToInclude( "[when]" ); + } ); + it( "looks for missing methods as scopes", function() { var users = getInstance( "User" ).latest().get(); expect( users ).toHaveLength( 5, "Five users should exist in the database and be returned." ); From f356855bef9b0335beaae73ab3e6bc364a52dacf Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 08:04:06 -0600 Subject: [PATCH 2/4] fix: handle null qb missing-method responses --- models/QuickQB.cfc | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/models/QuickQB.cfc b/models/QuickQB.cfc index 81f568f7..7c7f1241 100644 --- a/models/QuickQB.cfc +++ b/models/QuickQB.cfc @@ -1007,23 +1007,31 @@ component return result; } + var qbResult = javacast( "null", "" ); + var qbError = {}; try { - return super.onMissingMethod( argumentCollection = arguments ); + qbResult = super.onMissingMethod( argumentCollection = arguments ); } catch ( QBMissingMethod e ) { - throw( - type = "QuickMissingMethod", - message = arrayToList( - [ - "Quick couldn't figure out what to do with [#arguments.missingMethodName#].", - "The error returned was: #e.message#", - "We tried checking columns, aliases, scopes, and relationships locally.", - "We also forwarded the call on to qb to see if it could do anything with it, but it couldn't." - ], - " " - ), - extendedInfo = serializeJSON( e ) - ); + qbError = e; + } + + if ( !isNull( qbResult ) ) { + return qbResult; } + + throw( + type = "QuickMissingMethod", + message = arrayToList( + [ + "Quick couldn't figure out what to do with [#arguments.missingMethodName#].", + qbError.keyExists( "message" ) ? "The error returned was: #qbError.message#" : "qb did not return a result.", + "We tried checking columns, aliases, scopes, and relationships locally.", + "We also forwarded the call on to qb to see if it could do anything with it, but it couldn't." + ], + " " + ), + extendedInfo = serializeJSON( qbError ) + ); } // override's super impl From 43f71d2af3b9e91b293b3734a7eb7075e0ffe4e6 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 08:27:40 -0600 Subject: [PATCH 3/4] fix: preserve Quick callbacks across CFML engines --- models/QuickBuilder.cfc | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/models/QuickBuilder.cfc b/models/QuickBuilder.cfc index 3e0d798e..4661affd 100644 --- a/models/QuickBuilder.cfc +++ b/models/QuickBuilder.cfc @@ -476,11 +476,15 @@ component accessors="true" transientCache="false" { arguments.onFalse( this ); } } else { + var condition = arguments.condition; + var onTrueCallback = arguments.onTrue; + var onFalseCallback = arguments.onFalse; + var builder = this; variables.qb.withScoping( function() { if ( condition ) { - onTrue( this ); + onTrueCallback( builder ); } else { - onFalse( this ); + onFalseCallback( builder ); } } ); } @@ -990,7 +994,17 @@ component accessors="true" transientCache="false" { return result; } - return javacast( "null", "" ); + throw( + type = "QuickMissingMethod", + message = arrayToList( + [ + "Quick couldn't figure out what to do with [#arguments.missingMethodName#].", + "We tried checking columns, aliases, scopes, and relationships locally.", + "We also forwarded the call on to qb to see if it could do anything with it, but it couldn't." + ], + " " + ) + ); } /** From 8244bd125912342682296dbd5ecad910069876f1 Mon Sep 17 00:00:00 2001 From: Eric Peterson Date: Sat, 22 Aug 2026 08:39:19 -0600 Subject: [PATCH 4/4] fix: support scoped when callbacks on Adobe CF --- models/QuickBuilder.cfc | 12 +++--------- tests/specs/integration/BaseEntity/ScopeSpec.cfc | 11 +++++------ 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/models/QuickBuilder.cfc b/models/QuickBuilder.cfc index 4661affd..2c09c6a1 100644 --- a/models/QuickBuilder.cfc +++ b/models/QuickBuilder.cfc @@ -476,16 +476,10 @@ component accessors="true" transientCache="false" { arguments.onFalse( this ); } } else { - var condition = arguments.condition; - var onTrueCallback = arguments.onTrue; - var onFalseCallback = arguments.onFalse; - var builder = this; + var selectedCallback = arguments.condition ? arguments.onTrue : arguments.onFalse; + var builder = this; variables.qb.withScoping( function() { - if ( condition ) { - onTrueCallback( builder ); - } else { - onFalseCallback( builder ); - } + selectedCallback( builder ); } ); } diff --git a/tests/specs/integration/BaseEntity/ScopeSpec.cfc b/tests/specs/integration/BaseEntity/ScopeSpec.cfc index e42c2e7d..2e7738e9 100644 --- a/tests/specs/integration/BaseEntity/ScopeSpec.cfc +++ b/tests/specs/integration/BaseEntity/ScopeSpec.cfc @@ -3,19 +3,18 @@ component extends="tests.resources.ModuleIntegrationSpec" { function run() { describe( "Scope Spec", function() { it( "surfaces the missing method inside a when callback", function() { - var exception = {}; try { getInstance( "User" ).when( true, function( q ) { q.missingScopeInsideWhen(); } ); } catch ( any e ) { - exception = e; + expect( e.type ).toBe( "QuickMissingMethod" ); + expect( e.message ).toInclude( "[missingScopeInsideWhen]" ); + expect( e.message ).notToInclude( "[when]" ); + return; } - expect( exception ).notToBeEmpty(); - expect( exception.type ).toBe( "QuickMissingMethod" ); - expect( exception.message ).toInclude( "[missingScopeInsideWhen]" ); - expect( exception.message ).notToInclude( "[when]" ); + fail( "Expected a QuickMissingMethod exception" ); } ); it( "looks for missing methods as scopes", function() {