diff --git a/system/cache/util/EventURLFacade.cfc b/system/cache/util/EventURLFacade.cfc index 8111e2c46..5a34ebf1a 100644 --- a/system/cache/util/EventURLFacade.cfc +++ b/system/cache/util/EventURLFacade.cfc @@ -35,15 +35,22 @@ component accessors="true" { /** * Build a unique hash from an incoming request context + * Note: the 'event' key is always ignored from the request collection * * @event A request context object + * @cacheIncludeRcKeys A list of keys to include ( '*'=all keys ) */ - string function getUniqueHash( required event ){ + string function getUniqueHash( required event, string cacheIncludeRcKeys = "*" ){ var rcTarget = arguments.event .getCollection() .filter( function( key, value ){ - // Remove event, not needed for hashing purposes - return ( key != "event" ); + return ( + key != "event" && // Remove event, not needed for hashing purposes + ( + cacheIncludeRcKeys == "*" || // include all keys if * + listFindNoCase( cacheIncludeRcKeys, key ) // or if the key is specified + ) + ); } ); // systemOutput( "=====> uniquehash-rcTarget: #variables.jTreeMap.init( rcTarget ).toString()#", true ); @@ -108,9 +115,13 @@ component accessors="true" { string function buildEventKey( required keySuffix, required targetEvent, - required targetContext + required targetContext, + string cacheIncludeRcKeys = "*" ){ - return buildBasicCacheKey( argumentCollection = arguments ) & getUniqueHash( arguments.targetContext ); + return buildBasicCacheKey( argumentCollection = arguments ) & getUniqueHash( + arguments.targetContext, + arguments.cacheIncludeRcKeys + ); } /** diff --git a/system/web/services/HandlerService.cfc b/system/web/services/HandlerService.cfc index 0f3644da7..4dc2d7084 100644 --- a/system/web/services/HandlerService.cfc +++ b/system/web/services/HandlerService.cfc @@ -200,9 +200,10 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { // Create the Cache Key to save eventCachingData.cacheKey = oEventURLFacade.buildEventKey( - keySuffix = eventDictionaryEntry.suffix, - targetEvent = arguments.ehBean.getFullEvent(), - targetContext = oRequestContext + keySuffix = eventDictionaryEntry.suffix, + targetEvent = arguments.ehBean.getFullEvent(), + targetContext = oRequestContext, + cacheIncludeRcKeys = eventDictionaryEntry.cacheIncludeRcKeys ); // Event is cacheable and we need to flag it so the Renderer caches it @@ -629,12 +630,13 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { */ private struct function getNewMDEntry(){ return { - "cacheable" : false, - "timeout" : "", - "lastAccessTimeout" : "", - "cacheKey" : "", - "suffix" : "", - "provider" : "template" + "cacheable" : false, + "timeout" : "", + "lastAccessTimeout" : "", + "cacheKey" : "", + "suffix" : "", + "provider" : "template", + "cacheIncludeRcKeys" : "*" }; } @@ -668,7 +670,11 @@ component extends="coldbox.system.web.services.BaseService" accessors="true" { "cacheLastAccessTimeout", "" ); - mdEntry.provider = arguments.ehBean.getActionMetadata( "cacheProvider", "template" ); + mdEntry.provider = arguments.ehBean.getActionMetadata( "cacheProvider", "template" ); + mdEntry.cacheIncludeRcKeys = arguments.ehBean.getActionMetadata( + "cacheIncludeRcKeys", + "*" + ); // Handler Event Cache Key Suffix, this is global to the event if ( diff --git a/system/web/services/RequestService.cfc b/system/web/services/RequestService.cfc index 53f59653d..7da730693 100644 --- a/system/web/services/RequestService.cfc +++ b/system/web/services/RequestService.cfc @@ -152,9 +152,10 @@ component extends="coldbox.system.web.services.BaseService" { eventCache.append( eventDictionary, true ); // Build the event cache key according to incoming request eventCache[ "cacheKey" ] = oEventURLFacade.buildEventKey( - keySuffix = eventDictionary.suffix, - targetEvent = currentEvent, - targetContext = arguments.context + keySuffix = eventDictionary.suffix, + targetEvent = currentEvent, + targetContext = arguments.context, + cacheIncludeRcKeys = eventDictionary.cacheIncludeRcKeys ); // Check for Event Cache Purge diff --git a/test-harness/handlers/eventcaching.cfc b/test-harness/handlers/eventcaching.cfc index eff25168a..01acd7ca3 100644 --- a/test-harness/handlers/eventcaching.cfc +++ b/test-harness/handlers/eventcaching.cfc @@ -46,6 +46,75 @@ return prc.data; } + // cacheIncludeRcKeys (all keys) + function withIncludeAllRcKeys( event, rc, prc ) + cache ="true" + cacheTimeout ="10" + cacheIncludeRcKeys="*" + { + + prc.data = [ + { id : createUUID(), name : "luis" }, + { id : createUUID(), name : "lucas" }, + { id : createUUID(), name : "fernando" } + ]; + + return prc.data; + } + + // cacheIncludeRcKeys (no keys) + function withIncludeNoRcKeys( event, rc, prc ) + cache ="true" + cacheTimeout ="10" + cacheIncludeRcKeys="" + { + + prc.data = [ + { id : createUUID(), name : "luis" }, + { id : createUUID(), name : "lucas" }, + { id : createUUID(), name : "fernando" } + ]; + + return prc.data; + } + + // cacheIncludeRcKeys (1 RC key) + function withIncludeOneRcKey( event, rc, prc ) + cache ="true" + cacheTimeout ="10" + cacheIncludeRcKeys="slug" + { + + param rc.slug = ""; + + prc.data = [ + { id : createUUID(), name : "luis" }, + { id : createUUID(), name : "lucas" }, + { id : createUUID(), name : "fernando" } + ]; + + return prc.data; + } + + // cacheIncludeRcKeys (RC 2 keys) + function withIncludeRcKeyList( event, rc, prc ) + cache ="true" + cacheTimeout ="10" + cacheIncludeRcKeys="slug,id" + { + + param rc.slug = ""; + param rc.id = ""; + + prc.data = [ + { id : createUUID(), name : "luis" }, + { id : createUUID(), name : "lucas" }, + { id : createUUID(), name : "fernando" } + ]; + + return prc.data; + } + function cacheKeys( event, rc, prc ){ var keys = { "template" : getCache( "template" ).getKeys(), diff --git a/tests/specs/cache/providers/CacheBoxProviderTest.cfc b/tests/specs/cache/providers/CacheBoxProviderTest.cfc index 06818cb03..74abb279c 100755 --- a/tests/specs/cache/providers/CacheBoxProviderTest.cfc +++ b/tests/specs/cache/providers/CacheBoxProviderTest.cfc @@ -172,8 +172,8 @@ var results = cache.getMulti( "test,test2" ); // debug(results); - expect( isNull( results.test ) ).toBeFalse(); - expect( isNull( results.test2 ) ).toBeTrue(); + expect( isNull( results.test ) ).toBeFalse(); + expect( isNull( results.test2 ) ).toBeTrue(); } function testgetCachedObjectMetadata(){ diff --git a/tests/specs/integration/EventCaching.cfc b/tests/specs/integration/EventCaching.cfc index 9a499384c..efb16720f 100755 --- a/tests/specs/integration/EventCaching.cfc +++ b/tests/specs/integration/EventCaching.cfc @@ -19,7 +19,7 @@ expect( prc.cbox_eventCacheableEntry ).toBeStruct().toHaveKey( "provider" ); expect( prc.cbox_eventCacheableEntry.provider ).toBe( "template" ); - // debug( prc.cbox_eventCacheableEntry ); + debug( prc.cbox_eventCacheableEntry ); } ); @@ -31,6 +31,139 @@ expect( prc.cbox_eventCacheableEntry.provider ).toBe( "default" ); } ); + it( "can handle different RC collections", function(){ + // execute an event and specify a queryString variable + var event1 = execute( + event = "eventcaching", + renderResults = true, + queryString = "id=1" + ); + var prc1 = event1.getPrivateCollection(); + + expect( prc1.cbox_eventCacheableEntry ).toBeStruct().toHaveKey( "cacheIncludeRcKeys" ); + expect( prc1.cbox_eventCacheableEntry.cacheIncludeRcKeys ).toBe( "*" ); + + // reset to simulate another request with a different rc scope + setup(); + + var event2 = execute( + event = "eventcaching", + renderResults = true, + queryString = "id=2" + ); + + var prc2 = event2.getPrivateCollection(); + + expect( prc2.cbox_eventCacheableEntry ).toBeStruct().toHaveKey( "cacheIncludeRcKeys" ); + expect( prc2.cbox_eventCacheableEntry.cacheIncludeRcKeys ).toBe( "*" ); + + // because the default cache considers the rc scope, the cache keys should be different + expect( prc1.cbox_eventCacheableEntry.cacheKey ).notToBe( prc2.cbox_eventCacheableEntry.cacheKey ); + } ); + + it( "can handle the cacheIncludeRcKeys metadata", function(){ + // execute an event and specify a queryString variable + var event1 = execute( + event = "eventcaching.withIncludeAllRcKeys", + renderResults = true, + queryString = "id=1" + ); + var prc1 = event1.getPrivateCollection(); + + expect( prc1.cbox_eventCacheableEntry ).toBeStruct().toHaveKey( "cacheIncludeRcKeys" ); + expect( prc1.cbox_eventCacheableEntry.cacheIncludeRcKeys ).toBe( "*" ); + } ); + + it( "can ignore the rc scope", function(){ + // execute an event and specify a queryString variable + var event1 = execute( + event = "eventcaching.withIncludeNoRcKeys", + renderResults = true, + queryString = "id=1" + ); + var prc1 = event1.getPrivateCollection(); + + expect( prc1.cbox_eventCacheableEntry ).toBeStruct().toHaveKey( "cacheIncludeRcKeys" ); + expect( prc1.cbox_eventCacheableEntry.cacheIncludeRcKeys ).toBe( "" ); + + // reset to simulate another request + setup(); + + var event2 = execute( + event = "eventcaching.withIncludeNoRcKeys", + renderResults = true, + queryString = "id=2" + ); + + var prc2 = event2.getPrivateCollection(); + + expect( prc2.cbox_eventCacheableEntry ).toBeStruct().toHaveKey( "cacheIncludeRcKeys" ); + expect( prc2.cbox_eventCacheableEntry.cacheIncludeRcKeys ).toBe( "" ); + + // because we ignore the RC, the cache key should match + expect( prc1.cbox_eventCacheableEntry.cacheKey ).toBe( prc2.cbox_eventCacheableEntry.cacheKey ); + } ); + + it( "can isolate specific RC scope keys and ignore the rest", function(){ + // execute an event and specify a queryString variable + var event1 = execute( + event = "eventcaching.withIncludeOneRcKey", + renderResults = true, + queryString = "id=1&slug=foo" + ); + var prc1 = event1.getPrivateCollection(); + + expect( prc1.cbox_eventCacheableEntry ).toBeStruct().toHaveKey( "cacheIncludeRcKeys" ); + expect( prc1.cbox_eventCacheableEntry.cacheIncludeRcKeys ).toBe( "slug" ); + + // reset to simulate another request + setup(); + + var event2 = execute( + event = "eventcaching.withIncludeOneRcKey", + renderResults = true, + queryString = "id=2&slug=foo" + ); + + var prc2 = event2.getPrivateCollection(); + + expect( prc2.cbox_eventCacheableEntry ).toBeStruct().toHaveKey( "cacheIncludeRcKeys" ); + expect( prc2.cbox_eventCacheableEntry.cacheIncludeRcKeys ).toBe( "slug" ); + + // because we ignore the RC, the cache key should match + expect( prc1.cbox_eventCacheableEntry.cacheKey ).toBe( prc2.cbox_eventCacheableEntry.cacheKey ); + } ); + + it( "can handle a list of specific RC scope keys and ignore the rest", function(){ + // execute an event and specify a queryString variable + var event1 = execute( + event = "eventcaching.withIncludeRcKeyList", + renderResults = true, + queryString = "id=1&slug=foo&source=google" + ); + var prc1 = event1.getPrivateCollection(); + + expect( prc1.cbox_eventCacheableEntry ).toBeStruct().toHaveKey( "cacheIncludeRcKeys" ); + expect( prc1.cbox_eventCacheableEntry.cacheIncludeRcKeys ).toBe( "slug,id" ); + + // reset to simulate another request + setup(); + + var event2 = execute( + event = "eventcaching.withIncludeRcKeyList", + renderResults = true, + queryString = "id=1&slug=foo&source=bing" + ); + + var prc2 = event2.getPrivateCollection(); + + expect( prc2.cbox_eventCacheableEntry ).toBeStruct().toHaveKey( "cacheIncludeRcKeys" ); + expect( prc2.cbox_eventCacheableEntry.cacheIncludeRcKeys ).toBe( "slug,id" ); + + // because we ignore the RC, the cache key should match + expect( prc1.cbox_eventCacheableEntry.cacheKey ).toBe( prc2.cbox_eventCacheableEntry.cacheKey ); + } ); + var formats = [ "json", "xml", "pdf" ]; for ( var thisFormat in formats ) { it( diff --git a/tests/specs/ioc/InjectorCreationTest.cfc b/tests/specs/ioc/InjectorCreationTest.cfc index 16860a2c6..1ac226625 100755 --- a/tests/specs/ioc/InjectorCreationTest.cfc +++ b/tests/specs/ioc/InjectorCreationTest.cfc @@ -142,7 +142,7 @@ function testWebService() skip="isAdobe"{ ws = injector.getInstance( "coldboxWS" ); - // + // if ( listFindNoCase( "Lucee", server.coldfusion.productname ) ) { expect( getMetadata( ws ).name ).toMatch( "rpc" ); } diff --git a/tests/specs/ioc/config/samples/NoScopeBinder.cfc b/tests/specs/ioc/config/samples/NoScopeBinder.cfc index 3568b457d..42ee33993 100644 --- a/tests/specs/ioc/config/samples/NoScopeBinder.cfc +++ b/tests/specs/ioc/config/samples/NoScopeBinder.cfc @@ -5,7 +5,7 @@ * The default ColdBox WireBox Injector configuration object that is used when the * WireBox injector is created **/ - component extends="coldbox.system.ioc.config.Binder" { +component extends="coldbox.system.ioc.config.Binder" { /** * Configure WireBox, that's it! diff --git a/tests/specs/web/flash/AbstractFlashScopeTest.cfc b/tests/specs/web/flash/AbstractFlashScopeTest.cfc index 06b69a855..85dba0a40 100755 --- a/tests/specs/web/flash/AbstractFlashScopeTest.cfc +++ b/tests/specs/web/flash/AbstractFlashScopeTest.cfc @@ -8,8 +8,8 @@ mockController.getConfigSettings().identifierProvider = function(){ return createUUID(); }; - mockRService = createMock( className = "coldbox.system.web.services.RequestService", clearMethods = true ); - mockEvent = createMock( className = "coldbox.system.web.context.RequestContext", clearMethods = true ); + mockRService = createMock( className = "coldbox.system.web.services.RequestService", clearMethods = true ); + mockEvent = createMock( className = "coldbox.system.web.context.RequestContext", clearMethods = true ); mockController .$( "getRequestService", mockRService ) diff --git a/tests/specs/web/flash/ColdboxCacheFlashTest.cfc b/tests/specs/web/flash/ColdboxCacheFlashTest.cfc index 9fd08e1c2..414ec45a5 100755 --- a/tests/specs/web/flash/ColdboxCacheFlashTest.cfc +++ b/tests/specs/web/flash/ColdboxCacheFlashTest.cfc @@ -9,11 +9,11 @@ // mocks session.sessionid = createUUID(); - mockController = createMock( "coldbox.system.web.Controller" ).init( expandPath( "/root" ) ); + mockController = createMock( "coldbox.system.web.Controller" ).init( expandPath( "/root" ) ); mockController.getConfigSettings().identifierProvider = function(){ return createUUID(); }; - mockCache = createMock( + mockCache = createMock( className = "coldbox.system.cache.providers.CacheBoxProvider", clearMethods = true );