From 8d9457d4a2ba7430785b36766e72ec48528aa3ec Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 21 Oct 2015 14:05:23 -0700 Subject: [PATCH 001/100] Start 3.6.0 development! --- framework/Application.cfc | 2 +- framework/MyApplication.cfc | 2 +- framework/WireBoxAdapter.cfc | 2 +- framework/aop.cfc | 4 ++-- framework/beanProxy.cfc | 4 ++-- framework/cfmljure.cfc | 4 ++-- framework/cljcontroller.cfc | 4 ++-- framework/ioc.cfc | 4 ++-- framework/ioclj.cfc | 4 ++-- framework/one.cfc | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/framework/Application.cfc b/framework/Application.cfc index fe7c6c5d..92ba657e 100644 --- a/framework/Application.cfc +++ b/framework/Application.cfc @@ -1,5 +1,5 @@ component { - // Version: FW/1 3.5.0 + // Version: FW/1 3.6.0 snapshot // copy this to your application root to use as your Application.cfc // or incorporate the logic below into your existing Application.cfc diff --git a/framework/MyApplication.cfc b/framework/MyApplication.cfc index 607f5959..fad1e63c 100644 --- a/framework/MyApplication.cfc +++ b/framework/MyApplication.cfc @@ -1,5 +1,5 @@ component extends="framework.one" { - // Version: FW/1 3.5.0 + // Version: FW/1 3.6.0 snapshot // if you need to provide extension points, copy this to // your web root, next to your Application.cfc, and add diff --git a/framework/WireBoxAdapter.cfc b/framework/WireBoxAdapter.cfc index b22004d8..4e088666 100644 --- a/framework/WireBoxAdapter.cfc +++ b/framework/WireBoxAdapter.cfc @@ -1,5 +1,5 @@ component extends="wirebox.system.ioc.Injector" { - variables._fw1_version = "3.5.0"; + variables._fw1_version = "3.6.0-snapshot"; /* Copyright (c) 2010-2015, Sean Corfield diff --git a/framework/aop.cfc b/framework/aop.cfc index e7e5452e..69e85d7a 100755 --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -1,6 +1,6 @@ component extends="framework.ioc" { - variables._fw1_version = "3.5.0"; - variables._aop1_version = "2.0.1"; + variables._fw1_version = "3.6.0-snapshot"; + variables._aop1_version = "2.0.2-snapshot"; /* Copyright (c) 2013-2015, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/beanProxy.cfc b/framework/beanProxy.cfc index 8b5a44d1..feb28bfb 100755 --- a/framework/beanProxy.cfc +++ b/framework/beanProxy.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "3.5.0"; - variables._aop1_version = "2.0.1"; + variables._fw1_version = "3.6.0-snapshot"; + variables._aop1_version = "2.0.2-snapshot"; /* Copyright (c) 2013-2015, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index 071797f1..f1392172 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "3.5.0"; - variables._cfmljure_version = "1.0.0"; + variables._fw1_version = "3.6.0-snapshot"; + variables._cfmljure_version = "1.0.1-snapshot"; /* Copyright (c) 2012-2015, Sean Corfield diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index f6c9da3a..6ff569ea 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "3.5.0"; - variables._ioclj_version = "1.0.0"; + variables._fw1_version = "3.6.0-snapshot"; + variables._ioclj_version = "1.0.1-snapshot"; /* Copyright (c) 2015, Sean Corfield diff --git a/framework/ioc.cfc b/framework/ioc.cfc index fbb145c9..8b89ee0a 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "3.5.0"; - variables._di1_version = "1.1.1"; + variables._fw1_version = "3.6.0-snapshot"; + variables._di1_version = "1.1.2-snapshot"; /* Copyright (c) 2010-2015, Sean Corfield diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index a8615b02..d7c0c5cd 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -1,6 +1,6 @@ component extends=framework.ioc { - variables._fw1_version = "3.5.0"; - variables._ioclj_version = "1.0.0"; + variables._fw1_version = "3.6.0-snapshot"; + variables._ioclj_version = "1.0.1-snapshot"; /* Copyright (c) 2015, Sean Corfield diff --git a/framework/one.cfc b/framework/one.cfc index d2d9d6a2..fbffc071 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "3.5.0"; + variables._fw1_version = "3.6.0-snapshot"; /* Copyright (c) 2009-2015, Sean Corfield, Marcin Szczepanski, Ryan Cogswell From c02164b15758931b5022546821e01142e1985a0b Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 21 Oct 2015 15:11:03 -0700 Subject: [PATCH 002/100] Minor code cleanup before starting on #389 --- framework/one.cfc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index fbffc071..a7e7b0ac 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2576,18 +2576,18 @@ component { } } // certain remote calls do not have URL or form scope: - if ( isDefined('URL') ) structAppend(request.context,URL); - if ( isDefined('form') ) structAppend(request.context,form); + if ( isDefined( 'URL' ) ) structAppend( request.context, URL ); + if ( isDefined( 'form' ) ) structAppend( request.context, form ); // figure out the request action before restoring flash context: - if ( !structKeyExists(request.context, variables.framework.action) ) { - request.context[variables.framework.action] = getFullyQualifiedAction( variables.framework.home ); + if ( !structKeyExists( request.context, variables.framework.action ) ) { + request.context[ variables.framework.action ] = getFullyQualifiedAction( variables.framework.home ); } else { - request.context[variables.framework.action] = getFullyQualifiedAction( request.context[variables.framework.action] ); + request.context[ variables.framework.action ] = getFullyQualifiedAction( request.context[ variables.framework.action ] ); } if ( variables.framework.noLowerCase ) { - request.action = validateAction( request.context[variables.framework.action] ); + request.action = validateAction( request.context[ variables.framework.action ] ); } else { - request.action = validateAction( lCase(request.context[variables.framework.action]) ); + request.action = validateAction( lCase(request.context[ variables.framework.action ]) ); } request._fw1.requestDefaultsInitialized = true; } From bdf033e4dacb268846e8a1d2733c8227bd97449c Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 21 Oct 2015 15:46:56 -0700 Subject: [PATCH 003/100] Fix #389 by supporting JSON POST Need to enable JSON POST setting first. --- framework/one.cfc | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/framework/one.cfc b/framework/one.cfc index a7e7b0ac..f4400518 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2416,6 +2416,7 @@ component { throw( type = "FW1.IllegalConfiguration", message = "ViewsFolder must be a plural word (ends in 's')." ); } + variables.viewFolder = left( variables.framework.viewsFolder, len( variables.framework.viewsFolder ) - 1 ); if ( !structKeyExists( variables.framework, 'diOverrideAllowed' ) ) { variables.framework.diOverrideAllowed = false; } @@ -2447,7 +2448,9 @@ component { } variables.framework.diComponent = diComponent; } - variables.viewFolder = left( variables.framework.viewsFolder, len( variables.framework.viewsFolder ) - 1 ); + if ( !structKeyExists( variables.framework, 'enableJSONPOST' ) ) { + variables.framework.enableJSONPOST = false; + } setupEnvironment( env ); request._fw1.doTrace = variables.framework.trace; // add this as a fingerprint so autowire can detect FW/1 CFC: @@ -2578,6 +2581,28 @@ component { // certain remote calls do not have URL or form scope: if ( isDefined( 'URL' ) ) structAppend( request.context, URL ); if ( isDefined( 'form' ) ) structAppend( request.context, form ); + if ( variables.framework.enableJSONPOST ) { + // thanks to Adam Tuttle and by proxy Jason Dean and Ray Camden for the + // seed of this code, inspired by Taffy's basic deserialization + var body = getHttpRequestData().content; + if ( isBinary( body ) ) body = charSetEncode( body, "utf-8" ); + if ( len( body ) ) { + switch ( CGI.CONTENT_TYPE ) { + case "application/json": + case "text/json": + try { + var bodyStruct = deserializeJSON( body ); + structAppend( request.context, bodyStruct ); + } catch ( any e ) { + throw "Content-Type implies JSON but could not deserialize body: " & e.message; + } + break; + default: + // ignore -- either built-in (form handling) or unsupported + break; + } + } + } // figure out the request action before restoring flash context: if ( !structKeyExists( request.context, variables.framework.action ) ) { request.context[ variables.framework.action ] = getFullyQualifiedAction( variables.framework.home ); From ae5c274dbd4442d6aa95a187f13271e830cc6029 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 21 Oct 2015 16:25:33 -0700 Subject: [PATCH 004/100] ACF compatibility on throw statement Because `throw "message";` just isn't good enough for Adobe ColdFusion :( --- framework/one.cfc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/one.cfc b/framework/one.cfc index f4400518..a7531715 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2594,7 +2594,8 @@ component { var bodyStruct = deserializeJSON( body ); structAppend( request.context, bodyStruct ); } catch ( any e ) { - throw "Content-Type implies JSON but could not deserialize body: " & e.message; + throw( type = "FW1.JSONPOST", + message = "Content-Type implies JSON but could not deserialize body: " & e.message ); } break; default: From 30b014bbadb39389c0200cf2707834ba40c2c003 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 21 Oct 2015 16:57:42 -0700 Subject: [PATCH 005/100] Fix #386 by adding convenience of $* --- framework/one.cfc | 10 ++++++++-- tests/frameworkRouteTest.cfc | 23 +++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index a7531715..eb6b177c 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1895,8 +1895,14 @@ component { if ( routeLen ) { if ( left( routeRegEx.pattern, 1 ) == '$' ) { // check HTTP method - routeRegEx.method = listFirst( routeRegEx.pattern, '*/^' ); - var methodLen = len( routeRegEx.method ); + var methodLen = 0; + if ( routeLen >= 2 && left( routeRegEx.pattern, 2 ) == '$*' ) { + // accept all methods so don't set method but... + methodLen = 2; // ...consume 2 characters + } else { + routeRegEx.method = listFirst( routeRegEx.pattern, '*/^' ); + methodLen = len( routeRegEx.method ); + } if ( routeLen == methodLen ) { routeRegEx.pattern = '*'; } else { diff --git a/tests/frameworkRouteTest.cfc b/tests/frameworkRouteTest.cfc index 63847935..5791c8f6 100644 --- a/tests/frameworkRouteTest.cfc +++ b/tests/frameworkRouteTest.cfc @@ -17,6 +17,11 @@ component extends="tests.InjectableTest" { assertEquals("/test/(.*)", match.pattern); assertEquals("routed/\1", match.target); + match = variables.fw.processRouteMatch("/", "routed", "/test", "GET"); + assertTrue(match.matched); + assertEquals("/(.*)", match.pattern); + assertEquals("routed/\1", match.target); + match = variables.fw.processRouteMatch("/test2/:id", "default.main?id=:id", "/test2/5", "GET"); assertTrue(match.matched); assertEquals("/test2/([^/]*)/(.*)", match.pattern); @@ -87,6 +92,24 @@ component extends="tests.InjectableTest" { match = variables.fw.processRouteMatch("$POST^/test/:id", "default.main?id=:id", "/foo/test/5", "POST"); assertFalse(match.matched); + + match = variables.fw.processRouteMatch("$*^/test/:id", "default.main?id=:id", "/test/5", "GET"); + assertTrue(match.matched); + + match = variables.fw.processRouteMatch("$*^/test/:id", "default.main?id=:id", "/test/5", "POST"); + assertTrue(match.matched); + + match = variables.fw.processRouteMatch("$*^/test/:id", "default.main?id=:id", "/foo/test/5", "GET"); + assertFalse(match.matched); + + match = variables.fw.processRouteMatch("$*^/test/:id", "default.main?id=:id", "/foo/test/5", "POST"); + assertFalse(match.matched); + + match = variables.fw.processRouteMatch("$*", "default.error", "/foo/test/5", "GET"); + assertTrue(match.matched); + + match = variables.fw.processRouteMatch("$*", "default.error", "/foo/test/5", "POST"); + assertTrue(match.matched); } public void function testRouteMatchRedirect() From d50dc5241ac5b246cc4882303b1f64d33c265da5 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 21 Oct 2015 17:01:25 -0700 Subject: [PATCH 006/100] Fix #389 by accounting for charset etc Forgot to listFirst() the value. --- framework/one.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/one.cfc b/framework/one.cfc index eb6b177c..8ee111c4 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2593,7 +2593,7 @@ component { var body = getHttpRequestData().content; if ( isBinary( body ) ) body = charSetEncode( body, "utf-8" ); if ( len( body ) ) { - switch ( CGI.CONTENT_TYPE ) { + switch ( listFirst( CGI.CONTENT_TYPE, ';' ) ) { case "application/json": case "text/json": try { From d60409375fe8c81f338452057c9d3797fe5abdc8 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 27 Oct 2015 09:45:42 -0700 Subject: [PATCH 007/100] Fix #391 by dropping 9.0.2 and switching 3.6 => 4.0 --- .travis.yml | 1 - box.json | 2 +- build.xml | 6 +----- framework/Application.cfc | 2 +- framework/MyApplication.cfc | 2 +- framework/WireBoxAdapter.cfc | 2 +- framework/aop.cfc | 2 +- framework/beanProxy.cfc | 2 +- framework/cfmljure.cfc | 2 +- framework/cljcontroller.cfc | 2 +- framework/ioc.cfc | 2 +- framework/ioclj.cfc | 2 +- framework/one.cfc | 2 +- 13 files changed, 12 insertions(+), 17 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5183710a..e5abee62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: java env: matrix: - PLATFORM=acf10-linux64 - - PLATFORM=acf902-linux64 - PLATFORM=railo42beta - PLATFORM=railo41 diff --git a/box.json b/box.json index 21ce9575..ecbc70e9 100644 --- a/box.json +++ b/box.json @@ -22,7 +22,7 @@ "engines" : [ { "type" : "railo", "version" : ">=4.1.x" }, { "type" : "lucee", "version" : ">=4.5.x" }, - { "type" : "adobe", "version" : ">=9.0.2" } + { "type" : "adobe", "version" : ">=10.0.x" } ], "License" : [ { "type" : "Apache 2.0", "URL" : "http://www.apache.org/licenses/LICENSE-2.0" } diff --git a/build.xml b/build.xml index c7584276..a6503415 100644 --- a/build.xml +++ b/build.xml @@ -21,7 +21,6 @@ - @@ -31,7 +30,6 @@ - @@ -42,7 +40,6 @@ - Unkown platform ${platform} for source ${source}. @@ -52,8 +49,7 @@ Valid values are: railo41-osx railo42beta acf10-linux32 - acf10-linux64 - acf902-linux64 + acf10-linux64 diff --git a/framework/Application.cfc b/framework/Application.cfc index 92ba657e..42720eff 100644 --- a/framework/Application.cfc +++ b/framework/Application.cfc @@ -1,5 +1,5 @@ component { - // Version: FW/1 3.6.0 snapshot + // Version: FW/1 4.0.0 snapshot // copy this to your application root to use as your Application.cfc // or incorporate the logic below into your existing Application.cfc diff --git a/framework/MyApplication.cfc b/framework/MyApplication.cfc index fad1e63c..d2c9078f 100644 --- a/framework/MyApplication.cfc +++ b/framework/MyApplication.cfc @@ -1,5 +1,5 @@ component extends="framework.one" { - // Version: FW/1 3.6.0 snapshot + // Version: FW/1 4.0.0 snapshot // if you need to provide extension points, copy this to // your web root, next to your Application.cfc, and add diff --git a/framework/WireBoxAdapter.cfc b/framework/WireBoxAdapter.cfc index 4e088666..69c983ac 100644 --- a/framework/WireBoxAdapter.cfc +++ b/framework/WireBoxAdapter.cfc @@ -1,5 +1,5 @@ component extends="wirebox.system.ioc.Injector" { - variables._fw1_version = "3.6.0-snapshot"; + variables._fw1_version = "4.0.0-snapshot"; /* Copyright (c) 2010-2015, Sean Corfield diff --git a/framework/aop.cfc b/framework/aop.cfc index 69e85d7a..b642ef9e 100755 --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -1,5 +1,5 @@ component extends="framework.ioc" { - variables._fw1_version = "3.6.0-snapshot"; + variables._fw1_version = "4.0.0-snapshot"; variables._aop1_version = "2.0.2-snapshot"; /* Copyright (c) 2013-2015, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/beanProxy.cfc b/framework/beanProxy.cfc index feb28bfb..c35a9143 100755 --- a/framework/beanProxy.cfc +++ b/framework/beanProxy.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "3.6.0-snapshot"; + variables._fw1_version = "4.0.0-snapshot"; variables._aop1_version = "2.0.2-snapshot"; /* Copyright (c) 2013-2015, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index f1392172..2b633387 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "3.6.0-snapshot"; + variables._fw1_version = "4.0.0-snapshot"; variables._cfmljure_version = "1.0.1-snapshot"; /* Copyright (c) 2012-2015, Sean Corfield diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index 6ff569ea..f36ce87b 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "3.6.0-snapshot"; + variables._fw1_version = "4.0.0-snapshot"; variables._ioclj_version = "1.0.1-snapshot"; /* Copyright (c) 2015, Sean Corfield diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 8b89ee0a..abf5edff 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "3.6.0-snapshot"; + variables._fw1_version = "4.0.0-snapshot"; variables._di1_version = "1.1.2-snapshot"; /* Copyright (c) 2010-2015, Sean Corfield diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index d7c0c5cd..dbc18bf4 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -1,5 +1,5 @@ component extends=framework.ioc { - variables._fw1_version = "3.6.0-snapshot"; + variables._fw1_version = "4.0.0-snapshot"; variables._ioclj_version = "1.0.1-snapshot"; /* Copyright (c) 2015, Sean Corfield diff --git a/framework/one.cfc b/framework/one.cfc index 8ee111c4..c1de6bb1 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "3.6.0-snapshot"; + variables._fw1_version = "4.0.0-snapshot"; /* Copyright (c) 2009-2015, Sean Corfield, Marcin Szczepanski, Ryan Cogswell From 72db21cca33d780fe19600ccb41187bafe2c2808 Mon Sep 17 00:00:00 2001 From: Kevin Morris Date: Fri, 30 Oct 2015 15:32:13 -0400 Subject: [PATCH 008/100] Changes example buildURLs to use '?' instead of '&' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If you have generateSES=true and SESOmitIndex=true, ampersand results in viewNotFound ‘/views/main/other&name=anonymous.cfm’ --- examples/subsystems/4hellocontroller/views/main/default.cfm | 2 +- examples/subsystems/5helloservice/views/main/default.cfm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/subsystems/4hellocontroller/views/main/default.cfm b/examples/subsystems/4hellocontroller/views/main/default.cfm index 0a8704d9..d61f858e 100644 --- a/examples/subsystems/4hellocontroller/views/main/default.cfm +++ b/examples/subsystems/4hellocontroller/views/main/default.cfm @@ -1,4 +1,4 @@

Hello #rc.name#!

-

Go away!

+

Go away!

diff --git a/examples/subsystems/5helloservice/views/main/default.cfm b/examples/subsystems/5helloservice/views/main/default.cfm index 0a8704d9..d61f858e 100644 --- a/examples/subsystems/5helloservice/views/main/default.cfm +++ b/examples/subsystems/5helloservice/views/main/default.cfm @@ -1,4 +1,4 @@

Hello #rc.name#!

-

Go away!

+

Go away!

From 145a4e5ab0613bde2948dc3e39cbecc86466466d Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 30 Oct 2015 23:49:21 -0700 Subject: [PATCH 009/100] Tentative fix for #392 Add perResourceError config (default true -- breaking change!) that adds a $* match for HTTP method and an error handler per-resource. --- framework/one.cfc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/framework/one.cfc b/framework/one.cfc index c1de6bb1..f1dbf184 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2372,6 +2372,9 @@ component { if ( !structKeyExists( variables.framework, 'routes' ) ) { variables.framework.routes = [ ]; } + if ( !structKeyExists( variables.framework, 'perResourceError' ) ) { + variables.framework.perResourceError = true; + } if ( !structKeyExists( variables.framework, 'resourceRouteTemplates' ) ) { variables.framework.resourceRouteTemplates = [ { method = 'default', httpMethods = [ '$GET' ] }, @@ -2381,6 +2384,9 @@ component { { method = 'update', httpMethods = [ '$PUT','$PATCH' ], includeId = true }, { method = 'destroy', httpMethods = [ '$DELETE' ], includeId = true } ]; + if ( variables.framework.perResourceError ) { + arrayAppend( variables.framework.resourceRouteTemplates, { method = 'error', httpMethods = [ '$*' ] } ); + } } if ( !structKeyExists( variables.framework, 'routesCaseSensitive' ) ) { variables.framework.routesCaseSensitive = true; From 1864b8064c0aa2ae6129c10558b17c763a9f2342 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sat, 31 Oct 2015 12:45:14 -0700 Subject: [PATCH 010/100] Fix #394 by actually throwing an exception! The code was in place to catch and wrap an exception such as this in order to make it easier to debug. Unfortunately it was missing the actual throw statement! I also tidied up the reporting from exceptions that occur when trying to get a component's metadata: it now attempts to give you the template and line number where the underlying error occurred. --- framework/ioc.cfc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index abf5edff..8cc75d0f 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -367,6 +367,13 @@ component { } catch ( any e ) { var except = "Unable to getComponentMetadata(#dottedPath#) because: " & e.message & ( len( e.detail ) ? " (#e.detail#)" : "" ); + try { + except = except & ", near line " & e.tagContext[1].line & + " in " & e.tagContext[1].template; + } catch ( any e ) { + // unable to determine template / line number so just use + // the exception message we built so far + } throw except; } } @@ -482,6 +489,7 @@ component { // more useful than the former var except = "Problem with metadata for #beanName# (#dottedPath#) because: " & e.message & ( len( e.detail ) ? " (#e.detail#)" : "" ); + throw except; } } } From ecb081c992c7f252659d625589869c757de537ed Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sat, 31 Oct 2015 15:26:13 -0700 Subject: [PATCH 011/100] Fix #390 by making missingBean() a proper extension point --- framework/ioc.cfc | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 8cc75d0f..71540020 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -133,7 +133,7 @@ component { } else if ( structKeyExists( variables, 'parent' ) ) { return variables.parent.getBean( beanName ); } else { - throw 'bean not found: #beanName#'; + return missingBean( beanName = beanName, dependency = false ); } } @@ -558,8 +558,12 @@ component { } - private void function missingBean( string beanName, string resolvingBeanName = '' ) { - if ( variables.config.strict ) { + /* + * override this if you want to add a convention-based bean factory hook, that returns + * beans instead of throwing an exception + */ + private any function missingBean( string beanName, string resolvingBeanName = '', boolean dependency = true ) { + if ( variables.config.strict || !dependency ) { if ( len( resolvingBeanName ) ) { throw 'bean not found: #beanName#; while resolving #resolvingBeanName#'; } else { @@ -654,8 +658,9 @@ component { } else if ( structKeyExists( variables, 'parent' ) && variables.parent.containsBean( property ) ) { args[ property ] = variables.parent.getBean( property ); } else { - missingBean( property, beanName ); - continue; + // allow for possible convention-based bean factory + args[ property ] = missingBean( property, beanName ); + if ( isNull( args[ property ] ) ) continue; } evaluate( 'injection.bean.set#property#( argumentCollection = args )' ); } @@ -797,12 +802,16 @@ component { } else { throw 'internal error: invalid metadata for #beanName#'; } - } else if ( structKeyExists( variables, 'parent' ) && variables.parent.containsBean( beanName ) ) { - bean = variables.parent.getBean( beanName ); - accumulator.injection[ beanName ] = { bean = bean, setters = { } }; - accumulator.bean = bean; } else { - missingBean( beanName ); + if ( structKeyExists( variables, 'parent' ) && variables.parent.containsBean( beanName ) ) { + bean = variables.parent.getBean( beanName ); + } else { + bean = missingBean( beanName = beanName, dependency = true ); + } + if ( !isNull( bean ) ) { + accumulator.injection[ beanName ] = { bean = bean, setters = { } }; + accumulator.bean = bean; + } } return accumulator; } From ef72f0ce82738a3f81da03dde5a1a4e11284fe96 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sat, 31 Oct 2015 16:02:22 -0700 Subject: [PATCH 012/100] Fix #385 #388 by adding builder syntax and statusText support --- framework/one.cfc | 50 ++++++++++++++++++++++++++++++++--- tests/frameworkRenderTest.cfc | 34 ++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index f1dbf184..f0de3d93 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1109,8 +1109,41 @@ component { } // call this to render data rather than a view and layouts - public void function renderData( string type, any data, numeric statusCode = 200, string jsonpCallback = "" ) { - request._fw1.renderData = { type = type, data = data, statusCode = statusCode, jsonpCallback = jsonpCallback }; + // arguments are deprecated in favor of build syntax as of 4.0 + public any function renderData( string type = '', any data = '', numeric statusCode = 200, string jsonpCallback = "" ) { + request._fw1.renderData = { + type = type, + data = data, + statusCode = statusCode, + statusText = '', + jsonpCallback = jsonpCallback + }; + // return a build to support nicer rendering syntax + var builder = { }; + structAppend( builder, { + // allow type and data to be overridden just for completeness + type : function( v ) { + request._fw1.renderData.type = v; + return builder; + }, + data : function( v ) { + request._fw1.renderData.data = v; + return builder; + }, + statusCode : function( v ) { + request._fw1.renderData.statusCode = v; + return builder; + }, + statusText : function( v ) { + request._fw1.renderData.statusText = v; + return builder; + }, + jsonpCallback : function( v ) { + request._fw1.renderData.jsonpCallback = v; + return builder; + } + } ); + return builder; } /* @@ -2030,6 +2063,7 @@ component { var type = request._fw1.renderData.type; var data = request._fw1.renderData.data; var statusCode = request._fw1.renderData.statusCode; + var statusText = request._fw1.renderData.statusText; switch ( type ) { case 'json': contentType = 'application/json; charset=utf-8'; @@ -2079,7 +2113,17 @@ component { detail = 'renderData() called with unknown type: ' & type ); break; } - getPageContext().getResponse().setStatus( statusCode ); + if ( len( statusText ) ) { + try { + // deprecated in Servlet 2.1 but still useful + getPageContext().getResponse().setStatus( statusCode, statusText ); + } catch ( any e ) { + // revert to sendError() if setStatus() fails with message + getPageContext().getResponse().sendError( statusCode, statusText ); + } + } else { + getPageContext().getResponse().setStatus( statusCode ); + } getPageContext().getResponse().setContentType( contentType ); return out; } diff --git a/tests/frameworkRenderTest.cfc b/tests/frameworkRenderTest.cfc index ec545e69..8191c6b4 100644 --- a/tests/frameworkRenderTest.cfc +++ b/tests/frameworkRenderTest.cfc @@ -79,6 +79,40 @@ component extends="mxunit.framework.TestCase" { assertTrue( output contains "framework lifecycle trace" ); } + public void function testNoTraceRenderVarBuilder() { + variables.fw.onApplicationStart(); + variables.fw.renderData( "text" ).data( "test" ); + var output = ""; + savecontent variable="output" { + variables.fw.onRequestEnd(); + } + assertFalse( output contains "framework lifecycle trace" ); + } + + public void function testTraceOutputReqBuilder() { + request.fw.onApplicationStart(); + variables.fw.renderData().type( "text" ).data( "myteststring" ); + var output = ""; + savecontent variable="output" { + request.fw.onRequest("/index.cfm"); + request.fw.onRequestEnd(); + } + assertTrue( output contains "myteststring" ); + assertFalse( output contains "framework lifecycle trace" ); + } + + public void function testTraceOutputHTMLReqBuilder() { + request.fw.onApplicationStart(); + variables.fw.renderData( data = "

myteststring

" ).type( "html" ); + var output = ""; + savecontent variable="output" { + request.fw.onRequest("/index.cfm"); + request.fw.onRequestEnd(); + } + assertTrue( output contains "myteststring" ); + assertTrue( output contains "framework lifecycle trace" ); + } + public void function testSetupTraceRenderHtml() { variables.fwExtended.onApplicationStart(); var output = ""; From f386d6f632ee4ef115c9f5dece800db46d3414aa Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sat, 31 Oct 2015 18:30:14 -0700 Subject: [PATCH 013/100] Bump version for #390 --- framework/ioc.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 71540020..3c3bab8e 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -1,6 +1,6 @@ component { variables._fw1_version = "4.0.0-snapshot"; - variables._di1_version = "1.1.2-snapshot"; + variables._di1_version = "1.2.0-snapshot"; /* Copyright (c) 2010-2015, Sean Corfield From ffea8209ada766f9f87de867276ec9b615b71c1e Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sat, 31 Oct 2015 18:31:01 -0700 Subject: [PATCH 014/100] Deprecate inline arguments #388 --- framework/one.cfc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/one.cfc b/framework/one.cfc index f0de3d93..d5ffc66e 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1111,6 +1111,8 @@ component { // call this to render data rather than a view and layouts // arguments are deprecated in favor of build syntax as of 4.0 public any function renderData( string type = '', any data = '', numeric statusCode = 200, string jsonpCallback = "" ) { + if ( statusCode != 200 ) deprecated( false, "Use the .statusCode() builder syntax instead of the inline argument." ); + if ( len( jsonpCallback ) ) deprecated( false, "Use the .jsonpCallback() builder syntax instead of the inline argument." ); request._fw1.renderData = { type = type, data = data, From 6515f8d0e94e7b2820f1b5e5d552e49414313da2 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sat, 31 Oct 2015 18:31:45 -0700 Subject: [PATCH 015/100] Proper status/error handling for #385 --- framework/one.cfc | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index d5ffc66e..0ae8f2e4 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2115,18 +2115,16 @@ component { detail = 'renderData() called with unknown type: ' & type ); break; } + var errorResponse = statusCode >= 400; + var resp = getPageContext().getResponse(); if ( len( statusText ) ) { - try { - // deprecated in Servlet 2.1 but still useful - getPageContext().getResponse().setStatus( statusCode, statusText ); - } catch ( any e ) { - // revert to sendError() if setStatus() fails with message - getPageContext().getResponse().sendError( statusCode, statusText ); - } + if ( errorResponse ) resp.sendError( statusCode, statusText ); + else resp.setStatus( statusCode, statusText ); } else { - getPageContext().getResponse().setStatus( statusCode ); + if ( errorResponse ) resp.sendError( statusCode ); + else resp.setStatus( statusCode ); } - getPageContext().getResponse().setContentType( contentType ); + resp.setContentType( contentType ); return out; } From f57c1532072ecdc47b074b419355b98bee0d3c2c Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 3 Nov 2015 16:55:30 -0800 Subject: [PATCH 016/100] Rollback sendError() change in #385 As I noted in the comments, using `sendError()` is not what we want anyway. I should have listened to myself :) --- framework/one.cfc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index 0ae8f2e4..62500047 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2115,14 +2115,14 @@ component { detail = 'renderData() called with unknown type: ' & type ); break; } - var errorResponse = statusCode >= 400; + // in theory, we should use sendError() instead of setStatus() but some + // Servlet containers interpret that to mean "Send my error page" instead + // of just sending the response you actually want! var resp = getPageContext().getResponse(); if ( len( statusText ) ) { - if ( errorResponse ) resp.sendError( statusCode, statusText ); - else resp.setStatus( statusCode, statusText ); + resp.setStatus( statusCode, statusText ); } else { - if ( errorResponse ) resp.sendError( statusCode ); - else resp.setStatus( statusCode ); + resp.setStatus( statusCode ); } resp.setContentType( contentType ); return out; From 59b2b91089ca4bae76287dfa20e17f3c2045cf03 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 13 Nov 2015 17:42:01 -0800 Subject: [PATCH 017/100] First cut of #387 preflightOptions, optionsAccessControl implemented. Route matching and non-route matching implemented. Renders data for options requests if preflightOptions enabled. Tests present for route matching. Need to write tests for overall HTTP OPTIONS requests. --- framework/one.cfc | 97 +++++++++++++++++++++------- tests/frameworkProcessRoutesTest.cfc | 33 +++++++++- 2 files changed, 103 insertions(+), 27 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index 62500047..a2b345d8 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -24,6 +24,7 @@ component { cgiRequestMethod = CGI.REQUEST_METHOD, controllers = [ ], requestDefaultsInitialized = false, + routeMethodsMatched = { }, doTrace = false, trace = [ ] }; @@ -803,30 +804,43 @@ component { var n = 0; request._fw1.controllerExecutionStarted = true; - try { - n = arrayLen( request._fw1.controllers ); - for ( i = 1; i <= n; i = i + 1 ) { - tuple = request._fw1.controllers[ i ]; - // run before once per controller: - if ( !structKeyExists( once, tuple.key ) ) { - once[ tuple.key ] = i; - doController( tuple, 'before', 'before' ); + if ( variables.framework.preflightOptions && + structCount( request._fw1.routeMethodsMatched ) ) { + // OPTIONS support enabled and at least one possible match + // bypass all normal controllers and render headers and data: + var resp = getPageContext().getResponse(); + resp.setHeader( "Access-Control-Allow-Origin", variables.framework.optionsAccessControl.origin ); + resp.setHeader( "Access-Control-Allow-Methods", "OPTIONS," & uCase( structKeyList( request._fw1.routeMethodsMatched ) ) ); + resp.setHeader( "Access-Control-Allow-Headers", variables.framework.optionsAccessControl.headers ); + resp.setHeader( "Access-Control-Allow-Credentials", variables.framework.optionsAccessControl ? "true" : "false" ); + resp.setHeader( "Access-Control-Max-Age", "#variables.framework.optionsAccessControl#" ); + renderData( "text", "" ); + } else { + try { + n = arrayLen( request._fw1.controllers ); + for ( i = 1; i <= n; i = i + 1 ) { + tuple = request._fw1.controllers[ i ]; + // run before once per controller: + if ( !structKeyExists( once, tuple.key ) ) { + once[ tuple.key ] = i; + doController( tuple, 'before', 'before' ); + if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); + } + doController( tuple, tuple.item, 'item' ); if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); } - doController( tuple, tuple.item, 'item' ); - if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); - } - n = arrayLen( request._fw1.controllers ); - for ( i = n; i >= 1; i = i - 1 ) { - tuple = request._fw1.controllers[ i ]; - // run after once per controller (in reverse order): - if ( once[ tuple.key ] eq i ) { - doController( tuple, 'after', 'after' ); - if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); + n = arrayLen( request._fw1.controllers ); + for ( i = n; i >= 1; i = i - 1 ) { + tuple = request._fw1.controllers[ i ]; + // run after once per controller (in reverse order): + if ( once[ tuple.key ] eq i ) { + doController( tuple, 'after', 'after' ); + if ( structKeyExists( request._fw1, 'abortController' ) ) abortController(); + } } + } catch ( FW1.AbortControllerException e ) { + // do "nothing" since this is a control flow exception } - } catch ( FW1.AbortControllerException e ) { - // do "nothing" since this is a control flow exception } request._fw1.controllerExecutionComplete = true; @@ -1985,11 +1999,25 @@ component { var routeMatch = { matched = false }; structAppend( routeMatch, regExCache[ cacheKey ] ); if ( !len( path ) || right( path, 1) != '/' ) path &= '/'; - var matched = len( routeMatch.method ) ? ( '$' & httpMethod == routeMatch.method ) : true; - if ( matched && routeRegexFind( routeMatch.pattern, path ) ) { - routeMatch.matched = true; - routeMatch.route = route; - routeMatch.path = path; + if ( routeRegexFind( routeMatch.pattern, path ) ) { + if ( len( routeMatch.method ) > 1 ) { + if ( '$' & httpMethod == routeMatch.method ) { + routeMatch.matched = true; + } else if ( variables.framework.preflightOptions ) { + // it matched apart from the method so record this + request._fw1.routeMethodsMatched[ right( routeMatch.method, len( routeMatch.method ) - 1 ) ] = true; + } + } else if ( variables.framework.preflightOptions && httpMethod == "OPTIONS" ) { + // it would have matched but we should special case OPTIONS + request._fw1.routeMethodsMatched.get = true; + request._fw1.routeMethodsMatched.post = true; + } else { + routeMatch.matched = true; + } + if ( routeMatch.matched ) { + routeMatch.route = route; + routeMatch.path = path; + } } return routeMatch; } @@ -2507,7 +2535,22 @@ component { if ( !structKeyExists( variables.framework, 'enableJSONPOST' ) ) { variables.framework.enableJSONPOST = false; } + if ( !structKeyExists( variables.framework, 'preflightOptions' ) ) { + variables.framework.preflightOptions = false; + } + if ( !structKeyExists( variables.framework, 'optionsAccessControl' ) ) { + variables.framework.optionsAccessControl = { }; + } setupEnvironment( env ); + if ( variables.framework.preflightOptions ) { + var defaultAccessControl = { + origin = "*", + headers = "Accept,Authorization,Content-Type", + credentials = true, + maxAge = 1728000 + }; + structAppend( variables.framework.optionsAccessControl, defaultAccessControl, false ); + } request._fw1.doTrace = variables.framework.trace; // add this as a fingerprint so autowire can detect FW/1 CFC: this.__fw1_version = variables.framework.version; @@ -2603,6 +2646,10 @@ component { request._fw1.route = routeMatch.route; } } + } else if ( variables.framework.preflightOptions && request._fw1.cgiRequestMethod == "OPTIONS" ) { + // non-route matching but we have OPTIONS support enabled + request._fw1.routeMethodsMatched.get = true; + request._fw1.routeMethodsMatched.post = true; } try { // we use .split() to handle empty items in pathInfo - we fallback to listToArray() on diff --git a/tests/frameworkProcessRoutesTest.cfc b/tests/frameworkProcessRoutesTest.cfc index c470d995..71fef98d 100755 --- a/tests/frameworkProcessRoutesTest.cfc +++ b/tests/frameworkProcessRoutesTest.cfc @@ -17,6 +17,7 @@ component extends="tests.InjectableTest" { { 'hint' = 'Resource Routes', '$RESOURCES' = 'dogs' } ]; variables.fwVars.framework.routesCaseSensitive = true; + variables.fwVars.framework.preflightOptions = true; } public void function testProcessRoutes() { @@ -41,7 +42,7 @@ component extends="tests.InjectableTest" { assertEquals( '/dogs/update/id/42/', rereplace( routeMatch.path, routeMatch.pattern, routeMatch.target ) ); } - + public void function testProcessRoutesExplicit() { request._fw1.cgiRequestMethod = 'FOO'; @@ -62,7 +63,35 @@ component extends="tests.InjectableTest" { assertEquals( '/dogs/update/id/42/', rereplace( routeMatch.path, routeMatch.pattern, routeMatch.target ) ); } - + + public void function testProcessRoutesOptions() { + + request._fw1.cgiRequestMethod = 'OPTIONS'; + + request._fw1.routeMethodsMatched = { }; + var routeMatch = variables.fw.processRoutes( '/no/match', variables.fw.getRoutes() ); + assertFalse( routeMatch.matched ); + assertEquals( { }, request._fw1.routeMethodsMatched ); + + request._fw1.routeMethodsMatched = { }; + routeMatch = variables.fw.processRoutes( '/old/path/foo', variables.fw.getRoutes() ); + assertFalse( routeMatch.matched ); + assertEquals( { get : true }, request._fw1.routeMethodsMatched ); + + // these show we correctly get methods back based on the ACTUAL route matched + + request._fw1.routeMethodsMatched = { }; + routeMatch = variables.fw.processRoutes( '/dogs/42', variables.fw.getRoutes() ); + assertFalse( routeMatch.matched ); + assertEquals( { get : true, delete : true, patch : true, put : true }, request._fw1.routeMethodsMatched ); + + request._fw1.routeMethodsMatched = { }; + routeMatch = variables.fw.processRoutes( '/dogs', variables.fw.getRoutes() ); + assertFalse( routeMatch.matched ); + assertEquals( { get : true, post : true }, request._fw1.routeMethodsMatched ); + + } + // PRIVATE private boolean function isFrameworkInitialized() { From b3f9c8bc1562434672080fc06c20586417a4c223 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 18 Nov 2015 13:53:09 -0500 Subject: [PATCH 018/100] Proof of concept for #399 Pushing this so Dan Switzer can test and see if it solves his use case. --- framework/ioc.cfc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 3c3bab8e..01cf60b4 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -126,10 +126,10 @@ component { // return the requested bean, fully populated - public any function getBean( string beanName ) { + public any function getBean( string beanName, struct constructorArgs = { } ) { discoverBeans(); if ( structKeyExists( variables.beanInfo, beanName ) ) { - return resolveBean( beanName ); + return resolveBean( beanName, constructorArgs ); } else if ( structKeyExists( variables, 'parent' ) ) { return variables.parent.getBean( beanName ); } else { @@ -625,11 +625,11 @@ component { } - private any function resolveBean( string beanName ) { + private any function resolveBean( string beanName, struct constructorArgs = { } ) { // do enough resolution to create and initialization this bean // returns a struct of the bean and a struct of beans and setters still to run // construction phase: - var partialBean = resolveBeanCreate( beanName, { injection = { }, dependencies = { } } ); + var partialBean = resolveBeanCreate( beanName, { injection = { }, dependencies = { } }, constructorArgs ); if ( structKeyExists( variables.resolutionCache, beanName ) && variables.resolutionCache[ beanName ] ) { // fully resolved, no action needed this time @@ -704,7 +704,7 @@ component { } - private struct function resolveBeanCreate( string beanName, struct accumulator ) { + private struct function resolveBeanCreate( string beanName, struct accumulator, struct constructorArgs = { } ) { var bean = 0; if ( structKeyExists( variables.beanInfo, beanName ) ) { var info = variables.beanInfo[ beanName ]; @@ -713,6 +713,7 @@ component { /*******************************************************/ var metaBean = cachable( beanName ); var overrides = structKeyExists( info, 'overrides' ) ? info.overrides : { }; + structAppend( overrides, constructorArgs ); bean = metaBean.bean; if ( metaBean.newObject ) { if ( structKeyExists( info.metadata, 'constructor' ) ) { From 5a7fba04a785f36442e4d8a4edbf3a75af8d815c Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 18 Nov 2015 19:06:36 -0500 Subject: [PATCH 019/100] Add parent factory constructor arg support #399 Also canonicalize folder names to avoid trailing slash weirdness. --- framework/ioc.cfc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 01cf60b4..71c02bbe 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -29,7 +29,15 @@ component { } var n = arrayLen( variables.folderArray ); for ( var i = 1; i <= n; ++i ) { - variables.folderArray[ i ] = trim( variables.folderArray[ i ] ); + var folderName = trim( variables.folderArray[ i ] ); + // strip trailing slash since it can cause weirdness in path + // deduction on some engines on some platforms (guess which!) + if ( len( folderName ) > 1 && + ( right( folderName, 1 ) == '/' || + right( folderName, 1 ) == chr(92) ) ) { + folderName = left( folderName, len( folderName ) - 1 ); + } + variables.folderArray[ i ] = folderName; } variables.config = config; variables.beanInfo = { }; @@ -131,7 +139,10 @@ component { if ( structKeyExists( variables.beanInfo, beanName ) ) { return resolveBean( beanName, constructorArgs ); } else if ( structKeyExists( variables, 'parent' ) ) { - return variables.parent.getBean( beanName ); + // ideally throw an exception for non-DI/1 parent when args passed + // WireBox adapter can do that since we control it but we can't do + // anything for other bean factories - will revisit before release + return variables.parent.getBean( beanName, constructorArgs ); } else { return missingBean( beanName = beanName, dependency = false ); } From f4a515d4b9748a9d59b82a0af444cee42b0adb20 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 20 Nov 2015 10:27:37 -0800 Subject: [PATCH 020/100] Fix #400 by checking defaulted properties Change typed property default to ignore (breaking). Add defaulted property with default to ignore. Update tests to verify behavior. --- framework/ioc.cfc | 18 ++++++++++++------ tests/DeclareBeanTest.cfc | 16 ++++++++++++++-- tests/declared/things/myconfig.cfc | 1 + 3 files changed, 27 insertions(+), 8 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 3c3bab8e..f61dd35e 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -318,8 +318,12 @@ component { ( !structKeyExists( property, 'setter' ) || isBoolean( property.setter ) && property.setter ) ) { if ( structKeyExists( property, 'type' ) && - property.type != 'any' ) { - iocMeta.setters[ property.name ] = 'typed'; + property.type != 'any' && + variables.config.omitTypedProperties ) { + iocMeta.setters[ property.name ] = 'ignored'; + } else if ( structKeyExists( property, 'default' ) && + variables.config.omitDefaultedProperties ) { + iocMeta.setters[ property.name ] = 'ignored'; } else { iocMeta.setters[ property.name ] = 'implicit'; } @@ -645,9 +649,8 @@ component { postInjectables[ name ] = true; } for ( var property in injection.setters ) { - if ( injection.setters[ property ] == 'typed' && - variables.config.omitTypedProperties ) { - // we do not inject typed properties! + if ( injection.setters[ property ] == 'ignored' ) { + // do not inject defaulted/typed properties! continue; } var args = { }; @@ -848,8 +851,11 @@ component { throw 'singletonPattern and transientPattern are mutually exclusive'; } + if ( !structKeyExists( variables.config, 'omitDefaultedProperties' ) ) { + variables.config.omitDefaultedProperties = true; + } if ( !structKeyExists( variables.config, 'omitTypedProperties' ) ) { - variables.config.omitTypedProperties = false; + variables.config.omitTypedProperties = true; } if ( !structKeyExists( variables.config, 'omitDirectoryAliases' ) ) { variables.config.omitDirectoryAliases = false; diff --git a/tests/DeclareBeanTest.cfc b/tests/DeclareBeanTest.cfc index 5359587e..ce4fbe99 100644 --- a/tests/DeclareBeanTest.cfc +++ b/tests/DeclareBeanTest.cfc @@ -41,18 +41,30 @@ component extends="mxunit.framework.TestCase" { } function shouldDeclareAndAdd() { - var bf = new framework.ioc( "" ).declareBean( "foo", "tests.declared.things.myconfig" ).addBean( "name", "test" ).addBean( "config", "some" ); + var bf = new framework.ioc( "", { omitTypedProperties = false } ).declareBean( "foo", "tests.declared.things.myconfig" ) + .addBean( "name", "test" ).addBean( "config", "some" ); var item = bf.getBean( "foo" ); assertEquals( "test", item.getName() ); assertEquals( "some", item.getConfig() ); } function shouldDeclareWithOverride() { - var bf = new framework.ioc( "" ).declareBean( "foo", "tests.declared.things.myconfig", true, { name = "test", config = "some" } ) + var bf = new framework.ioc( "", { omitTypedProperties = false } ).declareBean( "foo", "tests.declared.things.myconfig", true, { name = "test", config = "some" } ) .addBean( "name", "not-test" ).addBean( "config", "config" ); var item = bf.getBean( "foo" ); assertEquals( "test", item.getName() ); assertEquals( "some", item.getConfig() ); } + function shouldDeclareInteractWithDefault() { + var bf = new framework.ioc( "", { omitDefaultedProperties = false } ).declareBean( "foo", "tests.declared.things.myconfig" ) + .addBean( "dftname", "injected" ); + var item = bf.getBean( "foo" ); + assertEquals( "injected", item.getDftName() ); + var bf = new framework.ioc( "", { omitDefaultedProperties = true } ).declareBean( "foo", "tests.declared.things.myconfig" ) + .addBean( "dftname", "injected" ); + var item = bf.getBean( "foo" ); + assertEquals( "default", item.getDftName() ); + } + } diff --git a/tests/declared/things/myconfig.cfc b/tests/declared/things/myconfig.cfc index 22341ebb..28f770ee 100644 --- a/tests/declared/things/myconfig.cfc +++ b/tests/declared/things/myconfig.cfc @@ -1,6 +1,7 @@ component accessors=true { property string name; + property dftname default="default"; function init( string data = "none" ) { setConfig( data ); From a9e9dca0f820691fe185c97b78c25b01805e78b2 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 20 Nov 2015 10:38:53 -0800 Subject: [PATCH 021/100] ACF compatibility for #400 --- tests/declared/things/myconfig.cfc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/declared/things/myconfig.cfc b/tests/declared/things/myconfig.cfc index 28f770ee..2e562951 100644 --- a/tests/declared/things/myconfig.cfc +++ b/tests/declared/things/myconfig.cfc @@ -1,7 +1,9 @@ component accessors=true { property string name; - property dftname default="default"; + property name="dftname" default="default"; + // not legal syntax: property dftname="default"; + // legal syntax, doesn't create setter: property string dftname="default"; function init( string data = "none" ) { setConfig( data ); From 2b1f4d1ebdd574c27216b654bf8f198263de0cf5 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 20 Nov 2015 12:05:48 -0800 Subject: [PATCH 022/100] Complete #387 by handling credentials and max age --- framework/one.cfc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index a2b345d8..f74592ce 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -812,8 +812,8 @@ component { resp.setHeader( "Access-Control-Allow-Origin", variables.framework.optionsAccessControl.origin ); resp.setHeader( "Access-Control-Allow-Methods", "OPTIONS," & uCase( structKeyList( request._fw1.routeMethodsMatched ) ) ); resp.setHeader( "Access-Control-Allow-Headers", variables.framework.optionsAccessControl.headers ); - resp.setHeader( "Access-Control-Allow-Credentials", variables.framework.optionsAccessControl ? "true" : "false" ); - resp.setHeader( "Access-Control-Max-Age", "#variables.framework.optionsAccessControl#" ); + resp.setHeader( "Access-Control-Allow-Credentials", variables.framework.optionsAccessControl.credentials ? "true" : "false" ); + resp.setHeader( "Access-Control-Max-Age", "#variables.framework.optionsAccessControl.maxAge#" ); renderData( "text", "" ); } else { try { From df6707e2d7432589ae49c4314a665d026e7125ef Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 20 Nov 2015 15:19:38 -0800 Subject: [PATCH 023/100] Fix #387 by ensuring we don't intercept non-OPTIONS requests --- framework/one.cfc | 1 + 1 file changed, 1 insertion(+) diff --git a/framework/one.cfc b/framework/one.cfc index f74592ce..9440aa55 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -805,6 +805,7 @@ component { request._fw1.controllerExecutionStarted = true; if ( variables.framework.preflightOptions && + && request._fw1.cgiRequestMethod == "OPTIONS" structCount( request._fw1.routeMethodsMatched ) ) { // OPTIONS support enabled and at least one possible match // bypass all normal controllers and render headers and data: From f77888c6bc6b1c583babffe911c7e4973c2f9dd3 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 20 Nov 2015 15:21:26 -0800 Subject: [PATCH 024/100] Ahem #387 fix syntax error --- framework/one.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/one.cfc b/framework/one.cfc index 9440aa55..26e1753c 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -805,7 +805,7 @@ component { request._fw1.controllerExecutionStarted = true; if ( variables.framework.preflightOptions && - && request._fw1.cgiRequestMethod == "OPTIONS" + request._fw1.cgiRequestMethod == "OPTIONS" && structCount( request._fw1.routeMethodsMatched ) ) { // OPTIONS support enabled and at least one possible match // bypass all normal controllers and render headers and data: From 5681a65e922b954c4c19c946eeff549bf1701705 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 20 Nov 2015 17:10:22 -0800 Subject: [PATCH 025/100] Tests for #399 --- tests/TransientTest.cfc | 35 ++++++++++++++++++++++++++++ tests/extrabeans/sheep/construct.cfc | 8 +++++++ 2 files changed, 43 insertions(+) create mode 100644 tests/extrabeans/sheep/construct.cfc diff --git a/tests/TransientTest.cfc b/tests/TransientTest.cfc index 3aa12e19..a9d62c3c 100644 --- a/tests/TransientTest.cfc +++ b/tests/TransientTest.cfc @@ -21,4 +21,39 @@ component extends="mxunit.framework.TestCase" { assertTrue( isObject( product ) ); } + function shouldInitializeWithBeans() { + variables.factory = new framework.ioc( "/tests/model, /tests/extrabeans", + { transients = [ "fish" ], singulars = { sheep = "bean" } } ) + .addBean( "one", 1 ).addBean( "two", "two" ); + var i = variables.factory.getBean( "item" ); + var n1 = application.itemCount; + var c = variables.factory.getBean( "construct" ); + assertEquals( 1, c.getOne() ); + assertEquals( "two", c.two ); + assertEquals( n1 + 1, application.itemCount ); + } + + function shouldInitializeWithConstructorArgs() { + variables.factory = new framework.ioc( "/tests/model, /tests/extrabeans", + { transients = [ "fish" ], singulars = { sheep = "bean" } } ) + .addBean( "one", 1 ).addBean( "two", "two" ); + var i = variables.factory.getBean( "item" ); + var n1 = application.itemCount; + var c = variables.factory.getBean( "construct", { one : "one", two : 2, item : "no-op" } ); + assertEquals( "one", c.getOne() ); + assertEquals( 2, c.two ); + assertEquals( n1, application.itemCount ); + } + + function shouldInitializeWithOnlyConstructorArgs() { + variables.factory = new framework.ioc( "/tests/extrabeans", + { singulars = { sheep = "bean" } } ); + var i = variables.factory.getBean( "item" ); + var n1 = application.itemCount; + var c = variables.factory.getBean( "construct", { one : "one", two : 2, item : "no-op" } ); + assertTrue( isNull( c.getOne() ) ); + assertEquals( 2, c.two ); + assertEquals( n1, application.itemCount ); + } + } diff --git a/tests/extrabeans/sheep/construct.cfc b/tests/extrabeans/sheep/construct.cfc new file mode 100644 index 00000000..0a09ebcd --- /dev/null +++ b/tests/extrabeans/sheep/construct.cfc @@ -0,0 +1,8 @@ +component accessors=true { + property one; + function init(two,item) { + this.two = two; + this.item = item; + return this; + } +} From e8f8c0f19ddd1682316b59969ad606297e27b0e7 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 27 Nov 2015 18:10:04 -0800 Subject: [PATCH 026/100] Allow render type to be a function #328 This allows custom renderers for built-in types by overriding the render_{type}() function or supplying a function or closure for the renderData() type argument. --- framework/one.cfc | 138 +++++++++++++++++++++------------- tests/frameworkRenderTest.cfc | 20 +++++ 2 files changed, 105 insertions(+), 53 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index 26e1753c..7be3267c 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -873,7 +873,13 @@ component { out = internalLayout( request._fw1.layouts[i], out ); } } - writeOutput( out ); + if ( isSimpleValue( out ) ) { + writeOutput( out ); + } else if ( structKeyExists( out, 'writer' ) ) { + out.writer( out.output ); + } else { + writeOutput( out.output ); + } setupResponseWrapper(); } @@ -2088,61 +2094,87 @@ component { return resourceCache[ cacheKey ]; } - private string function renderDataWithContentType() { - var out = ''; - var contentType = ''; - var type = request._fw1.renderData.type; - var data = request._fw1.renderData.data; + private struct function render_json( struct renderData ) { + return { + contentType = 'application/json; charset=utf-8', + output = serializeJSON( renderData.data ), + }; + } + + private struct function render_jsonp( struct renderData ) { + if ( !structKeyExists( renderData, 'jsonpCallback' ) || !len( renderData.jsonpCallback ) ){ + throw( type = 'FW1.jsonpCallbackRequired', + message = 'Callback was not defined', + detail = 'renderData() called with jsonp type requires a jsonpCallback' ); + } + return { + contentType = 'application/javascript; charset=utf-8', + output = renderData.jsonpCallback & "(" & serializeJSON( renderData.data ) & ");" + }; + } + + private struct function render_rawjson( struct renderData ) { + return { + contentType = 'application/json; charset=utf-8', + output = renderData.data + }; + } + + private struct function render_html( struct renderData ) { + structDelete( request._fw1, 'renderData' ); + return { + contentType = 'text/html; charset=utf-8', + output = renderData.data + }; + } + + private struct function render_xml( struct renderData ) { + var output = ''; + if ( isXML( renderData.data ) ) { + if ( isSimpleValue( renderData.data ) ) { + // XML as string already + output = renderData.data; + } else { + // XML object + output = toString( renderData.data ); + } + } else { + throw( type = 'FW1.UnsupportXMLRender', + message = 'Data is not XML', + detail = 'renderData() called with XML type but unrecognized data format' ); + } + return { + contentType = 'text/xml; charset=utf-8', + output = output + }; + } + + private struct function render_text( struct renderData ) { + return { + contentType = 'text/plain; charset=utf-8', + output = renderData.data + }; + } + + private struct function renderDataWithContentType() { + var out = { }; + var renderType = request._fw1.renderData.type; var statusCode = request._fw1.renderData.statusCode; var statusText = request._fw1.renderData.statusText; - switch ( type ) { - case 'json': - contentType = 'application/json; charset=utf-8'; - out = serializeJSON( data ); - break; - case 'jsonp': - contentType = 'application/javascript; charset=utf-8'; - if ( !len(request._fw1.renderData.jsonpCallback) ){ - throw( type = 'FW1.jsonpCallbackRequired', - message = 'Callback was not defined', - detail = 'renderData() called with jsonp type requires a jsonpCallback' ); - } - out = request._fw1.renderData.jsonpCallback & "(" & serializeJSON( data ) & ");"; - break; - case 'rawjson': - contentType = 'application/json; charset=utf-8'; - out = data; - break; - case 'html': - contentType = 'text/html; charset=utf-8'; - out = data; - structDelete( request._fw1, 'renderData' ); - break; - case 'xml': - contentType = 'text/xml; charset=utf-8'; - if ( isXML( data ) ) { - if ( isSimpleValue( data ) ) { - // XML as string already - out = data; - } else { - // XML object - out = toString( data ); - } + if ( isSimpleValue( renderType ) ) { + var fn_type = 'render_' & renderType; + if ( structKeyExists( variables, fn_type ) ) { + renderType = variables[ fn_type ]; + // evaluate with no FW/1 context! + out = renderType( request._fw1.renderData ); } else { - throw( type = 'FW1.UnsupportXMLRender', - message = 'Data is not XML', - detail = 'renderData() called with XML type but unrecognized data format' ); + throw( type = 'FW1.UnsupportedRenderType', + message = 'Only HTML, JSON, JSONP, RAWJSON, XML, and TEXT are supported', + detail = 'renderData() called with unknown type: ' & renderType ); } - break; - case 'text': - contentType = 'text/plain; charset=utf-8'; - out = data; - break; - default: - throw( type = 'FW1.UnsupportedRenderType', - message = 'Only HTML, JSON, JSONP, RAWJSON, XML, and TEXT are supported', - detail = 'renderData() called with unknown type: ' & type ); - break; + } else { + // assume it is a function + out = renderType( request._fw1.renderData ); } // in theory, we should use sendError() instead of setStatus() but some // Servlet containers interpret that to mean "Send my error page" instead @@ -2153,7 +2185,7 @@ component { } else { resp.setStatus( statusCode ); } - resp.setContentType( contentType ); + resp.setContentType( out.contentType ); return out; } diff --git a/tests/frameworkRenderTest.cfc b/tests/frameworkRenderTest.cfc index 8191c6b4..f58b858e 100644 --- a/tests/frameworkRenderTest.cfc +++ b/tests/frameworkRenderTest.cfc @@ -132,5 +132,25 @@ component extends="mxunit.framework.TestCase" { assertEquals( output, "custom trace render" ); } + public void function testRenderFunction() { + request.fw.onApplicationStart(); + variables.fw.renderData().type( function( renderData ) { + return { + contentType = "text/html; charset=utf-8", + output = "string", + writer = function( out ) { + writeOutput( "my written " & out ); + } + }; + } ).data( "myteststring" ); + var output = ""; + savecontent variable="output" { + request.fw.onRequest("/index.cfm"); + request.fw.onRequestEnd(); + } + assertTrue( output contains "my written string" ); + assertFalse( output contains "framework lifecycle trace" ); + } + } From 4a29d0f280ea864683affc0e97bad83a0c860de0 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 27 Nov 2015 18:22:06 -0800 Subject: [PATCH 027/100] Allow onMissingView() to return renderer struct #328 --- framework/one.cfc | 6 +++--- tests/onMissingViewLayoutTest.cfc | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index 7be3267c..d44439c1 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -771,10 +771,10 @@ component { * this can be overridden if you want to change the behavior when * FW/1 cannot find a matching view */ - public string function onMissingView( struct rc ) { + public any function onMissingView( struct rc ) { // unable to find a matching view - fail with a nice exception viewNotFound(); - // if we got here, we would return the string to be rendered + // if we got here, we would return the string or struct to be rendered // but viewNotFound() throws an exception... // for example, return view( 'main.missing' ); } @@ -1293,7 +1293,7 @@ component { * view() may be invoked inside views and layouts * returns the UI generated by the named view */ - public string function view( string path, struct args = { }, + public any function view( string path, struct args = { }, any missingView = { } ) { var viewPath = parseViewOrLayoutPath( path, 'view' ); if ( cachedFileExists( viewPath ) ) { diff --git a/tests/onMissingViewLayoutTest.cfc b/tests/onMissingViewLayoutTest.cfc index 3998f95f..952a0e61 100644 --- a/tests/onMissingViewLayoutTest.cfc +++ b/tests/onMissingViewLayoutTest.cfc @@ -33,6 +33,24 @@ component extends="tests.InjectableTest" { assertEquals( "TWOTEST", trim( output ) ); } + public void function testCustomRenderer() { + var omv = function( rc ) { + request.layout = false; + return { + contentType = "text/html; charset=utf-8", + output = "custom output" + }; + }; + variables.fw.onMissingView = omv; + variables.fwvars.onMissingView = omv; + variables.fw.onRequestStart( "" ); + var output = ""; + savecontent variable="output" { + variables.fw.onRequest( "" ); + } + assertEquals( "custom output", trim( output ) ); + } + private string function selectLayoutTwo( struct rc ) { setLayout( "main.two" ); return view( "main/test" ); From 72190b55d8576852ebbad9c6c59951d829363230 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 27 Nov 2015 18:24:26 -0800 Subject: [PATCH 028/100] Lift contentType to response #328 This allows onMissingView() to control content type as well as renderData(). --- framework/one.cfc | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index d44439c1..74608058 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -875,10 +875,16 @@ component { } if ( isSimpleValue( out ) ) { writeOutput( out ); - } else if ( structKeyExists( out, 'writer' ) ) { - out.writer( out.output ); } else { - writeOutput( out.output ); + if ( structKeyExists( out, 'contentType' ) ) { + var resp = getPageContext().getResponse(); + resp.setContentType( out.contentType ); + } + if ( structKeyExists( out, 'writer' ) ) { + out.writer( out.output ); + } else { + writeOutput( out.output ); + } } setupResponseWrapper(); } @@ -2185,7 +2191,6 @@ component { } else { resp.setStatus( statusCode ); } - resp.setContentType( out.contentType ); return out; } From 8ad64436ed16360ed33e4bf57244ee454389f1b4 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 27 Nov 2015 18:27:53 -0800 Subject: [PATCH 029/100] ACF compatibility #328 Trailing comma in struct. Sigh. --- framework/one.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/one.cfc b/framework/one.cfc index 74608058..1f8ca804 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2103,7 +2103,7 @@ component { private struct function render_json( struct renderData ) { return { contentType = 'application/json; charset=utf-8', - output = serializeJSON( renderData.data ), + output = serializeJSON( renderData.data ) }; } From af44b674d4c322e65658da05e17ddfdcc13a002d Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 27 Nov 2015 18:31:32 -0800 Subject: [PATCH 030/100] Test order dependency #328 --- tests/onMissingViewLayoutTest.cfc | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/onMissingViewLayoutTest.cfc b/tests/onMissingViewLayoutTest.cfc index 952a0e61..c9ef8cb0 100644 --- a/tests/onMissingViewLayoutTest.cfc +++ b/tests/onMissingViewLayoutTest.cfc @@ -7,6 +7,7 @@ component extends="tests.InjectableTest" { variables.fwvars.framework = { base = "/tests/omv" }; + structDelete( request, "layout" ); } public void function testSetLayout() { From b77fddd0c60a548272f25ef38b308f3e790868c0 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 1 Dec 2015 12:18:39 -0800 Subject: [PATCH 031/100] Update to house style --- framework/ioc.cfc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 1e05f36f..2037fa80 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -95,9 +95,9 @@ component { ( structKeyExists( variables, 'parent' ) && variables.parent.containsBean( beanName ) ); } - // return true if this factory has a parent. + // return true if this factory has a parent public boolean function hasParent() { - return structKeyExists(variables, 'parent'); + return structKeyExists( variables, 'parent' ); } // programmatically register new beans with the factory (add an actual CFC) From bad754beb63136308d720d3ac1c3f9bb537449f0 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 1 Dec 2015 12:25:15 -0800 Subject: [PATCH 032/100] Use hasParent() instead of structKeyExists() Now that we have a method for this, use it, instead of low-level tests. --- framework/ioc.cfc | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 2037fa80..cb5cede6 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -92,7 +92,7 @@ component { public boolean function containsBean( string beanName ) { discoverBeans(); return structKeyExists( variables.beanInfo, beanName ) || - ( structKeyExists( variables, 'parent' ) && variables.parent.containsBean( beanName ) ); + ( hasParent() && variables.parent.containsBean( beanName ) ); } // return true if this factory has a parent @@ -142,7 +142,7 @@ component { discoverBeans(); if ( structKeyExists( variables.beanInfo, beanName ) ) { return resolveBean( beanName, constructorArgs ); - } else if ( structKeyExists( variables, 'parent' ) ) { + } else if ( hasParent() ) { // ideally throw an exception for non-DI/1 parent when args passed // WireBox adapter can do that since we control it but we can't do // anything for other bean factories - will revisit before release @@ -161,13 +161,13 @@ component { if ( structKeyExists( variables.beanInfo, beanName ) ) { return variables.beanInfo[ beanName ]; } - if ( structKeyExists( variables, 'parent' ) ) { + if ( hasParent() ) { return parentBeanInfo( beanName ); } throw 'bean not found: #beanName#'; } else { var result = { beanInfo = { } }; - if ( structKeyExists( variables, 'parent' ) ) { + if ( hasParent() ) { if ( flatten || len( regex ) ) { structAppend( result.beanInfo, parentBeanInfoList( flatten ).beanInfo ); structAppend( result.beanInfo, variables.beanInfo ); @@ -210,7 +210,7 @@ component { discoverBeans(); if ( structKeyExists( variables.beanInfo, beanName ) ) { return variables.beanInfo[ beanName ].isSingleton; - } else if ( structKeyExists( variables, 'parent' ) ) { + } else if ( hasParent() ) { try { return variables.parent.isSingleton( beanName ); } catch ( any e ) { @@ -673,7 +673,7 @@ component { args[ property ] = injection.overrides[ property ]; } else if ( structKeyExists( partialBean.injection, property ) ) { args[ property ] = partialBean.injection[ property ].bean; - } else if ( structKeyExists( variables, 'parent' ) && variables.parent.containsBean( property ) ) { + } else if ( hasParent() && variables.parent.containsBean( property ) ) { args[ property ] = variables.parent.getBean( property ); } else { // allow for possible convention-based bean factory @@ -822,7 +822,7 @@ component { throw 'internal error: invalid metadata for #beanName#'; } } else { - if ( structKeyExists( variables, 'parent' ) && variables.parent.containsBean( beanName ) ) { + if ( hasParent() && variables.parent.containsBean( beanName ) ) { bean = variables.parent.getBean( beanName ); } else { bean = missingBean( beanName = beanName, dependency = true ); From 37d10a394a2d988722080e17076cc4d7c2121f17 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 15 Dec 2015 18:42:37 -0800 Subject: [PATCH 033/100] Tentative top-level singleton caching #409 --- framework/ioc.cfc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index cb5cede6..44072e51 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -43,6 +43,7 @@ component { variables.beanInfo = { }; variables.beanCache = { }; variables.resolutionCache = { }; + variables.getBeanCache = { }; variables.initMethodCache = { }; variables.settersInfo = { }; variables.autoExclude = [ @@ -141,7 +142,12 @@ component { public any function getBean( string beanName, struct constructorArgs = { } ) { discoverBeans(); if ( structKeyExists( variables.beanInfo, beanName ) ) { - return resolveBean( beanName, constructorArgs ); + if ( structKeyExists( variables.getBeanCache, beanName ) ) { + return variables.getBeanCache[ beanName ]; + } + var bean = resolveBean( beanName, constructorArgs ); + if ( isSingleton( beanName ) ) variables.getBeanCache[ beanName ] = bean; + return bean; } else if ( hasParent() ) { // ideally throw an exception for non-DI/1 parent when args passed // WireBox adapter can do that since we control it but we can't do From 891e6eaaa264824ee46e460033753889afb9b9aa Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 15 Dec 2015 23:15:25 -0800 Subject: [PATCH 034/100] Tackle dependency caching too #409 --- framework/ioc.cfc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 44072e51..fcc4d9b0 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -44,6 +44,7 @@ component { variables.beanCache = { }; variables.resolutionCache = { }; variables.getBeanCache = { }; + variables.accumulatorCache = { }; variables.initMethodCache = { }; variables.settersInfo = { }; variables.autoExclude = [ @@ -255,6 +256,8 @@ component { discoverBeans(); variables.beanCache = { }; variables.resolutionCache = { }; + variables.accumulatorCache = { }; + variables.getBeanCache = { }; variables.initMethodCache = { }; for ( var key in variables.beanInfo ) { if ( !structKeyExists( variables.beanInfo[ key ], "isSingleton" ) ) @@ -654,7 +657,10 @@ component { // do enough resolution to create and initialization this bean // returns a struct of the bean and a struct of beans and setters still to run // construction phase: - var partialBean = resolveBeanCreate( beanName, { injection = { }, dependencies = { } }, constructorArgs ); + if ( !structKeyExists( variables.accumulatorCache, beanName ) ) { + variables.accumulatorCache[ beanName ] = { injection = { }, dependencies = { } }; + } + var partialBean = resolveBeanCreate( beanName, variables.accumulatorCache[ beanName ], constructorArgs ); if ( structKeyExists( variables.resolutionCache, beanName ) && variables.resolutionCache[ beanName ] ) { // fully resolved, no action needed this time From b8538119ea736df8215d4780e1941412841c3452 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 15 Dec 2015 23:49:16 -0800 Subject: [PATCH 035/100] Minor tweak for #409 Ensure we don't overwrite any cached dependencies --- framework/ioc.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index fcc4d9b0..f65f0243 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -738,7 +738,7 @@ component { var bean = 0; if ( structKeyExists( variables.beanInfo, beanName ) ) { var info = variables.beanInfo[ beanName ]; - accumulator.dependencies[ beanName ] = { }; + if ( !structKeyExists( accumulator.dependencies, beanName ) ) accumulator.dependencies[ beanName ] = { }; if ( structKeyExists( info, 'cfc' ) ) { /*******************************************************/ var metaBean = cachable( beanName ); From db17e5b86f1aca1609937594da99732a0e8ed43f Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 15 Dec 2015 23:57:09 -0800 Subject: [PATCH 036/100] Ensure caching doesn't mess with construct args #409 --- tests/TransientTest.cfc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/TransientTest.cfc b/tests/TransientTest.cfc index a9d62c3c..f6613f50 100644 --- a/tests/TransientTest.cfc +++ b/tests/TransientTest.cfc @@ -50,10 +50,13 @@ component extends="mxunit.framework.TestCase" { { singulars = { sheep = "bean" } } ); var i = variables.factory.getBean( "item" ); var n1 = application.itemCount; - var c = variables.factory.getBean( "construct", { one : "one", two : 2, item : "no-op" } ); - assertTrue( isNull( c.getOne() ) ); - assertEquals( 2, c.two ); + var c1 = variables.factory.getBean( "construct", { one : "one", two : 2, item : "no-op" } ); + assertTrue( isNull( c1.getOne() ) ); + assertEquals( 2, c1.two ); assertEquals( n1, application.itemCount ); + var c2 = variables.factory.getBean( "construct", { one : 1, two : "two", item : "something" } ); + assertTrue( isNull( c2.getOne() ) ); + assertEquals( "two", c2.two ); } } From 1730a28742fe744e5619e2b74f3f7c8c1a7f4dcf Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 16 Dec 2015 16:02:33 -0800 Subject: [PATCH 037/100] Fix #410 by adding LICENSE / updating README etc Mention grant of copyright release in CONTRIBUTING. --- CONTRIBUTING.md | 2 ++ LICENSE | 14 ++++++++++++++ README.md | 13 ++++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 LICENSE diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5bc7ea2a..cf78256b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,6 +10,8 @@ Look at `run-tests-example.sh` to see how to run tests locally (copy that shell Please follow the same formatting as the existing code, especially in terms of spacing around operators, parentheses, braces and so on. If in doubt, ask on the mailing list. +By submitting a Pull Request, you are granting copyright license to Sean Corfield and that your submission may be legally released under the Apache Source License 2.0 (http://www.apache.org/licenses/LICENSE-2.0). + The **master** branch represents the current stable release of FW/1. Do not submit Pull Requests against **master**. Showstopping bugs should be raised as issues and fixes will be applied to **develop** (if appropriate) and backported to **master** manually. **Note:** Do not submit Pull Requests against [Sean's personal fork](https://github.com/seancorfield/fw1) - that exists for historical reasons and Github doesn't let you turn Pull Requests off. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..a6f29c8d --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ +Copyright (c) 2009-2015 Sean Corfield (see individual files for any + additional copyright holders) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/README.md b/README.md index 30327cb8..731e71e7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ [![Build Status](https://travis-ci.org/framework-one/fw1.png)](https://travis-ci.org/framework-one/fw1) +# Overview + This FW/1 directory is a complete web application and expects to live in its own webroot if you plan to run the applications within it. To use FW/1 in a separate webroot you can either copy the `framework` directory to that webroot or add a mapping @@ -9,6 +11,8 @@ in your admin - you can't just use a per-application mapping. Please read the [Framework One Code of Conduct](CODE_OF_CONDUCT.md) - we want FW/1 to be a welcoming and supportive environment for everyone to feel comfortable contributing! +# Resources + **Project home:** http://fw1.riaforge.org **Documentation / Wiki:** http://framework-one.github.io/documentation/ / http://github.com/framework-one/fw1/wiki @@ -19,7 +23,7 @@ Please read the [Framework One Code of Conduct](CODE_OF_CONDUCT.md) - we want FW **Chat:** The [CFML team Slack](http://cfml-slack.herokuapp.com) has a [dedicated #fw1 channel](https://cfml.slack.com/messages/fw1/). -**Running the tests:** +# Running the Tests The Ant `build.xml` file is primarily designed to be used by Travis to run the tests automatically, but it is possible to run the tests locally, with some setup: @@ -40,3 +44,10 @@ See the `run-tests-example.sh` file for a template (for Mac/Linux). * `server.name` should be the test domain you have configured * `server.port` should be the port on which you access that test domain * `run-tests-mxunit` is the actual Ant task that does the testing + +# Copyright and License + +Copyright (c) 2009-2015 Sean Corfield (and others -- see individual files for additional copyright holders). All rights reserved. +The use and distribution terms for this software are covered by the Apache Software License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) which can also be found in the file LICENSE at the root of this distribution and in individual licensed files. +By using this software in any fashion, you are agreeing to be bound by the terms of this license. You must not remove this notice, or any other, from this software. + From d56fea99fd880f39a812084e1b265833ab7b36b7 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 18 Dec 2015 16:18:45 -0800 Subject: [PATCH 038/100] Fix #413 by removing restriction on layout() --- framework/one.cfc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index 1f8ca804..d75c53b9 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -714,7 +714,6 @@ component { request.event = event; // reset lifecycle flags: structDelete( request, 'layout' ); - structDelete( request._fw1, 'controllerExecutionComplete' ); structDelete( request._fw1, 'controllerExecutionStarted' ); structDelete( request._fw1, 'overrideViewAction' ); if ( structKeyExists( request._fw1, 'renderData' ) ) { @@ -843,7 +842,6 @@ component { // do "nothing" since this is a control flow exception } } - request._fw1.controllerExecutionComplete = true; if ( structKeyExists( request._fw1, 'renderData' ) ) { out = renderDataWithContentType(); @@ -1826,10 +1824,6 @@ component { if ( structKeyExists( rc, '$' ) ) { $ = rc.$; } - if ( !structKeyExists( request._fw1, 'controllerExecutionComplete' ) ) { - throw( type='FW1.layoutExecutionFromController', message='Invalid to call the layout method at this point.', - detail='The layout method should not be called prior to the completion of the controller execution phase.' ); - } var response = ''; savecontent variable="response" { include '#layoutPath#'; From 7785254ce2ec91f1c0a5c12f188a55eb3a60f0a9 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sun, 20 Dec 2015 16:01:05 -0800 Subject: [PATCH 039/100] Add Waffle badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 731e71e7..3bc6ead3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ [![Build Status](https://travis-ci.org/framework-one/fw1.png)](https://travis-ci.org/framework-one/fw1) +[![Stories in Ready](https://badge.waffle.io/framework-one/fw1.png?label=ready&title=Ready)](http://waffle.io/framework-one/fw1) # Overview From cdb0e0634f42b9dea2aba30ba5eda1cddd2c37b1 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sun, 20 Dec 2015 16:03:18 -0800 Subject: [PATCH 040/100] Cleanup badges --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3bc6ead3..cebd495b 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -[![Build Status](https://travis-ci.org/framework-one/fw1.png)](https://travis-ci.org/framework-one/fw1) -[![Stories in Ready](https://badge.waffle.io/framework-one/fw1.png?label=ready&title=Ready)](http://waffle.io/framework-one/fw1) +Travis CI: [![Build Status](https://travis-ci.org/framework-one/fw1.png)](https://travis-ci.org/framework-one/fw1) -- +Waffle.IO: [![Stories in Ready](https://badge.waffle.io/framework-one/fw1.png?label=ready&title=Ready)](http://waffle.io/framework-one/fw1) # Overview From 36e86f2b24b6220ad214f7aceb966ac1f3c648e8 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 21 Dec 2015 11:28:06 -0800 Subject: [PATCH 041/100] First cut of #414 to add Boot support Needs testing and refinement. --- framework/cfmljure.cfc | 29 ++++++++++++++++++++--------- framework/ioclj.cfc | 21 +++++++++++---------- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index 2b633387..afb5c408 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -1,6 +1,6 @@ component { variables._fw1_version = "4.0.0-snapshot"; - variables._cfmljure_version = "1.0.1-snapshot"; + variables._cfmljure_version = "1.1.0-snapshot"; /* Copyright (c) 2012-2015, Sean Corfield @@ -21,6 +21,7 @@ component { // constructor public any function init( string project = "", numeric timeout = 300, string lein = "lein", // to allow default to be overridden + string boot = "", // to allow Boot to be selected instead string ns = "", any root = 0 ) { variables.refCache = { }; if ( project != "" ) { @@ -34,22 +35,32 @@ component { var script = ""; var cmd = { }; var tmpDir = ""; + var buildType = ""; + var buildCommand = ""; + if ( len( boot ) ) { + // select Boot build tool + buildType = "boot"; + buildCommand = boot & " aot show -C"; + } else { + buildType = "lein"; + buildCommand = lein & " with-profile production do clean, compile, classpath"; + } if ( nixLike ) { // *nix / Mac tmpDir = "/tmp"; - script = getTempFile( tmpDir, "lein" ); + script = getTempFile( tmpDir, buildType ); cmd = { cd = "cd", run = "/bin/sh", arg = script, // make sure we are not trying to run under root account! preflightCmd = "if [ `id -u` -eq 0 ]; then >&2 echo 'DO NOT RUN CFML OR CFMLJURE AS ROOT!'; exit 1; fi#nl#", exitCmd = "exit 0#nl#" }; - // ensure Servlet container's options do not affect Leiningen: - lein = "JAVA_OPTS= " & lein; + // ensure Servlet container's options do not affect the build tool: + buildCommand = "JAVA_OPTS= " & buildCommand; } else { // Windows tmpDir = replace( javaLangSystem.getenv( "TEMP" ), chr(92), "/", "all" ); - script = getTempFile( tmpDir, "lein" ); + script = getTempFile( tmpDir, buildType ); script &= ".bat"; cmd = { cd = "chdir", run = script, arg = "", @@ -61,7 +72,7 @@ component { script, "#cmd.cd# #project#" & nl & cmd.preflightCmd & - "#lein# with-profile production do clean, compile, classpath" & nl & + buildCommand & nl & cmd.exitCmd ); var classpath = ""; @@ -77,8 +88,8 @@ component { if ( structKeyExists( URL, "cfmljure" ) && URL.cfmljure == "abortOnFailure" ) { writeDump( var = cmd, label = "Unable to cfexecute this script" ); - if ( !isNull( classpath ) ) writeDump( var = classpath, label = "Leiningen stdout" ); - if ( !isNull( errors ) ) writeDump( var = errors, label = "Leiningen stderr" ); + if ( !isNull( classpath ) ) writeDump( var = classpath, label = "Build (#buildType#) stdout" ); + if ( !isNull( errors ) ) writeDump( var = errors, label = "Build (#buildType#) stderr" ); writeDump( var = e, label = "Full stack trace" ); abort; } @@ -166,7 +177,7 @@ component { variables._clj_root = root; variables._clj_ns = ns; } else { - throw "cfmljure requires the path of a Leiningen project."; + throw "cfmljure requires the path of a Clojure project."; } return this; } diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index dbc18bf4..f1581f70 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -1,6 +1,6 @@ component extends=framework.ioc { variables._fw1_version = "4.0.0-snapshot"; - variables._ioclj_version = "1.0.1-snapshot"; + variables._ioclj_version = "1.1.0-snapshot"; /* Copyright (c) 2015, Sean Corfield @@ -28,8 +28,10 @@ component extends=framework.ioc { super.init( folders, config ); variables.cljBeans = { }; if ( structKeyExists( config, "noClojure" ) && config.noClojure ) return; - // find the first folder that includes project.clj - that's our project - variables.project = findProjectFile(); + var lein = structKeyExists( config, "lein" ) ? config.lein : "lein"; + var boot = structKeyExists( config, "boot" ) ? config.boot : ""; // default is not Boot + // find the first folder that includes project.clj (or build.boot) - that's our project + variables.project = findProjectFile( len( boot ) ? "build.boot" : "project.clj" ); discoverClojureFiles(); // list of namespaces to expose: var ns = [ ]; @@ -38,16 +40,15 @@ component extends=framework.ioc { } // and create a cfmljure instance var timeout = structKeyExists( config, "timeout" ) ? config.timeout : 300; - var lein = structKeyExists( config, "lein" ) ? config.lein : "lein"; var useServerScope = structKeyExists( config, "server" ) ? config.server : false; var cfmljure = 0; if ( useServerScope ) { if ( !structKeyExists( server, "__cfmljure" ) ) { - server.__cfmljure = new framework.cfmljure( variables.project, timeout, lein ); + server.__cfmljure = new framework.cfmljure( variables.project, timeout, lein, boot ); } cfmljure = server.__cfmljure; } else { - cfmljure = new framework.cfmljure( variables.project, timeout, lein ); + cfmljure = new framework.cfmljure( variables.project, timeout, lein, boot ); } if ( cfmljure.isAvailable() ) { // Clojure loaded -- install the discovered namespaces @@ -207,7 +208,7 @@ component extends=framework.ioc { } } - private string function findProjectFile() { + private string function findProjectFile( string buildFile ) { for ( var folder in variables.folderArray ) { if ( right( folder, 1 ) == "/" ) { if ( len( folder ) == 1 ) folder = ""; @@ -222,13 +223,13 @@ component extends=framework.ioc { if ( len( path ) == 1 ) path = ""; else path = left( path, len( path ) - 1 ); } - if ( fileExists( path & "/project.clj" ) ) { + if ( fileExists( path & "/" & buildFile ) ) { // found our Clojure project, return it - if ( variables.debug ) variables.stdout.println( "ioclj: using #path#/project.clj for Clojure root" ); + if ( variables.debug ) variables.stdout.println( "ioclj: using #path#/#buildFile# for Clojure root" ); return path; } } - throw "Unable to find project.clj in any of: #variables.folderList#"; + throw "Unable to find #buildFile# in any of: #variables.folderList#"; } } From 64ab5cb8fcd2a223dcdeb05aef9128817876b360 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 21 Dec 2015 13:07:59 -0800 Subject: [PATCH 042/100] Add Boot files for tesing #414 By adding `diConfig : { boot : "boot" },` to `Application.cfc` for `6helloclojure` you can use Boot instead of Leiningen. --- examples/subsystems/6helloclojure/boot.properties | 6 ++++++ examples/subsystems/6helloclojure/build.boot | 5 +++++ 2 files changed, 11 insertions(+) create mode 100644 examples/subsystems/6helloclojure/boot.properties create mode 100644 examples/subsystems/6helloclojure/build.boot diff --git a/examples/subsystems/6helloclojure/boot.properties b/examples/subsystems/6helloclojure/boot.properties new file mode 100644 index 00000000..421b1965 --- /dev/null +++ b/examples/subsystems/6helloclojure/boot.properties @@ -0,0 +1,6 @@ +#http://boot-clj.com +#Mon Dec 21 11:44:14 PST 2015 +BOOT_CLOJURE_NAME=org.clojure/clojure +BOOT_CLOJURE_VERSION=1.7.0 +BOOT_VERSION=2.5.2 +BOOT_EMIT_TARGET=no diff --git a/examples/subsystems/6helloclojure/build.boot b/examples/subsystems/6helloclojure/build.boot new file mode 100644 index 00000000..1afcedf2 --- /dev/null +++ b/examples/subsystems/6helloclojure/build.boot @@ -0,0 +1,5 @@ +;; very minimal Boot build file: for use with cfmljure we do not +;; need any pom / jar information, just the dependencies and a +;; matching boot.properties file +(set-env! :resource-paths #{"src"} + :dependencies '[[org.clojure/clojure "1.7.0"]]) From cef7fa3aeee6a6462a2a95880c832785f6ab2292 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 21 Dec 2015 22:11:44 -0800 Subject: [PATCH 043/100] Add boot test support to 6helloclojure #414 --- examples/subsystems/6helloclojure/build.boot | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/subsystems/6helloclojure/build.boot b/examples/subsystems/6helloclojure/build.boot index 1afcedf2..29581d13 100644 --- a/examples/subsystems/6helloclojure/build.boot +++ b/examples/subsystems/6helloclojure/build.boot @@ -2,4 +2,8 @@ ;; need any pom / jar information, just the dependencies and a ;; matching boot.properties file (set-env! :resource-paths #{"src"} - :dependencies '[[org.clojure/clojure "1.7.0"]]) + :source-paths #{"test"} + :dependencies '[[org.clojure/clojure "1.7.0"] + [adzerk/boot-test "1.0.7"]]) + +(require '[adzerk.boot-test :refer [test]]) From 8921c7ef673ecab8930a259f21b85aadf41e75f9 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 22 Dec 2015 21:29:32 -0800 Subject: [PATCH 044/100] Fix #411 by passing headers to controllers Controllers now get `rc` and `headers`. `onMissingMethod` now gets `rc`, `method`, and `headers`. --- framework/one.cfc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index d75c53b9..d499bfd2 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1469,7 +1469,7 @@ component { if ( structKeyExists( cfc, method ) ) { try { internalFrameworkTrace( 'calling #lifecycle# controller', tuple.subsystem, tuple.section, method ); - evaluate( 'cfc.#method#( rc = request.context )' ); + evaluate( 'cfc.#method#( rc = request.context, headers = request._fw1.headers )' ); } catch ( any e ) { setCfcMethodFailureInfo( cfc, method ); rethrow; @@ -1477,7 +1477,7 @@ component { } else if ( structKeyExists( cfc, 'onMissingMethod' ) ) { try { internalFrameworkTrace( 'calling #lifecycle# controller (via onMissingMethod)', tuple.subsystem, tuple.section, method ); - evaluate( 'cfc.#method#( rc = request.context, method = lifecycle )' ); + evaluate( 'cfc.#method#( rc = request.context, method = lifecycle, headers = request._fw1.headers )' ); } catch ( any e ) { setCfcMethodFailureInfo( cfc, method ); rethrow; @@ -2716,10 +2716,11 @@ component { // certain remote calls do not have URL or form scope: if ( isDefined( 'URL' ) ) structAppend( request.context, URL ); if ( isDefined( 'form' ) ) structAppend( request.context, form ); + var httpData = getHttpRequestData(); if ( variables.framework.enableJSONPOST ) { // thanks to Adam Tuttle and by proxy Jason Dean and Ray Camden for the // seed of this code, inspired by Taffy's basic deserialization - var body = getHttpRequestData().content; + var body = httpData.content; if ( isBinary( body ) ) body = charSetEncode( body, "utf-8" ); if ( len( body ) ) { switch ( listFirst( CGI.CONTENT_TYPE, ';' ) ) { @@ -2739,6 +2740,7 @@ component { } } } + request._fw1.headers = httpData.headers; // figure out the request action before restoring flash context: if ( !structKeyExists( request.context, variables.framework.action ) ) { request.context[ variables.framework.action ] = getFullyQualifiedAction( variables.framework.home ); From 3e6c1f41911a00c77b46239fe52d9ca9331f7bda Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 22 Dec 2015 22:15:25 -0800 Subject: [PATCH 045/100] Fix #412 by adding headers() builder Adds renderer() function to retrieve the builder. Adds header() to the builder. Sets HTTP response headers from the array of headers. --- framework/one.cfc | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index d499bfd2..6d253c72 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1145,27 +1145,44 @@ component { statusText = '', jsonpCallback = jsonpCallback }; - // return a build to support nicer rendering syntax + // return a builder to support nicer rendering syntax + return renderer(); + } + + public any function renderer() { var builder = { }; structAppend( builder, { // allow type and data to be overridden just for completeness type : function( v ) { + if ( !structKeyExists( request._fw1, 'renderData' ) ) request._fw1.renderData = { }; request._fw1.renderData.type = v; return builder; }, data : function( v ) { + if ( !structKeyExists( request._fw1, 'renderData' ) ) request._fw1.renderData = { }; request._fw1.renderData.data = v; return builder; }, + header : function( h, v ) { + if ( !structKeyExists( request._fw1, 'renderData' ) ) request._fw1.renderData = { }; + if ( !structKeyExists( request._fw1.renderData, 'headers' ) ) { + request._fw1.renderData.headers = [ ]; + } + arrayAppend( request._fw1.renderData.headers, { name = h, value = v } ); + return builder; + }, statusCode : function( v ) { + if ( !structKeyExists( request._fw1, 'renderData' ) ) request._fw1.renderData = { }; request._fw1.renderData.statusCode = v; return builder; }, statusText : function( v ) { + if ( !structKeyExists( request._fw1, 'renderData' ) ) request._fw1.renderData = { }; request._fw1.renderData.statusText = v; return builder; }, jsonpCallback : function( v ) { + if ( !structKeyExists( request._fw1, 'renderData' ) ) request._fw1.renderData = { }; request._fw1.renderData.jsonpCallback = v; return builder; } @@ -2161,6 +2178,8 @@ component { var renderType = request._fw1.renderData.type; var statusCode = request._fw1.renderData.statusCode; var statusText = request._fw1.renderData.statusText; + var headers = structKeyExists( request._fw1.renderData, 'headers' ) ? + request._fw1.renderData.headers : [ ]; if ( isSimpleValue( renderType ) ) { var fn_type = 'render_' & renderType; if ( structKeyExists( variables, fn_type ) ) { @@ -2176,10 +2195,13 @@ component { // assume it is a function out = renderType( request._fw1.renderData ); } + var resp = getPageContext().getResponse(); + for ( var h in headers ) { + resp.setHeader( h.name, h.value ); + } // in theory, we should use sendError() instead of setStatus() but some // Servlet containers interpret that to mean "Send my error page" instead // of just sending the response you actually want! - var resp = getPageContext().getResponse(); if ( len( statusText ) ) { resp.setStatus( statusCode, statusText ); } else { From 0d4b21db9d54c2e21584574ecb330a9d2d7804eb Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Thu, 24 Dec 2015 14:19:40 -0800 Subject: [PATCH 046/100] Fix #415 by adding clj: prefixed paths --- framework/ioclj.cfc | 117 +++++++++++++++++++++++++------------------- 1 file changed, 68 insertions(+), 49 deletions(-) diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index f1581f70..e7d31a5c 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -24,8 +24,22 @@ component extends=framework.ioc { if ( variables.debug ) { variables.stdout = createObject( "java", "java.lang.System" ).out; } + if ( isSimpleValue( folders ) ) { + folders = listToArray( folders ); + } + var cfmlFolders = [ ]; + var allFolders = [ ]; + for ( var folder in folders ) { + if ( len( folder ) > 4 && left( folder, 4 ) == "clj:" ) { + arrayAppend( allFolders, right( folder, len( folder ) - 4 ) ); + } else { + arrayAppend( cfmlFolders, folder ); + arrayAppend( allFolders, folder ); + } + } + variables.allFolderArray = allFolders; // initialize DI/1 parent - super.init( folders, config ); + super.init( cfmlFolders, config ); variables.cljBeans = { }; if ( structKeyExists( config, "noClojure" ) && config.noClojure ) return; var lein = structKeyExists( config, "lein" ) ? config.lein : "lein"; @@ -154,62 +168,67 @@ component extends=framework.ioc { private void function discoverClojureFiles() { var cljs = [ ]; - var src = variables.project & "/src"; - var n = len( src ) + 1; // allow for trailing / - try { - cljs = directoryList( src, true, "path", "*.clj" ); - // we also support .cljc files - var cljcs = directoryList( src, true, "path", "*.cljc" ); - for ( var cljcOSPath in cljcs ) cljs.append( cljcOSPath ); - } catch ( any e ) { - // assume bad path and ignore it - } - for ( var cljOSPath in cljs ) { - var cljPath = replace( cljOSPath, chr(92), "/", "all" ); - cljPath = right( cljPath, len( cljPath ) - n ); - // allow for extension being either .clj or .cljc - cljPath = left( cljPath, len( cljPath ) - ( right( cljPath, 1 ) == "c" ? 5 : 4 ) ); - var ns = replace( replace( cljPath, "/", ".", "all" ), "_", "-", "all" ); - // per #366, the pattern allowed is - // top-level(.optional)*.plural.(prefix.)*name - // and this will generate prefixNameSingular - var parts = listToArray( cljPath, "/" ); - var nParts = arrayLen( parts ); - // ignore temp files from editors (starting with .) - if ( left( parts[ nParts ], 1 ) == "." ) continue; - if ( nParts >= 3 ) { - var pluralCandidate = 2; - do { - var lbo = parts[ pluralCandidate ]; - var lbo1 = singular( lbo ); - ++pluralCandidate; - } while ( lbo == lbo1 && pluralCandidate < nParts ); - if ( lbo1 != lbo ) { - var beanName = ""; - while ( pluralCandidate <= nParts ) { - beanName &= parts[ pluralCandidate ]; + for ( var folder in variables.allFolderArray ) { + var src = folder & "/src"; + var expandedFolder = expandPath( src ); + if ( directoryExists( expandedFolder ) ) src = expandedFolder; + if ( !directoryExists( src ) ) continue; + var n = len( src ) + 1; // allow for trailing / + try { + cljs = directoryList( src, true, "path", "*.clj" ); + // we also support .cljc files + var cljcs = directoryList( src, true, "path", "*.cljc" ); + for ( var cljcOSPath in cljcs ) cljs.append( cljcOSPath ); + } catch ( any e ) { + // assume bad path and ignore it + } + for ( var cljOSPath in cljs ) { + var cljPath = replace( cljOSPath, chr(92), "/", "all" ); + cljPath = right( cljPath, len( cljPath ) - n ); + // allow for extension being either .clj or .cljc + cljPath = left( cljPath, len( cljPath ) - ( right( cljPath, 1 ) == "c" ? 5 : 4 ) ); + var ns = replace( replace( cljPath, "/", ".", "all" ), "_", "-", "all" ); + // per #366, the pattern allowed is + // top-level(.optional)*.plural.(prefix.)*name + // and this will generate prefixNameSingular + var parts = listToArray( cljPath, "/" ); + var nParts = arrayLen( parts ); + // ignore temp files from editors (starting with .) + if ( left( parts[ nParts ], 1 ) == "." ) continue; + if ( nParts >= 3 ) { + var pluralCandidate = 2; + do { + var lbo = parts[ pluralCandidate ]; + var lbo1 = singular( lbo ); ++pluralCandidate; - } - beanName &= lbo1; - if ( structKeyExists( variables.cljBeans, beanName ) ) { - throw "#beanName# is not unique (from #cljPath#)"; - } else { - variables.cljBeans[ beanName ] = { - ns : ns, nsx : parts, type : lbo1, - isSingleton : true // for DI/1 compatibility - }; + } while ( lbo == lbo1 && pluralCandidate < nParts ); + if ( lbo1 != lbo ) { + var beanName = ""; + while ( pluralCandidate <= nParts ) { + beanName &= parts[ pluralCandidate ]; + ++pluralCandidate; + } + beanName &= lbo1; + if ( structKeyExists( variables.cljBeans, beanName ) ) { + throw "#beanName# is not unique (from #cljPath#)"; + } else { + variables.cljBeans[ beanName ] = { + ns : ns, nsx : parts, type : lbo1, + isSingleton : true // for DI/1 compatibility + }; + } + } else if ( variables.debug ) { + variables.stdout.println( "ioclj: ignoring #cljPath#.clj because it has no plural segment" ); } } else if ( variables.debug ) { - variables.stdout.println( "ioclj: ignoring #cljPath#.clj because it has no plural segment" ); + variables.stdout.println( "ioclj: ignoring #cljPath#.clj because it does not have at least three segments" ); } - } else if ( variables.debug ) { - variables.stdout.println( "ioclj: ignoring #cljPath#.clj because it does not have at least three segments" ); } } } private string function findProjectFile( string buildFile ) { - for ( var folder in variables.folderArray ) { + for ( var folder in variables.allFolderArray ) { if ( right( folder, 1 ) == "/" ) { if ( len( folder ) == 1 ) folder = ""; else folder = left( folder, len( folder ) - 1 ); @@ -229,7 +248,7 @@ component extends=framework.ioc { return path; } } - throw "Unable to find #buildFile# in any of: #variables.folderList#"; + throw "Unable to find #buildFile# in any of: #arrayToList( variables.allFolderArray )#"; } } From 0c757b10be93f2dd3e27a10ea879a75c63e2deb2 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 25 Dec 2015 17:15:06 -0800 Subject: [PATCH 047/100] Fix #415 again by adding cfml: prefixed paths --- framework/ioclj.cfc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index e7d31a5c..f6f5ee95 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -28,16 +28,18 @@ component extends=framework.ioc { folders = listToArray( folders ); } var cfmlFolders = [ ]; - var allFolders = [ ]; + var cljFolders = [ ]; for ( var folder in folders ) { if ( len( folder ) > 4 && left( folder, 4 ) == "clj:" ) { - arrayAppend( allFolders, right( folder, len( folder ) - 4 ) ); + arrayAppend( cljFolders, right( folder, len( folder ) - 4 ) ); + } else if ( len( folder ) > 5 && left( folder, 4 ) == "cfml:" ) { + arrayAppend( cfmlFolders, right( folder, len( folder ) - 5 ); } else { arrayAppend( cfmlFolders, folder ); - arrayAppend( allFolders, folder ); + arrayAppend( cljFolders, folder ); } } - variables.allFolderArray = allFolders; + variables.cljFolderArray = cljFolders; // initialize DI/1 parent super.init( cfmlFolders, config ); variables.cljBeans = { }; @@ -168,7 +170,7 @@ component extends=framework.ioc { private void function discoverClojureFiles() { var cljs = [ ]; - for ( var folder in variables.allFolderArray ) { + for ( var folder in variables.cljFolderArray ) { var src = folder & "/src"; var expandedFolder = expandPath( src ); if ( directoryExists( expandedFolder ) ) src = expandedFolder; @@ -228,7 +230,7 @@ component extends=framework.ioc { } private string function findProjectFile( string buildFile ) { - for ( var folder in variables.allFolderArray ) { + for ( var folder in variables.cljFolderArray ) { if ( right( folder, 1 ) == "/" ) { if ( len( folder ) == 1 ) folder = ""; else folder = left( folder, len( folder ) - 1 ); @@ -248,7 +250,7 @@ component extends=framework.ioc { return path; } } - throw "Unable to find #buildFile# in any of: #arrayToList( variables.allFolderArray )#"; + throw "Unable to find #buildFile# in any of: #arrayToList( variables.cljFolderArray )#"; } } From 890e860e39407b23d078645ec0f92067b567e65c Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 29 Dec 2015 12:30:12 -0800 Subject: [PATCH 048/100] Typo in ioclj.cfc --- framework/ioclj.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index f6f5ee95..8d1e6b9a 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -33,7 +33,7 @@ component extends=framework.ioc { if ( len( folder ) > 4 && left( folder, 4 ) == "clj:" ) { arrayAppend( cljFolders, right( folder, len( folder ) - 4 ) ); } else if ( len( folder ) > 5 && left( folder, 4 ) == "cfml:" ) { - arrayAppend( cfmlFolders, right( folder, len( folder ) - 5 ); + arrayAppend( cfmlFolders, right( folder, len( folder ) - 5 ) ); } else { arrayAppend( cfmlFolders, folder ); arrayAppend( cljFolders, folder ); From b425bd2883247c30cb641d9420f6a4436d4b3e08 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 29 Dec 2015 12:46:32 -0800 Subject: [PATCH 049/100] Length error in ioclj.cfc --- framework/ioclj.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index 8d1e6b9a..43b7fe04 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -32,7 +32,7 @@ component extends=framework.ioc { for ( var folder in folders ) { if ( len( folder ) > 4 && left( folder, 4 ) == "clj:" ) { arrayAppend( cljFolders, right( folder, len( folder ) - 4 ) ); - } else if ( len( folder ) > 5 && left( folder, 4 ) == "cfml:" ) { + } else if ( len( folder ) > 5 && left( folder, 5 ) == "cfml:" ) { arrayAppend( cfmlFolders, right( folder, len( folder ) - 5 ) ); } else { arrayAppend( cfmlFolders, folder ); From 66242d49d770321d1d7711b4ddce1658444b0ab4 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sun, 3 Jan 2016 16:43:08 -0800 Subject: [PATCH 050/100] Delay bean discovery / load listener for most declarations #416 --- framework/ioc.cfc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index f65f0243..692b2678 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -74,7 +74,7 @@ component { // programmatically register an alias public any function addAlias( string aliasName, string beanName ) { - discoverBeans(); + discoverBeans(); // still need this since we rely on beanName having been discovered :( variables.beanInfo[ aliasName ] = variables.beanInfo[ beanName ]; return this; } @@ -82,7 +82,6 @@ component { // programmatically register new beans with the factory (add a singleton name/value pair) public any function addBean( string beanName, any beanValue ) { - discoverBeans(); variables.beanInfo[ beanName ] = { name = beanName, value = beanValue, isSingleton = true }; @@ -104,7 +103,6 @@ component { // programmatically register new beans with the factory (add an actual CFC) public any function declareBean( string beanName, string dottedPath, boolean isSingleton = true, struct overrides = { } ) { - discoverBeans(); var singleDir = ''; if ( listLen( dottedPath, '.' ) > 1 ) { var cfc = listLast( dottedPath, '.' ); @@ -128,7 +126,6 @@ component { } public any function factoryBean( string beanName, any factory, string methodName, array args = [ ], struct overrides = { } ) { - discoverBeans(); var metadata = { name = beanName, isSingleton = false, // really? factory = factory, method = methodName, args = args, From ff209823683f4b2a83691e57c589d88989ffbf36 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sun, 3 Jan 2016 18:35:27 -0800 Subject: [PATCH 051/100] Relax transient injection rule #417 This addresses the common cases in a different way, but may not be entirely desirable. --- framework/ioc.cfc | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 692b2678..c3fdafd0 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -520,18 +520,20 @@ component { } - private struct function findSetters( any cfc, struct iocMeta ) { + private struct function findSetters( any cfc, struct iocMeta, boolean isSingleton ) { var liveMeta = { setters = iocMeta.setters }; if ( !iocMeta.pruned ) { - // need to prune known setters of transients: - var prunable = { }; - for ( var known in iocMeta.setters ) { - if ( !isSingleton( known ) ) { - prunable[ known ] = true; + // for transients, we need to prune known setters of transients: + if ( !isSingleton ) { + var prunable = { }; + for ( var known in iocMeta.setters ) { + if ( !this.isSingleton( known ) ) { + prunable[ known ] = true; + } + } + for ( known in prunable ) { + structDelete( iocMeta.setters, known ); } - } - for ( known in prunable ) { - structDelete( iocMeta.setters, known ); } iocMeta.pruned = true; } @@ -541,8 +543,8 @@ component { var n = len( member ); if ( isCustomFunction( method ) && left( member, 3 ) == 'set' && n > 3 ) { var property = right( member, n - 3 ); - if ( !isSingleton( property ) ) { - // ignore properties that we know to be transients... + if ( !isSingleton && !this.isSingleton( property ) ) { + // for transients, ignore properties that we know to be transients... continue; } if ( !structKeyExists( liveMeta.setters, property ) ) { @@ -792,7 +794,7 @@ component { /*******************************************************/ if ( !structKeyExists( accumulator.injection, beanName ) ) { if ( !structKeyExists( variables.settersInfo, beanName ) ) { - variables.settersInfo[ beanName ] = findSetters( bean, info.metadata ); + variables.settersInfo[ beanName ] = findSetters( bean, info.metadata, info.isSingleton ); } var setterMeta = { setters = variables.settersInfo[ beanName ].setters, From 8a1cec9a2eeb83f058935cdb1701722816d5cb7f Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sun, 3 Jan 2016 18:46:48 -0800 Subject: [PATCH 052/100] ACF10 workaround This has been working just fine, but all of a sudden it stopped working. Sigh. Adobe... seriously? --- framework/ioc.cfc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index c3fdafd0..d5a71df6 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -689,7 +689,8 @@ component { } else { // allow for possible convention-based bean factory args[ property ] = missingBean( property, beanName ); - if ( isNull( args[ property ] ) ) continue; + // isNull() does not always work on ACF10... + try { if ( isNull( args[ property ] ) ) continue; } catch ( any e ) { continue; } } evaluate( 'injection.bean.set#property#( argumentCollection = args )' ); } From bafcf1b45cd685d3c24ee0d4111d01db9f34ce31 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 11 Jan 2016 16:23:02 -0800 Subject: [PATCH 053/100] `factoryBean()` accepts function/closure #418 --- framework/ioc.cfc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index d5a71df6..4dcb06be 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -125,7 +125,7 @@ component { return this; } - public any function factoryBean( string beanName, any factory, string methodName, array args = [ ], struct overrides = { } ) { + public any function factoryBean( string beanName, any factory, string methodName = "", array args = [ ], struct overrides = { } ) { var metadata = { name = beanName, isSingleton = false, // really? factory = factory, method = methodName, args = args, @@ -828,7 +828,13 @@ component { argStruct[ i ] = this.getBean( argName ); } } - accumulator.bean = evaluate( 'fmBean.#info.method#(argumentCollection=argStruct)' ); + if ( isCustomFunction( fmBean ) || + ( listFirst( server.coldfusion.productVersion ) >= 10 && + isClosure( fmBean ) ) ) { + accumulator.bean = fmBean( argumentCollection = argStruct ); + } else { + accumulator.bean = evaluate( 'fmBean.#info.method#( argumentCollection = argStruct )' ); + } accumulator.injection[ beanName ] = { bean = accumulator.bean, setters = { } }; } else { throw 'internal error: invalid metadata for #beanName#'; From 767d87b26dabf3650aea2c89f58f3ff242754f87 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 11 Jan 2016 16:27:47 -0800 Subject: [PATCH 054/100] Revert "Relax transient injection rule #417" This can break reasonable, existing code. Specifically it breaks World Singles code in very unpleasant ways that are not easy to workaround. This reverts commit ff209823683f4b2a83691e57c589d88989ffbf36. --- framework/ioc.cfc | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 4dcb06be..fe066708 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -520,21 +520,19 @@ component { } - private struct function findSetters( any cfc, struct iocMeta, boolean isSingleton ) { + private struct function findSetters( any cfc, struct iocMeta ) { var liveMeta = { setters = iocMeta.setters }; if ( !iocMeta.pruned ) { - // for transients, we need to prune known setters of transients: - if ( !isSingleton ) { - var prunable = { }; - for ( var known in iocMeta.setters ) { - if ( !this.isSingleton( known ) ) { - prunable[ known ] = true; - } - } - for ( known in prunable ) { - structDelete( iocMeta.setters, known ); + // need to prune known setters of transients: + var prunable = { }; + for ( var known in iocMeta.setters ) { + if ( !isSingleton( known ) ) { + prunable[ known ] = true; } } + for ( known in prunable ) { + structDelete( iocMeta.setters, known ); + } iocMeta.pruned = true; } // gather up explicit setters: @@ -543,8 +541,8 @@ component { var n = len( member ); if ( isCustomFunction( method ) && left( member, 3 ) == 'set' && n > 3 ) { var property = right( member, n - 3 ); - if ( !isSingleton && !this.isSingleton( property ) ) { - // for transients, ignore properties that we know to be transients... + if ( !isSingleton( property ) ) { + // ignore properties that we know to be transients... continue; } if ( !structKeyExists( liveMeta.setters, property ) ) { @@ -795,7 +793,7 @@ component { /*******************************************************/ if ( !structKeyExists( accumulator.injection, beanName ) ) { if ( !structKeyExists( variables.settersInfo, beanName ) ) { - variables.settersInfo[ beanName ] = findSetters( bean, info.metadata, info.isSingleton ); + variables.settersInfo[ beanName ] = findSetters( bean, info.metadata ); } var setterMeta = { setters = variables.settersInfo[ beanName ].setters, From 2d822510e7c99f06e4ee8f8d56fa74d356dc341e Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 11 Jan 2016 17:18:11 -0800 Subject: [PATCH 055/100] Declare bean builder syntax #417 Converted several tests to use new builder syntax to provide test coverage. Added factory function test. --- framework/ioc.cfc | 82 ++++++++++++++++++++++++++++++++++++--- tests/AddBeanTest.cfc | 2 +- tests/BeanInfoTest.cfc | 7 ++-- tests/DeclareBeanTest.cfc | 33 ++++++++++++---- tests/FactoryBeanTest.cfc | 20 ++++++++-- 5 files changed, 124 insertions(+), 20 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index fe066708..e024fa9f 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -2,7 +2,7 @@ component { variables._fw1_version = "4.0.0-snapshot"; variables._di1_version = "1.2.0-snapshot"; /* - Copyright (c) 2010-2015, Sean Corfield + Copyright (c) 2010-2016, Sean Corfield Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -96,10 +96,76 @@ component { ( hasParent() && variables.parent.containsBean( beanName ) ); } - // return true if this factory has a parent - public boolean function hasParent() { - return structKeyExists( variables, 'parent' ); - } + + // builder syntax for declaring new beans + public any function declare( string beanName ) { + var declaration = { beanName : beanName, built : false }; + var beanFactory = this; // to make the builder functions less confusing + structAppend( declaration, { + // builder for addAlias() + aliasFor : function( string beanName ) { + if ( declaration.built ) throw "Declaration builder already completed!"; + declaration.built = true; + beanFactory.addAlias( declaration.beanName, beanName ); + return declaration; + }, + // builder for addBean() + asValue : function( any beanValue ) { + if ( declaration.built ) throw "Declaration builder already completed!"; + declaration.built = true; + beanFactory.addBean( declaration.beanName, beanValue ); + return declaration; + }, + // builder for factoryBean() + fromFactory : function( any factory, string methodName = "" ) { + if ( declaration.built ) throw "Declaration builder already completed!"; + declaration.built = true; + // use defaults -- we can override later + beanFactory.factoryBean( declaration.beanName, factory, methodName ); + return declaration; + }, + // builder for declareBean() + instanceOf : function( string dottedPath ) { + if ( declaration.built ) throw "Declaration builder already completed!"; + declaration.built = true; + // use defaults -- we can override later + beanFactory.declareBean( declaration.beanName, dottedPath ); + return declaration; + }, + // modifiers for metadata + asSingleton : function() { + if ( !declaration.built ) throw "No declaration builder to modify!"; + variables.beanInfo[ declaration.beanName ].isSingleton = true; + return declaration; + }, + asTransient : function() { + if ( !declaration.built ) throw "No declaration builder to modify!"; + variables.beanInfo[ declaration.beanName ].isSingleton = false; + return declaration; + }, + withArguments : function( array args ) { + if ( !declaration.built ) throw "No declaration builder to modify!"; + var info = variables.beanInfo[ declaration.beanName ]; + if ( !structKeyExists( info, 'factory' ) ) throw "withArguments() requires fromFactory()!"; + info.args = args; + return declaration; + }, + withOverrides : function( struct overrides ) { + if ( !declaration.built ) throw "No declaration builder to modify!"; + var info = variables.beanInfo[ declaration.beanName ]; + if ( !structKeyExists( info, 'factory' ) && + !structKeyExists( info, 'cfc' ) ) throw "withOverrides() requires fromFactory() or instanceOf()!"; + info.overrides = overrides; + return declaration; + }, + // to allow chaining + done : function() { + return beanFactory; + } + } ); + return declaration; + } + // programmatically register new beans with the factory (add an actual CFC) public any function declareBean( string beanName, string dottedPath, boolean isSingleton = true, struct overrides = { } ) { @@ -209,6 +275,12 @@ component { } + // return true if this factory has a parent + public boolean function hasParent() { + return structKeyExists( variables, 'parent' ); + } + + // return true iff bean is known to be a singleton public boolean function isSingleton( string beanName ) { discoverBeans(); diff --git a/tests/AddBeanTest.cfc b/tests/AddBeanTest.cfc index 87aee3ed..9937d57c 100644 --- a/tests/AddBeanTest.cfc +++ b/tests/AddBeanTest.cfc @@ -3,7 +3,7 @@ component extends="mxunit.framework.TestCase" { function setup() { variables.added = new framework.ioc( "" ) - .addBean( "known", 42 ); + .declare( "known" ).asValue( 42 ).done(); } function shouldHaveKnownValue() { diff --git a/tests/BeanInfoTest.cfc b/tests/BeanInfoTest.cfc index 726a20e7..68b0b7c3 100644 --- a/tests/BeanInfoTest.cfc +++ b/tests/BeanInfoTest.cfc @@ -29,7 +29,7 @@ component extends="mxunit.framework.TestCase" { function shouldBeFlat() { var parent = new framework.ioc( "" ); - parent.addBean( "father", "figure" ); + parent.declare( "father" ).asValue( "figure" ); variables.factory.setParent( parent ); var info = variables.factory.getBeanInfo( flatten = true ); assertFalse( structKeyExists( info, "parent" ) ); @@ -38,8 +38,9 @@ component extends="mxunit.framework.TestCase" { } function shouldMatchRegex() { - variables.factory.addBean( "father", "figure" ); - variables.factory.addBean( "mother", "figure" ); + variables.factory + .declare( "father" ).asValue( "figure" ).done() + .addBean( "mother", "figure" ); var info = variables.factory.getBeanInfo( regex = "her$" ); assertEquals( 2, structCount( info.beaninfo ) ); assertTrue( structKeyExists( info.beaninfo, "father" ) ); diff --git a/tests/DeclareBeanTest.cfc b/tests/DeclareBeanTest.cfc index ce4fbe99..0f0df62c 100644 --- a/tests/DeclareBeanTest.cfc +++ b/tests/DeclareBeanTest.cfc @@ -1,7 +1,8 @@ component extends="mxunit.framework.TestCase" { function shouldDeclareSingleton() { - var bf = new framework.ioc( "" ).declareBean( "foo", "tests.extrabeans.sheep.item" ); + var bf = new framework.ioc( "" ) + .declare( "foo" ).instanceOf( "tests.extrabeans.sheep.item" ).done(); structDelete( application, "itemCount" ); var item1 = bf.getBean( "foo" ); assertEquals( 1, application.itemCount ); @@ -11,7 +12,11 @@ component extends="mxunit.framework.TestCase" { } function shouldDeclareTransient() { - var bf = new framework.ioc( "" ).declareBean( "foo", "tests.extrabeans.sheep.item", false ); + var bf = new framework.ioc( "" ) + .declare( "foo" ) + .instanceOf( "tests.extrabeans.sheep.item" ) + .asTransient() + .done(); structDelete( application, "itemCount" ); var item1 = bf.getBean( "foo" ); assertEquals( 1, application.itemCount ); @@ -21,7 +26,11 @@ component extends="mxunit.framework.TestCase" { } function shouldDeclareSingletonWithOverride() { - var bf = new framework.ioc( "" ).declareBean( "foo", "tests.extrabeans.sheep.item", true, { start = 100 } ); + var bf = new framework.ioc( "" ) + .declare( "foo" ) + .instanceOf( "tests.extrabeans.sheep.item" ) + .withOverrides( { start = 100 } ) + .done(); structDelete( application, "itemCount" ); var item1 = bf.getBean( "foo" ); assertEquals( 101, application.itemCount ); @@ -31,7 +40,12 @@ component extends="mxunit.framework.TestCase" { } function shouldDeclareTransientWithOverride() { - var bf = new framework.ioc( "" ).declareBean( "foo", "tests.extrabeans.sheep.item", false, { start = 100 } ); + var bf = new framework.ioc( "" ) + .declare( "foo" ) + .instanceOf( "tests.extrabeans.sheep.item" ) + .asTransient() + .withOverrides( { start = 100 } ) + .done(); structDelete( application, "itemCount" ); var item1 = bf.getBean( "foo" ); assertEquals( 101, application.itemCount ); @@ -41,15 +55,20 @@ component extends="mxunit.framework.TestCase" { } function shouldDeclareAndAdd() { - var bf = new framework.ioc( "", { omitTypedProperties = false } ).declareBean( "foo", "tests.declared.things.myconfig" ) - .addBean( "name", "test" ).addBean( "config", "some" ); + var bf = new framework.ioc( "", { omitTypedProperties = false } ) + .declare( "foo" ).instanceOf( "tests.declared.things.myconfig" ).done() + .declare( "name" ).asValue( "test" ).done() + .declare( "config" ).asValue( "some" ).done(); var item = bf.getBean( "foo" ); assertEquals( "test", item.getName() ); assertEquals( "some", item.getConfig() ); } function shouldDeclareWithOverride() { - var bf = new framework.ioc( "", { omitTypedProperties = false } ).declareBean( "foo", "tests.declared.things.myconfig", true, { name = "test", config = "some" } ) + var bf = new framework.ioc( "", { omitTypedProperties = false } ) + .declare( "foo" ) + .instanceOf( "tests.declared.things.myconfig" ) + .withOverrides( { name = "test", config = "some" } ).done() .addBean( "name", "not-test" ).addBean( "config", "config" ); var item = bf.getBean( "foo" ); assertEquals( "test", item.getName() ); diff --git a/tests/FactoryBeanTest.cfc b/tests/FactoryBeanTest.cfc index 945ee3bf..162ec4d6 100644 --- a/tests/FactoryBeanTest.cfc +++ b/tests/FactoryBeanTest.cfc @@ -2,7 +2,15 @@ component extends="mxunit.framework.TestCase" { function shouldSupportBasicFactoryMethod() { var bf = new framework.ioc( "/tests/model" ); - bf.factoryBean( "a", "factory", "makeMeAnA" ); + bf.declare( "a" ).fromFactory( "factory", "makeMeAnA" ); + assertEquals( "I am an A", bf.getBean( "a" ) ); + } + + function shouldSupportFactoryFunction() { + var bf = new framework.ioc( "/tests/model" ); + bf.declare( "a" ).fromFactory( function() { + return "I am an A"; + } ); assertEquals( "I am an A", bf.getBean( "a" ) ); } @@ -15,14 +23,18 @@ component extends="mxunit.framework.TestCase" { function shouldSupportFactoryMethodWithBeanArg() { var bf = new framework.ioc( "/tests/model" ); - bf.factoryBean( "a", "factory", "makeAWithFava", [ "favaBean" ] ); + bf.declare( "a" ) + .fromFactory( "factory", "makeAWithFava" ) + .withArguments( [ "favaBean" ] ); assertEquals( "I am a fava bean", bf.getBean( "a" ) ); } function shouldSupportFactoryMethodWithLocalArg() { var bf = new framework.ioc( "/tests/model" ); - bf.factoryBean( "a", "factory", "makeAWithFava", [ "favaBean" ], - { favaBean = { stamp = "different" } } ); + bf.declare( "a" ) + .fromFactory( "factory", "makeAWithFava" ) + .withArguments( [ "favaBean" ] ) + .withOverrides( { favaBean = { stamp = "different" } } ); assertEquals( "I am a different bean", bf.getBean( "a" ) ); } From a2451c13c4076fbdeebc8b08a9ff58b338aa27c7 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 11 Jan 2016 17:20:27 -0800 Subject: [PATCH 056/100] Remove test for ACF10 #417 Since we no longer support ACF9, we can assume isClosure() is present without needing a version test! --- framework/ioc.cfc | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index e024fa9f..61be6489 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -675,9 +675,7 @@ component { private void function onLoadEvent() { var head = variables.listeners; while ( isStruct( head ) ) { - if ( isCustomFunction( head.listener ) || - ( listFirst( server.coldfusion.productVersion ) >= 10 && - isClosure( head.listener ) ) ) { + if ( isCustomFunction( head.listener ) || isClosure( head.listener ) ) { head.listener( this ); } else if ( isObject( head.listener ) ) { head.listener.onLoad( this ); @@ -898,9 +896,7 @@ component { argStruct[ i ] = this.getBean( argName ); } } - if ( isCustomFunction( fmBean ) || - ( listFirst( server.coldfusion.productVersion ) >= 10 && - isClosure( fmBean ) ) ) { + if ( isCustomFunction( fmBean ) || isClosure( fmBean ) ) { accumulator.bean = fmBean( argumentCollection = argStruct ); } else { accumulator.bean = evaluate( 'fmBean.#info.method#( argumentCollection = argStruct )' ); From 8da13fab913bf9d09d99138daa592c3b46cb1a92 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 11 Jan 2016 17:31:48 -0800 Subject: [PATCH 057/100] Fix bug in #399 Constructor arguments accidentally overwrote metadata! --- framework/ioc.cfc | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 61be6489..e37cac0d 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -810,8 +810,18 @@ component { if ( structKeyExists( info, 'cfc' ) ) { /*******************************************************/ var metaBean = cachable( beanName ); - var overrides = structKeyExists( info, 'overrides' ) ? info.overrides : { }; - structAppend( overrides, constructorArgs ); + var overrides = { }; + // be careful not to modify overrides metadata: + if ( structCount( constructorArgs ) ) { + if ( structKeyExists( info, 'overrides' ) ) { + structAppend( overrides, info.overrides ); + } + structAppend( overrides, constructorArgs ); + } else { + if ( structKeyExists( info, 'overrides' ) ) { + overrides = info.overrides; + } + } bean = metaBean.bean; if ( metaBean.newObject ) { if ( structKeyExists( info.metadata, 'constructor' ) ) { From 6a57ebb4f1b71b856933fa4e9485e20339f72cf0 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 20 Jan 2016 17:06:11 -0800 Subject: [PATCH 058/100] Add getCGIRequestMethod() and a test #419 --- framework/one.cfc | 6 ++++++ tests/frameworkEnvTest.cfc | 4 ++++ 2 files changed, 10 insertions(+) diff --git a/framework/one.cfc b/framework/one.cfc index 6d253c72..3427a8bc 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -467,6 +467,12 @@ component { return listLast( getSectionAndItem( action ), '.' ); } + /* + * return this request's CGI method + */ + public string function getCGIRequestMethod() { + return request._fw1.cgiRequestMethod; + } /* * return the current route (if any) diff --git a/tests/frameworkEnvTest.cfc b/tests/frameworkEnvTest.cfc index 6bb90e5e..10059d6a 100644 --- a/tests/frameworkEnvTest.cfc +++ b/tests/frameworkEnvTest.cfc @@ -17,6 +17,10 @@ component extends="tests.InjectableTest" { }; } + public void function testRequestMethod() { + assertEquals( CGI.REQUEST_METHOD, variables.fw.getCGIRequestMethod() ); + } + public void function testGetEnvironmentIsCalled() { variables.fw.getEnvironment = recordCalls; variables.fwvars.getEnvironment = recordCalls; From bdc477cc48275a4505dd935dfd888371fe48c96d Mon Sep 17 00:00:00 2001 From: John Whish Date: Tue, 26 Jan 2016 22:02:56 +0000 Subject: [PATCH 059/100] Test for #420 Transients not properly autowired on subsequent get --- tests/TransientInjectionTest.cfc | 22 ++++++++++++++++++++++ tests/issue420/singleton.cfc | 8 ++++++++ tests/issue420/transient.cfc | 8 ++++++++ 3 files changed, 38 insertions(+) create mode 100644 tests/TransientInjectionTest.cfc create mode 100644 tests/issue420/singleton.cfc create mode 100644 tests/issue420/transient.cfc diff --git a/tests/TransientInjectionTest.cfc b/tests/TransientInjectionTest.cfc new file mode 100644 index 00000000..9a8d70c0 --- /dev/null +++ b/tests/TransientInjectionTest.cfc @@ -0,0 +1,22 @@ +component extends="mxunit.framework.TestCase" { + + function shouldReturnWiredTransient() { + // issue #420 + var bf = new framework.ioc( "" ); + bf.declareBean("transient", "tests.issue420.transient", false); + bf.declareBean("singleton", "tests.issue420.singleton", true); + + assertTrue( bf.containsBean( "transient" ) ); + assertFalse( bf.isSingleton( "transient" ) ); + + var singleton = bf.getBean( "transient" ).getSingleton(); + assertTrue( isValid( "component", singleton ), "should return the singleton instance on the 1st call" ); + assertTrue( isValid( "component", singleton.getBeanFactory() ), "should return ioc instance on the 1st call" ); + + // call again to check subsequent calls return wired transient + singleton = bf.getBean( "transient" ).getSingleton(); + assertTrue( isValid( "component", singleton ), "should return the singleton instance on the 2nd call" ); + assertTrue( isValid( "component", singleton.getBeanFactory() ), "should return ioc instance on the 2nd call" ); + } + +} diff --git a/tests/issue420/singleton.cfc b/tests/issue420/singleton.cfc new file mode 100644 index 00000000..72d3884a --- /dev/null +++ b/tests/issue420/singleton.cfc @@ -0,0 +1,8 @@ +component accessors="true" { + + property name="beanFactory"; + + function init() { + return this; + } +} diff --git a/tests/issue420/transient.cfc b/tests/issue420/transient.cfc new file mode 100644 index 00000000..3aea6dbf --- /dev/null +++ b/tests/issue420/transient.cfc @@ -0,0 +1,8 @@ +component accessors="true" { + + property name="singleton"; + + function init() { + return this; + } +} From 27f4334c24a970bc14d2373a2a1cf38d23ce8f46 Mon Sep 17 00:00:00 2001 From: John Whish Date: Tue, 26 Jan 2016 22:03:46 +0000 Subject: [PATCH 060/100] Fix #420 Add check for singleton --- framework/ioc.cfc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index e37cac0d..b86e8e26 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -891,6 +891,9 @@ component { } } accumulator.bean = bean; + if ( !isSingleton( beanName ) && structKeyExists( accumulator.injection, beanName ) ) { + accumulator.injection[ beanName ].bean = accumulator.bean; + } } else if ( isConstant( beanName ) ) { accumulator.bean = info.value; accumulator.injection[ beanName ] = { bean = info.value, setters = { } }; From e68f5ed702d80c59c57119d5e5247ab3e002b052 Mon Sep 17 00:00:00 2001 From: Mingo Hagen Date: Thu, 28 Jan 2016 00:50:18 +0100 Subject: [PATCH 061/100] Added routesCaseSensitive check to setupRequestDefaults() Route matching (in `routeRegexFind()`) has this check, but it was missing from the route replacement regex in `setupRequestDefaults()`. --- framework/one.cfc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/one.cfc b/framework/one.cfc index 3427a8bc..7bc3700c 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2699,7 +2699,11 @@ component { var routeMatch = processRoutes( pathInfo, routes ); if ( routeMatch.matched ) { internalFrameworkTrace( 'route matched - #routeMatch.route# - #pathInfo#' ); - pathInfo = rereplace( routeMatch.path, routeMatch.pattern, routeMatch.target ); + if ( variables.framework.routesCaseSensitive ) { + pathInfo = rereplace( routeMatch.path, routeMatch.pattern, routeMatch.target ); + } else { + pathInfo = rereplacenocase( routeMatch.path, routeMatch.pattern, routeMatch.target ); + } if ( routeMatch.redirect ) { location( pathInfo, false, routeMatch.statusCode ); } else { From 9a4f485693ce73edd2d70dbee38e6448ad0f8baa Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Thu, 28 Jan 2016 19:42:45 -0800 Subject: [PATCH 062/100] Fix bean info for Clojure integration --- framework/ioclj.cfc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index 43b7fe04..c45835ed 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -136,8 +136,13 @@ component extends=framework.ioc { } return super.getBeanInfo( beanName, flatten, regex ); } else { - var result = super.getBeanInfo( beanName, flatten, regex ); + var result = { beanInfo = { } }; + var superInfo = super.getBeanInfo( beanName, flatten, regex ); structAppend( result.beanInfo, variables.cljBeans ); + structAppend( result.beanInfo, superInfo.beanInfo ); + if ( structKeyExists( superInfo, 'parent' ) ) { + result.parent = superInfo.parent; + } if ( len( regex ) ) { var matched = { }; for ( var name in result.beanInfo ) { From 392ed8fdd40da17187ad8782102d0998e359c4c7 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 12 Feb 2016 19:00:16 -0800 Subject: [PATCH 063/100] Bug fixes for Clojure controllers Render data needs to preserve case so we need to be more careful about how we convert from Clojure RC data to CFML data that will be rendered. That means we also need clojure.walk available even when CFML interop library is available. --- framework/cfmljure.cfc | 5 ++--- framework/cljcontroller.cfc | 15 +++++++++------ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index afb5c408..d3af6901 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -2,7 +2,7 @@ component { variables._fw1_version = "4.0.0-snapshot"; variables._cfmljure_version = "1.1.0-snapshot"; /* - Copyright (c) 2012-2015, Sean Corfield + Copyright (c) 2012-2016, Sean Corfield Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -158,7 +158,7 @@ component { this.install = this.__install; this.isAvailable = this.__isAvailable; this.read = this.__read; - var autoLoaded = "clojure.core"; + var autoLoaded = "clojure.core,clojure.walk"; if ( cfmlInteropAvailable ) { variables.out.println( "Detected cfml-interop for interop" ); // perform the best interop we can: @@ -168,7 +168,6 @@ component { } else { variables.out.println( "Falling back to clojure.walk for interop" ); // fall back to basic interop: - autoLoaded = listAppend( autoLoaded, "clojure.walk" ); this.toCFML = this.__toCFML; this.toClojure = this.__toClojure; } diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index f36ce87b..771214e8 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -30,15 +30,14 @@ component { var rc = missingMethodArguments.rc; try { var rcClj = variables.cfmljure.toClojure( rc ); - var result = variables.cfmljure.toCFML( - evaluate( "variables.ns.#missingMethodName#( rcClj )" ) - ); + var rawResult = evaluate( "variables.ns.#missingMethodName#( rcClj )" ); + var result = variables.cfmljure.toCFML( rawResult ); structClear( rc ); structAppend( rc, result ); // post-process special keys in rc for abort / redirect etc var core = variables.cfmljure.clojure.core; if ( structKeyExists( rc, "redirect" ) && isStruct( rc.redirect ) && - structKeyExists( rc.redirect, "action" ) ) { + structKeyExists( rc.redirect, "action" ) ) { if ( isObject( variables.fw ) ) { variables.fw.redirect( action = rc.redirect["action"], @@ -52,11 +51,15 @@ component { } } if ( structKeyExists( rc, "render" ) && isStruct( rc.render ) && - structKeyExists( rc.render, "type" ) && structKeyExists( rc.render, "data" ) ) { + structKeyExists( rc.render, "type" ) && structKeyExists( rc.render, "data" ) ) { if ( isObject( variables.fw ) ) { + var core = variables.cfmljure.clojure.core; + var walk = variables.cfmljure.clojure.walk; variables.fw.renderData( core.name( rc.render["type"] ), - rc.render["data"], + // since Clojure generated the render data we must be careful to + // preserve case but still convert keys to strings... + walk.stringify_keys( core.get( core.get( rawResult, core.keyword( "render" ) ), core.keyword( "data" ) ) ), structKeyExists( rc.render, "statusCode" ) ? rc.render["statusCode"] : "200" ); } else { From 6cb044144e41c8cdb3a029a3bbe6fd7342ad513f Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 12 Feb 2016 19:01:48 -0800 Subject: [PATCH 064/100] Whitespace cleanup per Emacs --- framework/cljcontroller.cfc | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index 771214e8..56a6b25b 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -1,21 +1,21 @@ component { variables._fw1_version = "4.0.0-snapshot"; variables._ioclj_version = "1.0.1-snapshot"; -/* - Copyright (c) 2015, Sean Corfield + /* + Copyright (c) 2015-2016, Sean Corfield - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ function init( any fw, any cfmljure, any ns ) { variables.fw = fw; @@ -67,15 +67,15 @@ component { } } if ( structKeyExists( rc, "view" ) && isStruct( rc.view ) && - structKeyExists( rc.view, "action" ) ) { + structKeyExists( rc.view, "action" ) ) { if ( isObject( variables.fw ) ) { variables.fw.setView( rc.view["action"] ); } else { - throw "Unable to renderData() due to lack of injected FW/1"; + throw "Unable to setView() due to lack of injected FW/1"; } } if ( structKeyExists( rc, "abort" ) && core.keyword_qmark_( rc.abort ) && - core.name( rc.abort ) == "controller" ) { + core.name( rc.abort ) == "controller" ) { if ( isObject( variables.fw ) ) { variables.fw.abortController(); } else { From bbf8cd9f39236526401cb553718f719760021b2d Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 17 Feb 2016 10:45:34 -0800 Subject: [PATCH 065/100] Switch Clojure controller to new builder syntax for renderData() --- framework/cljcontroller.cfc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index 56a6b25b..90c1647f 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -55,13 +55,14 @@ component { if ( isObject( variables.fw ) ) { var core = variables.cfmljure.clojure.core; var walk = variables.cfmljure.clojure.walk; - variables.fw.renderData( + var renderer = variables.fw.renderData( core.name( rc.render["type"] ), // since Clojure generated the render data we must be careful to // preserve case but still convert keys to strings... - walk.stringify_keys( core.get( core.get( rawResult, core.keyword( "render" ) ), core.keyword( "data" ) ) ), - structKeyExists( rc.render, "statusCode" ) ? rc.render["statusCode"] : "200" + walk.stringify_keys( core.get( core.get( rawResult, core.keyword( "render" ) ), core.keyword( "data" ) ) ) ); + if ( structKeyExists( rc.render, "statusCode" ) ) renderer.statusCode( rc.render["statusCode"] ); + if ( structKeyExists( rc.render, "statusText" ) ) renderer.statusText( rc.render["statusText"] ); } else { throw "Unable to renderData() due to lack of injected FW/1"; } From 4ac2762b6d0fdc0916053979ce5e1c3c989a8a4c Mon Sep 17 00:00:00 2001 From: Tony Junkes Date: Fri, 19 Feb 2016 19:45:28 -0500 Subject: [PATCH 066/100] Update box.json regarding issue #425 --- box.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/box.json b/box.json index ecbc70e9..df1f869a 100644 --- a/box.json +++ b/box.json @@ -1,9 +1,9 @@ { "name" : "Framework One", "slug" : "fw1", - "version" : "3.5.0", + "version" : "4.0.0", "author" : "Sean Corfield, Marcin Szczepanski, Ryan Cogswell", - "location" : "https://github.com/framework-one/fw1/archive/v3.5.0.zip", + "location" : "https://github.com/framework-one/fw1/archive/develop.zip", "createPackageDirectory" : true, "packageDirectory" : "framework", "Homepage" : "http://framework-one.github.io/", From e90d4a4d5180ad476ef28e5c0a34fcf01fde74c3 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sat, 5 Mar 2016 16:15:21 -0800 Subject: [PATCH 067/100] Set versions to Alpha 1 --- framework/Application.cfc | 2 +- framework/MyApplication.cfc | 2 +- framework/WireBoxAdapter.cfc | 4 ++-- framework/aop.cfc | 6 +++--- framework/beanProxy.cfc | 6 +++--- framework/cfmljure.cfc | 4 ++-- framework/cljcontroller.cfc | 4 ++-- framework/ioc.cfc | 4 ++-- framework/ioclj.cfc | 6 +++--- framework/one.cfc | 4 ++-- 10 files changed, 21 insertions(+), 21 deletions(-) diff --git a/framework/Application.cfc b/framework/Application.cfc index 42720eff..8878c6ee 100644 --- a/framework/Application.cfc +++ b/framework/Application.cfc @@ -1,5 +1,5 @@ component { - // Version: FW/1 4.0.0 snapshot + // Version: FW/1 4.0.0 Alpha 1 // copy this to your application root to use as your Application.cfc // or incorporate the logic below into your existing Application.cfc diff --git a/framework/MyApplication.cfc b/framework/MyApplication.cfc index d2c9078f..edb57e1e 100644 --- a/framework/MyApplication.cfc +++ b/framework/MyApplication.cfc @@ -1,5 +1,5 @@ component extends="framework.one" { - // Version: FW/1 4.0.0 snapshot + // Version: FW/1 4.0.0 Alpha 1 // if you need to provide extension points, copy this to // your web root, next to your Application.cfc, and add diff --git a/framework/WireBoxAdapter.cfc b/framework/WireBoxAdapter.cfc index 69c983ac..ea8a15d5 100644 --- a/framework/WireBoxAdapter.cfc +++ b/framework/WireBoxAdapter.cfc @@ -1,7 +1,7 @@ component extends="wirebox.system.ioc.Injector" { - variables._fw1_version = "4.0.0-snapshot"; + variables._fw1_version = "4.0.0-alpha1"; /* - Copyright (c) 2010-2015, Sean Corfield + Copyright (c) 2010-2016, Sean Corfield Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/framework/aop.cfc b/framework/aop.cfc index b642ef9e..41c42936 100755 --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -1,8 +1,8 @@ component extends="framework.ioc" { - variables._fw1_version = "4.0.0-snapshot"; - variables._aop1_version = "2.0.2-snapshot"; + variables._fw1_version = "4.0.0-alpha1"; + variables._aop1_version = "2.0.2-alpha1"; /* - Copyright (c) 2013-2015, Mark Drew, Sean Corfield, Daniel Budde + Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/framework/beanProxy.cfc b/framework/beanProxy.cfc index c35a9143..10f3bce8 100755 --- a/framework/beanProxy.cfc +++ b/framework/beanProxy.cfc @@ -1,8 +1,8 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._aop1_version = "2.0.2-snapshot"; + variables._fw1_version = "4.0.0-alpha1"; + variables._aop1_version = "2.0.2-alpha1"; /* - Copyright (c) 2013-2015, Mark Drew, Sean Corfield, Daniel Budde + Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index d3af6901..5695b102 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._cfmljure_version = "1.1.0-snapshot"; + variables._fw1_version = "4.0.0-alpha1"; + variables._cfmljure_version = "1.1.0-alpha1"; /* Copyright (c) 2012-2016, Sean Corfield diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index 90c1647f..c3dcf8e9 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._ioclj_version = "1.0.1-snapshot"; + variables._fw1_version = "4.0.0-alpha1"; + variables._ioclj_version = "1.0.1-alpha1"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/ioc.cfc b/framework/ioc.cfc index b86e8e26..0264211b 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._di1_version = "1.2.0-snapshot"; + variables._fw1_version = "4.0.0-alpha1"; + variables._di1_version = "1.2.0-alpha1"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index c45835ed..7c6c0198 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -1,8 +1,8 @@ component extends=framework.ioc { - variables._fw1_version = "4.0.0-snapshot"; - variables._ioclj_version = "1.1.0-snapshot"; + variables._fw1_version = "4.0.0-alpha1"; + variables._ioclj_version = "1.1.0-alpha1"; /* - Copyright (c) 2015, Sean Corfield + Copyright (c) 2015-2016, Sean Corfield Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/framework/one.cfc b/framework/one.cfc index 7bc3700c..6d327d41 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1,7 +1,7 @@ component { - variables._fw1_version = "4.0.0-snapshot"; + variables._fw1_version = "4.0.0-alpha1"; /* - Copyright (c) 2009-2015, Sean Corfield, Marcin Szczepanski, Ryan Cogswell + Copyright (c) 2009-2016, Sean Corfield, Marcin Szczepanski, Ryan Cogswell Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From bd8ca8543163c5a9087665995061173a27f24edd Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Sat, 5 Mar 2016 16:28:00 -0800 Subject: [PATCH 068/100] It's 2016! --- LICENSE | 2 +- README.md | 2 +- examples/layouts/default.cfm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/LICENSE b/LICENSE index a6f29c8d..210b6d6f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009-2015 Sean Corfield (see individual files for any +Copyright (c) 2009-2016 Sean Corfield (see individual files for any additional copyright holders) Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/README.md b/README.md index cebd495b..ec9209d7 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ See the `run-tests-example.sh` file for a template (for Mac/Linux). # Copyright and License -Copyright (c) 2009-2015 Sean Corfield (and others -- see individual files for additional copyright holders). All rights reserved. +Copyright (c) 2009-2016 Sean Corfield (and others -- see individual files for additional copyright holders). All rights reserved. The use and distribution terms for this software are covered by the Apache Software License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) which can also be found in the file LICENSE at the root of this distribution and in individual licensed files. By using this software in any fashion, you are agreeing to be bound by the terms of this license. You must not remove this notice, or any other, from this software. diff --git a/examples/layouts/default.cfm b/examples/layouts/default.cfm index c3658ca3..4ca572f7 100644 --- a/examples/layouts/default.cfm +++ b/examples/layouts/default.cfm @@ -12,7 +12,7 @@

Examples Home

From d284ba5902b65499ba996739fbd47732bc0fd2df Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 21 Mar 2016 18:46:17 -0700 Subject: [PATCH 069/100] Add __name() for debugging. --- framework/cfmljure.cfc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index d3af6901..b2938dbe 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -369,6 +369,10 @@ component { } } + public string function __name() { + return variables._clj_ns; + } + public void function __require( string ns ) { if ( !structKeyExists( variables, "_clj_require" ) ) { variables._clj_require = __var( "clojure.core", "require" ); From f8ddd9738b2dcfa06629810e8d587e3f3ec9b62a Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 21 Mar 2016 18:47:23 -0700 Subject: [PATCH 070/100] Allow before()/after() #427 Allow any call with `rc` as an argument. When a call does not resolve, add a dummy (CFML) method to the controller shim so future calls are not routed through onMissingMethod(). --- framework/cljcontroller.cfc | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index 90c1647f..9bf9089b 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -25,8 +25,7 @@ component { } function onMissingMethod( string missingMethodName, struct missingMethodArguments ) { - if ( structKeyExists( missingMethodArguments, "method" ) && - missingMethodArguments.method == "item" ) { + if ( structKeyExists( missingMethodArguments, "rc" ) ) { var rc = missingMethodArguments.rc; try { var rcClj = variables.cfmljure.toClojure( rc ); @@ -86,6 +85,7 @@ component { } catch ( java.lang.IllegalStateException e ) { if ( e.message.startsWith( "Attempting to call unbound fn" ) ) { // no such controller method - ignore it + this[ missingMethodName ] = __dummy; } else { throw e; } @@ -93,4 +93,8 @@ component { } } + function setFramework() { } + + function __dummy() { } + } From 8f8dcdbf77e19651af8e1e5e437de33c31635874 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 21 Mar 2016 18:49:32 -0700 Subject: [PATCH 071/100] Hoist namespace -> bean logic #427 This allows for both `containsBean()` and `getBean()` overrides to be removed and the parent DI/1 methods to be relied on. This also ensures that controller shims are created only once and that, even in the event of direct calls to `getBean()`, no additional metadata operations are performed. --- framework/ioclj.cfc | 56 ++++++++++++++++----------------------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index c45835ed..001b07ca 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -2,7 +2,7 @@ component extends=framework.ioc { variables._fw1_version = "4.0.0-snapshot"; variables._ioclj_version = "1.1.0-snapshot"; /* - Copyright (c) 2015, Sean Corfield + Copyright (c) 2015-2016, Sean Corfield Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -80,8 +80,26 @@ component extends=framework.ioc { // patch DI/1 bean info to include Clojure "beans" -- this allows Clojure // to be autowired like any other "value" bean: for ( var cljBean in variables.cljBeans ) { + var info = variables.cljBeans[ cljBean ]; + // navigate to actual namespace "object": + var ns = variables.clojureApp; + for ( var x in info.nsx ) { + ns = ns[ x ]; + } + var bean = ns; + if ( info.type == "controller" ) { + // need a wrapper - try to find FW/1 instance via bean factory: + var fw = containsBean( "fw" ) ? getBean( "fw" ) : + ( containsBean( "fw1" ) ? getBean( "fw1" ) : + ( containsBean( "framework" ) ? getBean( "framework" ) : + "" ) ); + var controller = new framework.cljcontroller( + fw, variables.cfmljure, ns + ); + bean = controller; + } variables.beanInfo[ cljBean ] = { - name : cljBean, value : getBean( cljBean ), isSingleton : true + name : cljBean, value : bean, isSingleton : true }; } // add cfmljure to expose Clojure-related functions: @@ -92,40 +110,6 @@ component extends=framework.ioc { // PUBLIC METHODS - // return true if the factory (or a parent factory) knows about the requested bean - public boolean function containsBean( string beanName ) { - return structKeyExists( variables.cljBeans, beanName ) || super.containsBean( beanName ); - } - - // return the requested bean, fully populated - public any function getBean( string beanName ) { - if ( structKeyExists( variables.cljBeans, beanName ) ) { - var info = variables.cljBeans[ beanName ]; - // navigate to actual namespace "object": - var ns = variables.clojureApp; - for ( var x in info.nsx ) { - ns = ns[ x ]; - } - if ( info.type == "controller" ) { - // need a wrapper - try to find FW/1 instance via bean factory: - var fw = super.containsBean( "fw" ) ? super.getBean( "fw" ) : - ( super.containsBean( "fw1" ) ? super.getBean( "fw1" ) : - ( super.containsBean( "framework" ) ? super.getBean( "framework" ) : - "" ) ); - var controller = new framework.cljcontroller( - fw, variables.cfmljure, ns - ); - return controller; - } else { - // expose as a regular bean - return ns; - } - } else { - return super.getBean( beanName ); - } - } - - // convenience API for metaprogramming perhaps? public any function getBeanInfo( string beanName = '', boolean flatten = false, string regex = '' ) { From b3c0001c8e944feea147ad3a7703da3ea4a9f233 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 21 Mar 2016 19:02:20 -0700 Subject: [PATCH 072/100] Back to post-Alpha 1 snapshot status. --- framework/Application.cfc | 2 +- framework/MyApplication.cfc | 2 +- framework/WireBoxAdapter.cfc | 2 +- framework/aop.cfc | 4 ++-- framework/beanProxy.cfc | 4 ++-- framework/cfmljure.cfc | 4 ++-- framework/cljcontroller.cfc | 4 ++-- framework/ioc.cfc | 4 ++-- framework/ioclj.cfc | 4 ++-- framework/one.cfc | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/framework/Application.cfc b/framework/Application.cfc index 8878c6ee..42720eff 100644 --- a/framework/Application.cfc +++ b/framework/Application.cfc @@ -1,5 +1,5 @@ component { - // Version: FW/1 4.0.0 Alpha 1 + // Version: FW/1 4.0.0 snapshot // copy this to your application root to use as your Application.cfc // or incorporate the logic below into your existing Application.cfc diff --git a/framework/MyApplication.cfc b/framework/MyApplication.cfc index edb57e1e..d2c9078f 100644 --- a/framework/MyApplication.cfc +++ b/framework/MyApplication.cfc @@ -1,5 +1,5 @@ component extends="framework.one" { - // Version: FW/1 4.0.0 Alpha 1 + // Version: FW/1 4.0.0 snapshot // if you need to provide extension points, copy this to // your web root, next to your Application.cfc, and add diff --git a/framework/WireBoxAdapter.cfc b/framework/WireBoxAdapter.cfc index ea8a15d5..b472284f 100644 --- a/framework/WireBoxAdapter.cfc +++ b/framework/WireBoxAdapter.cfc @@ -1,5 +1,5 @@ component extends="wirebox.system.ioc.Injector" { - variables._fw1_version = "4.0.0-alpha1"; + variables._fw1_version = "4.0.0-snapshot"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/aop.cfc b/framework/aop.cfc index 41c42936..47ccb927 100755 --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -1,6 +1,6 @@ component extends="framework.ioc" { - variables._fw1_version = "4.0.0-alpha1"; - variables._aop1_version = "2.0.2-alpha1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._aop1_version = "2.0.2-snapshot"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/beanProxy.cfc b/framework/beanProxy.cfc index 10f3bce8..9f4aa62a 100755 --- a/framework/beanProxy.cfc +++ b/framework/beanProxy.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-alpha1"; - variables._aop1_version = "2.0.2-alpha1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._aop1_version = "2.0.2-snapshot"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index 018c68b6..c740a10a 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-alpha1"; - variables._cfmljure_version = "1.1.0-alpha1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._cfmljure_version = "1.1.0-snapshot"; /* Copyright (c) 2012-2016, Sean Corfield diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index ce61e207..9243d033 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-alpha1"; - variables._ioclj_version = "1.0.1-alpha1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._ioclj_version = "1.0.1-snapshot"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 0264211b..b86e8e26 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-alpha1"; - variables._di1_version = "1.2.0-alpha1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._di1_version = "1.2.0-snapshot"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index 3c081a29..c5ed7b98 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -1,6 +1,6 @@ component extends=framework.ioc { - variables._fw1_version = "4.0.0-alpha1"; - variables._ioclj_version = "1.1.0-alpha1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._ioclj_version = "1.1.0-snapshot"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/one.cfc b/framework/one.cfc index 6d327d41..6680ddb5 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-alpha1"; + variables._fw1_version = "4.0.0-snapshot"; /* Copyright (c) 2009-2016, Sean Corfield, Marcin Szczepanski, Ryan Cogswell From 26dc0592bf43890c205f095e2b3d437ce6a29f6d Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 1 Apr 2016 13:54:52 -0700 Subject: [PATCH 073/100] Fix #429 by removing expandPath() calls --- framework/one.cfc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index 6680ddb5..162b94b6 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1946,11 +1946,11 @@ component { pathInfo.base = pathInfo.base & getSubsystemDirPrefix( subsystem ); } var defaultPath = pathInfo.base & folder & 's/' & pathInfo.path & '.cfm'; - if ( !cachedFileExists( expandPath( defaultPath ) ) ) + if ( !cachedFileExists( defaultPath ) ) defaultPath = pathInfo.base & folder & 's/' & pathInfo.path & '.lucee'; - if ( !cachedFileExists( expandPath( defaultPath ) ) ) + if ( !cachedFileExists( defaultPath ) ) defaultPath = pathInfo.base & folder & 's/' & pathInfo.path & '.lc'; - if ( !cachedFileExists( expandPath( defaultPath ) ) ) + if ( !cachedFileExists( defaultPath ) ) // can't find it so assume .cfm default value defaultPath = pathInfo.base & folder & 's/' & pathInfo.path & '.cfm'; return customizeViewOrLayoutPath( pathInfo, type, defaultPath ); From 97e11e643f2ad4540dc966fdb29272813ac38828 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 19 Apr 2016 21:23:44 -0700 Subject: [PATCH 074/100] Proof of concept to show how Clojure can call back into CFML! The global `before()` function stores the PageContext and the bean factory into RC so they are available in Clojure controllers. The `main` controller contains `call-method` which accepts RC, a CFC instance, a method name, and whatever arguments you want to pass to CFML, and it uses the PageContext in the RC to make the call. Then it has `get-bean` which uses `call-method` to call `getBean()` on the bean factory. --- examples/subsystems/6helloclojure/MyApplication.cfc | 4 ++++ examples/subsystems/6helloclojure/Test.cfc | 5 +++++ .../6helloclojure/src/hello/controllers/main.clj | 12 +++++++++++- .../subsystems/6helloclojure/views/main/default.cfm | 1 + 4 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 examples/subsystems/6helloclojure/Test.cfc diff --git a/examples/subsystems/6helloclojure/MyApplication.cfc b/examples/subsystems/6helloclojure/MyApplication.cfc index eb99463f..da088b9c 100644 --- a/examples/subsystems/6helloclojure/MyApplication.cfc +++ b/examples/subsystems/6helloclojure/MyApplication.cfc @@ -1,4 +1,8 @@ component extends=framework.one { + function before( rc ) { + rc["pc"] = getPageContext(); + rc["ioc"] = getBeanFactory(); + } function setupRequest() { // reload Clojure when FW/1 is reloaded: if ( isFrameworkReloadRequest() ) { diff --git a/examples/subsystems/6helloclojure/Test.cfc b/examples/subsystems/6helloclojure/Test.cfc new file mode 100644 index 00000000..55c28673 --- /dev/null +++ b/examples/subsystems/6helloclojure/Test.cfc @@ -0,0 +1,5 @@ +component { + function greet( name ) { + return "Hello #name#!"; + } +} diff --git a/examples/subsystems/6helloclojure/src/hello/controllers/main.clj b/examples/subsystems/6helloclojure/src/hello/controllers/main.clj index d92ebbe1..1b435d1a 100644 --- a/examples/subsystems/6helloclojure/src/hello/controllers/main.clj +++ b/examples/subsystems/6helloclojure/src/hello/controllers/main.clj @@ -1,8 +1,18 @@ (ns hello.controllers.main (:require [hello.services.greeter :as greet])) +(defn call-method [rc cfc method & args] + (let [pc (:pc rc)] + (.call cfc pc method (into-array Object args)))) + +(defn get-bean [rc bean-name] + (let [ioc (:ioc rc)] + (call-method rc ioc "getBean" bean-name))) + (defn default [rc] - (assoc rc :greeting (greet/hello (:name rc "anonymous")))) + (-> rc + (assoc :greeting (greet/hello (:name rc "anonymous"))) + (assoc :cfmlgreeting (call-method rc (get-bean rc "test") "greet" "CFML")))) (defn do-redirect [rc] (assoc rc :redirect {:action "main.default" :queryString "name=Mr. Redirect"})) diff --git a/examples/subsystems/6helloclojure/views/main/default.cfm b/examples/subsystems/6helloclojure/views/main/default.cfm index d1da5a44..6eb106cd 100644 --- a/examples/subsystems/6helloclojure/views/main/default.cfm +++ b/examples/subsystems/6helloclojure/views/main/default.cfm @@ -2,6 +2,7 @@

Hello!

Clojure produced the greeting "#rc.greeting#"

+

Clojure called CFML to produce "#rc.cfmlgreeting#"

We can also get a greeterService in Clojure and use that: #getBeanFactory().getBean("greeterService").hello("Earthling")#

Display this page with a value specified for 'name'.

From 0ee69a42f90c2eb78c72f4661e98e2fe9fcf6a79 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 27 Apr 2016 13:25:39 -0700 Subject: [PATCH 075/100] Add defaulted property tests Based on test case from Jonathan Price / Puritan Paul on the mailing list. --- tests/defaultPropertyTest.cfc | 25 +++++++++++++++++++++++++ tests/extrabeans/sheep/default.cfc | 6 ++++++ 2 files changed, 31 insertions(+) create mode 100644 tests/defaultPropertyTest.cfc create mode 100644 tests/extrabeans/sheep/default.cfc diff --git a/tests/defaultPropertyTest.cfc b/tests/defaultPropertyTest.cfc new file mode 100644 index 00000000..f05e0369 --- /dev/null +++ b/tests/defaultPropertyTest.cfc @@ -0,0 +1,25 @@ +component extends=mxunit.framework.TestCase { + + function setup() { + variables.bf = new framework.ioc( "" ) + .declare( "default" ).instanceOf( "tests.extrabeans.sheep.default" ) + .asTransient() + .done(); + } + + function shouldHaveDefaultValue() { + var data = { + viaNew : new tests.extrabeans.sheep.default(), + viaDI1 : variables.bf.getBean( "default" ) + }; + assertTrue( isNull( data.viaNew.getSimple() ) ); + assertTrue( isNull( data.viaNew.getTyped() ) ); + assertEquals( "Default Value", data.viaNew.getDefaulted() ); + assertEquals( "Default Type Value", data.viaNew.getDefaultedType() ); + assertTrue( isNull( data.viaDI1.getSimple() ) ); + assertTrue( isNull( data.viaDI1.getTyped() ) ); + assertEquals( "Default Value", data.viaDI1.getDefaulted() ); + assertEquals( "Default Type Value", data.viaDI1.getDefaultedType() ); + } + +} diff --git a/tests/extrabeans/sheep/default.cfc b/tests/extrabeans/sheep/default.cfc new file mode 100644 index 00000000..92e40e02 --- /dev/null +++ b/tests/extrabeans/sheep/default.cfc @@ -0,0 +1,6 @@ +component accessors=true { + property simple; + property name="typed" type="string"; + property name="defaulted" default="Default Value"; + property name="defaultedType" type="string" default="Default Type Value"; +} From 095c59eea7acbbd53f443e84f1bb05a4445ab41c Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 11 May 2016 11:36:45 -0700 Subject: [PATCH 076/100] Update README.md Cleanup badges / fix typos --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index ec9209d7..140cf8b0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,4 @@ -Travis CI: [![Build Status](https://travis-ci.org/framework-one/fw1.png)](https://travis-ci.org/framework-one/fw1) -- -Waffle.IO: [![Stories in Ready](https://badge.waffle.io/framework-one/fw1.png?label=ready&title=Ready)](http://waffle.io/framework-one/fw1) - -# Overview +# FW/1 (Framework One) [![Build Status](https://travis-ci.org/framework-one/fw1.png)](https://travis-ci.org/framework-one/fw1) [![Stories in Ready](https://badge.waffle.io/framework-one/fw1.png?label=ready&title=Ready)](http://waffle.io/framework-one/fw1) [![Build Status](https://travis-ci.org/framework-one/fw1.png)](https://travis-ci.org/framework-one/fw1) [![Join the chat at https://gitter.im/framework-one/fw1](https://badges.gitter.im/framework-one/fw1.svg)](https://gitter.im/framework-one/fw1?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) This FW/1 directory is a complete web application and expects to live in its own webroot if you plan to run the applications within it. To use FW/1 in a separate From 4315b6c61a37b9f1a339b0836f2fc2df19fff66b Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 11 May 2016 11:37:27 -0700 Subject: [PATCH 077/100] Update README.md Remove duplicate Travis CI badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 140cf8b0..5759a3d1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# FW/1 (Framework One) [![Build Status](https://travis-ci.org/framework-one/fw1.png)](https://travis-ci.org/framework-one/fw1) [![Stories in Ready](https://badge.waffle.io/framework-one/fw1.png?label=ready&title=Ready)](http://waffle.io/framework-one/fw1) [![Build Status](https://travis-ci.org/framework-one/fw1.png)](https://travis-ci.org/framework-one/fw1) [![Join the chat at https://gitter.im/framework-one/fw1](https://badges.gitter.im/framework-one/fw1.svg)](https://gitter.im/framework-one/fw1?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +# FW/1 (Framework One) [![Build Status](https://travis-ci.org/framework-one/fw1.png)](https://travis-ci.org/framework-one/fw1) [![Stories in Ready](https://badge.waffle.io/framework-one/fw1.png?label=ready&title=Ready)](http://waffle.io/framework-one/fw1) [![Join the chat at https://gitter.im/framework-one/fw1](https://badges.gitter.im/framework-one/fw1.svg)](https://gitter.im/framework-one/fw1?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) This FW/1 directory is a complete web application and expects to live in its own webroot if you plan to run the applications within it. To use FW/1 in a separate From bcaa43d2ffec899daba0257dbdac9a0028231989 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 20 May 2016 20:21:21 -0700 Subject: [PATCH 078/100] Fix #434 by adding `getRoutePath()` This turns out not to address the use case I thought I needed (which the existing `getRoute()` better serves) but it provides a useful value when you are reporting what actual path info was used for the route / action. --- framework/one.cfc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/framework/one.cfc b/framework/one.cfc index 162b94b6..59a87473 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -476,11 +476,19 @@ component { /* * return the current route (if any) + * this is the raw, matched route that we mapped */ public string function getRoute() { return structKeyExists( request._fw1, 'route' ) ? request._fw1.route : ''; } + /* + * return the part of the pathinfo that was used as the route + * prefixed by the HTTP method + */ + public string function getRoutePath() { + return '$' & request._fw1.cgiRequestMethod & request._fw1.currentRoute; + } /* * return the configured routes @@ -2693,17 +2701,22 @@ component { // pathInfo is bogus so ignore it: pathInfo = ''; } + request._fw1.currentRoute = ''; var routes = getRoutes(); if ( arrayLen( routes ) ) { internalFrameworkTrace( 'processRoutes() called' ); var routeMatch = processRoutes( pathInfo, routes ); if ( routeMatch.matched ) { internalFrameworkTrace( 'route matched - #routeMatch.route# - #pathInfo#' ); + var routeTail = ''; if ( variables.framework.routesCaseSensitive ) { pathInfo = rereplace( routeMatch.path, routeMatch.pattern, routeMatch.target ); + routeTail = rereplace( routeMatch.path, routeMatch.pattern, '' ); } else { pathInfo = rereplacenocase( routeMatch.path, routeMatch.pattern, routeMatch.target ); + routeTail = rereplacenocase( routeMatch.path, routeMatch.pattern, '' ); } + request._fw1.currentRoute = left( routeMatch.path, len( routeMatch.path ) - len( routeTail ) ); if ( routeMatch.redirect ) { location( pathInfo, false, routeMatch.statusCode ); } else { @@ -2731,6 +2744,13 @@ component { pathInfo = listToArray( pathInfo, '/' ); } var sesN = arrayLen( pathInfo ); + if ( !len( request._fw1.currentRoute ) ) { + switch ( sesN ) { + case 0 : request._fw1.currentRoute = '/'; break; + case 1 : request._fw1.currentRoute = '/' & pathInfo[1] & '/'; break; + default: request._fw1.currentRoute = '/' & pathInfo[1] & '/' & pathInfo[2] & '/'; break; + } + } if ( ( sesN > 0 || variables.framework.generateSES ) && getBaseURL() != 'useRequestURI' ) { request._fw1.generateSES = true; } From f32e781e0d8b6a3d2b5c259b38e2b4d7b45e5a1d Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 27 May 2016 15:53:05 -0700 Subject: [PATCH 079/100] Revert "Proof of concept to show how Clojure can call back into CFML!" This reverts commit 97e11e643f2ad4540dc966fdb29272813ac38828. --- examples/subsystems/6helloclojure/MyApplication.cfc | 4 ---- examples/subsystems/6helloclojure/Test.cfc | 5 ----- .../6helloclojure/src/hello/controllers/main.clj | 12 +----------- .../subsystems/6helloclojure/views/main/default.cfm | 1 - 4 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 examples/subsystems/6helloclojure/Test.cfc diff --git a/examples/subsystems/6helloclojure/MyApplication.cfc b/examples/subsystems/6helloclojure/MyApplication.cfc index da088b9c..eb99463f 100644 --- a/examples/subsystems/6helloclojure/MyApplication.cfc +++ b/examples/subsystems/6helloclojure/MyApplication.cfc @@ -1,8 +1,4 @@ component extends=framework.one { - function before( rc ) { - rc["pc"] = getPageContext(); - rc["ioc"] = getBeanFactory(); - } function setupRequest() { // reload Clojure when FW/1 is reloaded: if ( isFrameworkReloadRequest() ) { diff --git a/examples/subsystems/6helloclojure/Test.cfc b/examples/subsystems/6helloclojure/Test.cfc deleted file mode 100644 index 55c28673..00000000 --- a/examples/subsystems/6helloclojure/Test.cfc +++ /dev/null @@ -1,5 +0,0 @@ -component { - function greet( name ) { - return "Hello #name#!"; - } -} diff --git a/examples/subsystems/6helloclojure/src/hello/controllers/main.clj b/examples/subsystems/6helloclojure/src/hello/controllers/main.clj index 1b435d1a..d92ebbe1 100644 --- a/examples/subsystems/6helloclojure/src/hello/controllers/main.clj +++ b/examples/subsystems/6helloclojure/src/hello/controllers/main.clj @@ -1,18 +1,8 @@ (ns hello.controllers.main (:require [hello.services.greeter :as greet])) -(defn call-method [rc cfc method & args] - (let [pc (:pc rc)] - (.call cfc pc method (into-array Object args)))) - -(defn get-bean [rc bean-name] - (let [ioc (:ioc rc)] - (call-method rc ioc "getBean" bean-name))) - (defn default [rc] - (-> rc - (assoc :greeting (greet/hello (:name rc "anonymous"))) - (assoc :cfmlgreeting (call-method rc (get-bean rc "test") "greet" "CFML")))) + (assoc rc :greeting (greet/hello (:name rc "anonymous")))) (defn do-redirect [rc] (assoc rc :redirect {:action "main.default" :queryString "name=Mr. Redirect"})) diff --git a/examples/subsystems/6helloclojure/views/main/default.cfm b/examples/subsystems/6helloclojure/views/main/default.cfm index 6eb106cd..d1da5a44 100644 --- a/examples/subsystems/6helloclojure/views/main/default.cfm +++ b/examples/subsystems/6helloclojure/views/main/default.cfm @@ -2,7 +2,6 @@

Hello!

Clojure produced the greeting "#rc.greeting#"

-

Clojure called CFML to produce "#rc.cfmlgreeting#"

We can also get a greeterService in Clojure and use that: #getBeanFactory().getBean("greeterService").hello("Earthling")#

Display this page with a value specified for 'name'.

From 6f43a8d017357bdf4ff36ae74052bc3d119d8e3a Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 1 Jun 2016 13:46:22 -0700 Subject: [PATCH 080/100] Implement FW/1 facade #439 --- framework/facade.cfc | 30 ++++++++++++++++++++++++++++++ framework/one.cfc | 1 + tests/frameworkFacadeTest.cfc | 25 +++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 framework/facade.cfc create mode 100644 tests/frameworkFacadeTest.cfc diff --git a/framework/facade.cfc b/framework/facade.cfc new file mode 100644 index 00000000..b895ac81 --- /dev/null +++ b/framework/facade.cfc @@ -0,0 +1,30 @@ +component { + variables._fw1_version = "4.0.0-snapshot"; +/* + Copyright (c) 2016, Sean Corfield + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + + function init() { + try { + return request._fw1.theFramework; + } catch ( any e ) { + throw( + type = "FW1.FacadeException", message = "Unable to locate FW/1 for this request", + detail = "It appears that you asked for the facade in a request that did not originate in FW/1?" + ); + } + } + +} diff --git a/framework/one.cfc b/framework/one.cfc index 59a87473..c4c51411 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2814,6 +2814,7 @@ component { request.subsystembase = request.base & getSubsystemDirPrefix( request.subsystem ); request.section = getSection( request.action ); request.item = getItem( request.action ); + request._fw1.theFramework = this; // for use in the facade (only!) if ( runSetup ) { controller( variables.magicApplicationSubsystem & variables.framework.subsystemDelimiter & diff --git a/tests/frameworkFacadeTest.cfc b/tests/frameworkFacadeTest.cfc new file mode 100644 index 00000000..954a9828 --- /dev/null +++ b/tests/frameworkFacadeTest.cfc @@ -0,0 +1,25 @@ +component extends="mxunit.framework.TestCase" { + + function setup() { + structDelete( request, "_fw1" ); // clean up the request + } + + function testFacadeOnNonFW1Request() { + try { + var facade = new framework.facade(); + fail( "facade creation did not fail" ); + } catch ( FW1.FacadeException e ) { + assertEquals( "Unable to locate FW/1 for this request", e.message ); + } catch ( any e ) { + fail( "caught unexpected exception: " & e.message ); + } + } + + function testFacadeWithFW1() { + var fw = new framework.one(); + fw.onRequestStart( "" ); + var facade = new framework.facade(); + assertTrue( structKeyExists( facade, "getBeanFactory" ), "Constructed facade does not look like FW/1" ); + } + +} From 15e8c66b756cf7933765540821c252ae91cce35d Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Thu, 16 Jun 2016 13:35:48 -0700 Subject: [PATCH 081/100] Improve reload thread safety #440 --- framework/one.cfc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index c4c51411..69ae6164 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -921,7 +921,7 @@ component { if ( !isFrameworkInitialized() || isFrameworkReloadRequest() ) { setupApplicationWrapper(); } else { - variables.fw1App = getFw1App(); + request._fw1.theApp = getFw1App(); } restoreFlashContext(); @@ -1767,8 +1767,8 @@ component { } private struct function getFw1App() { - if ( structKeyExists( variables, "fw1App" ) ) { - return variables.fw1App; + if ( structKeyExists( request._fw1, 'theApp' ) ) { + return request._fw1.theApp; } else { return application[variables.framework.applicationKey]; } @@ -1879,7 +1879,8 @@ component { private boolean function isFrameworkInitialized() { return structKeyExists( variables, 'framework' ) && - structKeyExists( application, variables.framework.applicationKey ); + ( structKeyExists( request._fw1, 'theApp' ) || + structKeyExists( application, variables.framework.applicationKey ) ); } private boolean function isSubsystemInitialized( string subsystem ) { @@ -2336,7 +2337,7 @@ component { private void function setupApplicationWrapper() { if ( structKeyExists( request._fw1, "appWrapped" ) ) return; request._fw1.appWrapped = true; - variables.fw1App = { + request._fw1.theApp = { cache = { lastReload = now(), fileExists = { }, @@ -2388,7 +2389,7 @@ component { // this will recreate the main bean factory on a reload: internalFrameworkTrace( 'setupApplication() called' ); setupApplication(); - application[variables.framework.applicationKey] = variables.fw1App; + application[variables.framework.applicationKey] = request._fw1.theApp; } From f40864667c48708f50e80e9ce5d359e4ae569ede Mon Sep 17 00:00:00 2001 From: John Whish Date: Thu, 7 Jul 2016 08:40:08 +0100 Subject: [PATCH 082/100] Parse PUT requests and populate request.context from body content --- framework/one.cfc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/framework/one.cfc b/framework/one.cfc index 69ae6164..b3793871 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2787,6 +2787,18 @@ component { message = "Content-Type implies JSON but could not deserialize body: " & e.message ); } break; + case "application/x-www-form-urlencoded": + try { + var paramPairs = listToArray( body, "&" ); + for (var pair in paramPairs) { + var parts = listToArray( pair, "=", true ); // handle blank values + request.context[ parts[ 1 ] ] = urlDecode( parts[ 2 ] ); + } + } catch ( any e ) { + throw( type = "FW1.JSONPOST", + message = "Content-Type implies form encoded but could not deserialize body: " & e.message ); + } + break; default: // ignore -- either built-in (form handling) or unsupported break; From a3347f72ac2dd3f166e804faa171089a5dd9e47b Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 11 Jul 2016 11:56:33 -0700 Subject: [PATCH 083/100] Minor code cleanup #441 Also add credit to John Whish for URL-encoded form data. --- framework/one.cfc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/one.cfc b/framework/one.cfc index b3793871..856539ff 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2773,6 +2773,8 @@ component { if ( variables.framework.enableJSONPOST ) { // thanks to Adam Tuttle and by proxy Jason Dean and Ray Camden for the // seed of this code, inspired by Taffy's basic deserialization + // also thanks to John Whish for the URL-encoded form support + // which adds support for PUT etc var body = httpData.content; if ( isBinary( body ) ) body = charSetEncode( body, "utf-8" ); if ( len( body ) ) { @@ -2790,7 +2792,7 @@ component { case "application/x-www-form-urlencoded": try { var paramPairs = listToArray( body, "&" ); - for (var pair in paramPairs) { + for ( var pair in paramPairs ) { var parts = listToArray( pair, "=", true ); // handle blank values request.context[ parts[ 1 ] ] = urlDecode( parts[ 2 ] ); } From cb7b69596c49c1e7bea6a8d075694819afe27426 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 11 Jul 2016 11:58:10 -0700 Subject: [PATCH 084/100] Whitespace/indentation cleanup Default Emacs indentation, untabify. --- framework/one.cfc | 94 +++++++++++++++++++++++------------------------ 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index 856539ff..a5a862a9 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1,20 +1,20 @@ component { variables._fw1_version = "4.0.0-snapshot"; -/* - Copyright (c) 2009-2016, Sean Corfield, Marcin Szczepanski, Ryan Cogswell + /* + Copyright (c) 2009-2016, Sean Corfield, Marcin Szczepanski, Ryan Cogswell - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ this.name = hash( getBaseTemplatePath() ); if ( !structKeyExists( request, '_fw1' ) ) { @@ -252,7 +252,7 @@ component { if ( structKeyExists( request._fw1, 'controllerExecutionStarted' ) ) { throw( type='FW1.controllerExecutionStarted', message="Controller '#action#' may not be added at this point.", - detail='The controller execution phase has already started. Controllers may not be added by other controller methods.' ); + detail='The controller execution phase has already started. Controllers may not be added by other controller methods.' ); } tuple.controller = getController( section = section, subsystem = subsystem ); @@ -392,7 +392,7 @@ component { if ( variables.framework.defaultSubsystem == '' ) { throw( type='FW1.subsystemNotSpecified', message='No subsystem specified and no default configured.', - detail='When using subsystems, every request should specify a subsystem or variables.framework.defaultSubsystem should be configured.' ); + detail='When using subsystems, every request should specify a subsystem or variables.framework.defaultSubsystem should be configured.' ); } return variables.framework.defaultSubsystem; @@ -715,9 +715,9 @@ component { try { if ( !structKeyExists( variables, 'framework' ) || !structKeyExists( variables.framework, 'version' ) ) { - // error occurred before framework was initialized - failure( exception, event, false, true ); - return; + // error occurred before framework was initialized + failure( exception, event, false, true ); + return; } // record details of the exception: @@ -1329,7 +1329,7 @@ component { * returns the UI generated by the named view */ public any function view( string path, struct args = { }, - any missingView = { } ) { + any missingView = { } ) { var viewPath = parseViewOrLayoutPath( path, 'view' ); if ( cachedFileExists( viewPath ) ) { internalFrameworkTrace( 'view( #path# ) called - rendering #viewPath#' ); @@ -1389,7 +1389,7 @@ component { internalFrameworkTrace( 'building layout queue', subsystem, section, item ); // look for item-specific layout: testLayout = parseViewOrLayoutPath( subsystem & variables.framework.subsystemDelimiter & - section & '/' & item, 'layout' ); + section & '/' & item, 'layout' ); if ( cachedFileExists( testLayout ) ) { internalFrameworkTrace( 'found item-specific layout #testLayout#', subsystem, section, item ); arrayAppend( request._fw1.layouts, testLayout ); @@ -1450,7 +1450,7 @@ component { internalFrameworkTrace( 'building view queue', subsystem, section, item ); // view and layout setup - used to be in setupRequestWrapper(): request._fw1.view = parseViewOrLayoutPath( subsystem & variables.framework.subsystemDelimiter & - section & '/' & item, 'view' ); + section & '/' & item, 'view' ); if ( cachedFileExists( request._fw1.view ) ) { internalFrameworkTrace( 'found view #request._fw1.view#', subsystem, section, item ); } else { @@ -1582,7 +1582,7 @@ component { for ( var i = 1; i <= n; ++i ) { var property = md.properties[ i ]; if ( implicitSetters || - structKeyExists( property, 'setter' ) && isBoolean( property.setter ) && property.setter ) { + structKeyExists( property, 'setter' ) && isBoolean( property.setter ) && property.setter ) { setters[ property.name ] = 'implicit'; } } @@ -1623,8 +1623,8 @@ component { writeOutput( '
' ); writeOutput( '
Framework Lifecycle Trace
' ); var table = '' & - '' & - ''; + '' & + ''; writeOutput( table ); var colors = [ '##ccd4dd', '##ccddcc' ]; var row = 0; @@ -1942,7 +1942,7 @@ component { case 'view': folder = variables.viewFolder; break; - // else leave it alone? + // else leave it alone? } var pathInfo = { }; var subsystem = getSubsystem( getSubsystemSectionAndItem( path ) ); @@ -2268,10 +2268,10 @@ component { structAppend( request.context, session[ preserveKeySessionKey ], false ); if ( variables.framework.maxNumContextsPreserved == 1 ) { /* - When multiple contexts are preserved, the oldest context is purged - within getNextPreserveKeyAndPurgeOld once the maximum is reached. - This allows for a browser refresh after the redirect to still receive - the same context. + When multiple contexts are preserved, the oldest context is purged + within getNextPreserveKeyAndPurgeOld once the maximum is reached. + This allows for a browser refresh after the redirect to still receive + the same context. */ structDelete( session, preserveKeySessionKey ); } @@ -2389,9 +2389,9 @@ component { // this will recreate the main bean factory on a reload: internalFrameworkTrace( 'setupApplication() called' ); setupApplication(); - application[variables.framework.applicationKey] = request._fw1.theApp; + application[variables.framework.applicationKey] = request._fw1.theApp; - } + } private void function setupFrameworkDefaults() { if ( structKeyExists( variables, "_fw1_defaults_initialized" ) ) return; @@ -2585,19 +2585,19 @@ component { if ( !structKeyExists( variables.framework, 'diComponent' ) ) { var diComponent = 'framework.ioc'; switch ( variables.framework.diEngine ) { - case 'aop1': - diComponent = 'framework.aop'; - break; - case 'wirebox': - diComponent = 'framework.WireBoxAdapter'; - break; - case 'custom': - throw( type="FW1.IllegalConfiguration", - message="If you specify diEngine='custom' you must specify a component path for diComponent." ); - break; - default: - // assume DI/1 - break; + case 'aop1': + diComponent = 'framework.aop'; + break; + case 'wirebox': + diComponent = 'framework.WireBoxAdapter'; + break; + case 'custom': + throw( type="FW1.IllegalConfiguration", + message="If you specify diEngine='custom' you must specify a component path for diComponent." ); + break; + default: + // assume DI/1 + break; } variables.framework.diComponent = diComponent; } @@ -2793,8 +2793,8 @@ component { try { var paramPairs = listToArray( body, "&" ); for ( var pair in paramPairs ) { - var parts = listToArray( pair, "=", true ); // handle blank values - request.context[ parts[ 1 ] ] = urlDecode( parts[ 2 ] ); + var parts = listToArray( pair, "=", true ); // handle blank values + request.context[ parts[ 1 ] ] = urlDecode( parts[ 2 ] ); } } catch ( any e ) { throw( type = "FW1.JSONPOST", @@ -2902,7 +2902,7 @@ component { // check for forward and backward slash in the action - using chr() to avoid confusing TextMate (Hi Nathan!) if ( findOneOf( chr(47) & chr(92), action ) > 0 ) { throw( type='FW1.actionContainsSlash', message="Found a slash in the action: '#action#'.", - detail='Actions are not allowed to embed sub-directory paths.'); + detail='Actions are not allowed to embed sub-directory paths.'); } return action; } @@ -2913,7 +2913,7 @@ component { // the exception we actually want to throw! param name="request.missingView" default=""; throw( type='FW1.viewNotFound', message="Unable to find a view for '#request.action#' action.", - detail="'#request.missingView#' does not exist." ); + detail="'#request.missingView#' does not exist." ); } } From 71f2dd5db91e668f591b65180d63486828fc14e1 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 11 Jul 2016 12:31:23 -0700 Subject: [PATCH 085/100] Prep 4.0.0 Beta 1 --- framework/Application.cfc | 2 +- framework/MyApplication.cfc | 2 +- framework/WireBoxAdapter.cfc | 2 +- framework/aop.cfc | 4 ++-- framework/beanProxy.cfc | 4 ++-- framework/cfmljure.cfc | 4 ++-- framework/cljcontroller.cfc | 4 ++-- framework/facade.cfc | 2 +- framework/ioc.cfc | 4 ++-- framework/ioclj.cfc | 4 ++-- framework/one.cfc | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/framework/Application.cfc b/framework/Application.cfc index 42720eff..62549953 100644 --- a/framework/Application.cfc +++ b/framework/Application.cfc @@ -1,5 +1,5 @@ component { - // Version: FW/1 4.0.0 snapshot + // Version: FW/1 4.0.0 Beta 1 // copy this to your application root to use as your Application.cfc // or incorporate the logic below into your existing Application.cfc diff --git a/framework/MyApplication.cfc b/framework/MyApplication.cfc index d2c9078f..39bf4197 100644 --- a/framework/MyApplication.cfc +++ b/framework/MyApplication.cfc @@ -1,5 +1,5 @@ component extends="framework.one" { - // Version: FW/1 4.0.0 snapshot + // Version: FW/1 4.0.0 Beta 1 // if you need to provide extension points, copy this to // your web root, next to your Application.cfc, and add diff --git a/framework/WireBoxAdapter.cfc b/framework/WireBoxAdapter.cfc index b472284f..eec35547 100644 --- a/framework/WireBoxAdapter.cfc +++ b/framework/WireBoxAdapter.cfc @@ -1,5 +1,5 @@ component extends="wirebox.system.ioc.Injector" { - variables._fw1_version = "4.0.0-snapshot"; + variables._fw1_version = "4.0.0-beta1"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/aop.cfc b/framework/aop.cfc index 47ccb927..f8c7187e 100755 --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -1,6 +1,6 @@ component extends="framework.ioc" { - variables._fw1_version = "4.0.0-snapshot"; - variables._aop1_version = "2.0.2-snapshot"; + variables._fw1_version = "4.0.0-beta1"; + variables._aop1_version = "2.0.2-beta1"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/beanProxy.cfc b/framework/beanProxy.cfc index 9f4aa62a..27a264b4 100755 --- a/framework/beanProxy.cfc +++ b/framework/beanProxy.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._aop1_version = "2.0.2-snapshot"; + variables._fw1_version = "4.0.0-beta1"; + variables._aop1_version = "2.0.2-beta1"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index c740a10a..aa3b6a85 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._cfmljure_version = "1.1.0-snapshot"; + variables._fw1_version = "4.0.0-beta1"; + variables._cfmljure_version = "1.1.0-beta1"; /* Copyright (c) 2012-2016, Sean Corfield diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index 9243d033..f010ee9b 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._ioclj_version = "1.0.1-snapshot"; + variables._fw1_version = "4.0.0-beta1"; + variables._ioclj_version = "1.0.1-beta1"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/facade.cfc b/framework/facade.cfc index b895ac81..4b674b4e 100644 --- a/framework/facade.cfc +++ b/framework/facade.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-snapshot"; + variables._fw1_version = "4.0.0-beta1"; /* Copyright (c) 2016, Sean Corfield diff --git a/framework/ioc.cfc b/framework/ioc.cfc index b86e8e26..2cdc99ac 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._di1_version = "1.2.0-snapshot"; + variables._fw1_version = "4.0.0-beta1"; + variables._di1_version = "1.2.0-beta1"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index c5ed7b98..ea75a631 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -1,6 +1,6 @@ component extends=framework.ioc { - variables._fw1_version = "4.0.0-snapshot"; - variables._ioclj_version = "1.1.0-snapshot"; + variables._fw1_version = "4.0.0-beta1"; + variables._ioclj_version = "1.1.0-beta1"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/one.cfc b/framework/one.cfc index a5a862a9..6131f94d 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-snapshot"; + variables._fw1_version = "4.0.0-beta1"; /* Copyright (c) 2009-2016, Sean Corfield, Marcin Szczepanski, Ryan Cogswell From a1e46de133d39fd82ede1a35df71a0f1a8683093 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 11 Jul 2016 14:18:41 -0700 Subject: [PATCH 086/100] Back to SNAPSHOT, post-beta1 --- framework/WireBoxAdapter.cfc | 2 +- framework/aop.cfc | 4 ++-- framework/beanProxy.cfc | 4 ++-- framework/cfmljure.cfc | 4 ++-- framework/cljcontroller.cfc | 4 ++-- framework/facade.cfc | 2 +- framework/ioc.cfc | 4 ++-- framework/ioclj.cfc | 4 ++-- framework/one.cfc | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/framework/WireBoxAdapter.cfc b/framework/WireBoxAdapter.cfc index eec35547..b472284f 100644 --- a/framework/WireBoxAdapter.cfc +++ b/framework/WireBoxAdapter.cfc @@ -1,5 +1,5 @@ component extends="wirebox.system.ioc.Injector" { - variables._fw1_version = "4.0.0-beta1"; + variables._fw1_version = "4.0.0-snapshot"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/aop.cfc b/framework/aop.cfc index f8c7187e..47ccb927 100755 --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -1,6 +1,6 @@ component extends="framework.ioc" { - variables._fw1_version = "4.0.0-beta1"; - variables._aop1_version = "2.0.2-beta1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._aop1_version = "2.0.2-snapshot"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/beanProxy.cfc b/framework/beanProxy.cfc index 27a264b4..9f4aa62a 100755 --- a/framework/beanProxy.cfc +++ b/framework/beanProxy.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-beta1"; - variables._aop1_version = "2.0.2-beta1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._aop1_version = "2.0.2-snapshot"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index aa3b6a85..c740a10a 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-beta1"; - variables._cfmljure_version = "1.1.0-beta1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._cfmljure_version = "1.1.0-snapshot"; /* Copyright (c) 2012-2016, Sean Corfield diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index f010ee9b..9243d033 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-beta1"; - variables._ioclj_version = "1.0.1-beta1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._ioclj_version = "1.0.1-snapshot"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/facade.cfc b/framework/facade.cfc index 4b674b4e..b895ac81 100644 --- a/framework/facade.cfc +++ b/framework/facade.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-beta1"; + variables._fw1_version = "4.0.0-snapshot"; /* Copyright (c) 2016, Sean Corfield diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 2cdc99ac..b86e8e26 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-beta1"; - variables._di1_version = "1.2.0-beta1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._di1_version = "1.2.0-snapshot"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index ea75a631..c5ed7b98 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -1,6 +1,6 @@ component extends=framework.ioc { - variables._fw1_version = "4.0.0-beta1"; - variables._ioclj_version = "1.1.0-beta1"; + variables._fw1_version = "4.0.0-snapshot"; + variables._ioclj_version = "1.1.0-snapshot"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/one.cfc b/framework/one.cfc index 6131f94d..a5a862a9 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-beta1"; + variables._fw1_version = "4.0.0-snapshot"; /* Copyright (c) 2009-2016, Sean Corfield, Marcin Szczepanski, Ryan Cogswell From 79087f655dd0ebe93df0b9dd2a66415ddd8ac028 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 12 Jul 2016 10:33:20 -0700 Subject: [PATCH 087/100] Fix #442 by renaming `enableJSONPOST` Setting is now `decodeRequestBody`. Using `enableJSONPOST` will throw an exception. --- framework/one.cfc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index a5a862a9..b72d1b0a 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2601,8 +2601,12 @@ component { } variables.framework.diComponent = diComponent; } - if ( !structKeyExists( variables.framework, 'enableJSONPOST' ) ) { - variables.framework.enableJSONPOST = false; + if ( structKeyExists( variables.framework, 'enableJSONPOST' ) ) { + throw( type="FW1.IllegalConfiguration", + message="The enableJSONPOST setting has been renamed to decodeRequestBody." ); + } + if ( !structKeyExists( variables.framework, 'decodeRequestBody' ) ) { + variables.framework.decodeRequestBody = false; } if ( !structKeyExists( variables.framework, 'preflightOptions' ) ) { variables.framework.preflightOptions = false; @@ -2770,7 +2774,7 @@ component { if ( isDefined( 'URL' ) ) structAppend( request.context, URL ); if ( isDefined( 'form' ) ) structAppend( request.context, form ); var httpData = getHttpRequestData(); - if ( variables.framework.enableJSONPOST ) { + if ( variables.framework.decodeRequestBody ) { // thanks to Adam Tuttle and by proxy Jason Dean and Ray Camden for the // seed of this code, inspired by Taffy's basic deserialization // also thanks to John Whish for the URL-encoded form support From 3a3235a4efac8ef996e1bc6aa65bb1d08b43752b Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 12 Jul 2016 12:27:08 -0700 Subject: [PATCH 088/100] Prep for 4.0.0 Beta 2 --- framework/WireBoxAdapter.cfc | 2 +- framework/aop.cfc | 4 ++-- framework/beanProxy.cfc | 4 ++-- framework/cfmljure.cfc | 4 ++-- framework/cljcontroller.cfc | 4 ++-- framework/facade.cfc | 2 +- framework/ioc.cfc | 4 ++-- framework/ioclj.cfc | 4 ++-- framework/one.cfc | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/framework/WireBoxAdapter.cfc b/framework/WireBoxAdapter.cfc index b472284f..05a3cc47 100644 --- a/framework/WireBoxAdapter.cfc +++ b/framework/WireBoxAdapter.cfc @@ -1,5 +1,5 @@ component extends="wirebox.system.ioc.Injector" { - variables._fw1_version = "4.0.0-snapshot"; + variables._fw1_version = "4.0.0-beta2"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/aop.cfc b/framework/aop.cfc index 47ccb927..f9d486de 100755 --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -1,6 +1,6 @@ component extends="framework.ioc" { - variables._fw1_version = "4.0.0-snapshot"; - variables._aop1_version = "2.0.2-snapshot"; + variables._fw1_version = "4.0.0-beta2"; + variables._aop1_version = "2.0.2-beta2"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/beanProxy.cfc b/framework/beanProxy.cfc index 9f4aa62a..77119806 100755 --- a/framework/beanProxy.cfc +++ b/framework/beanProxy.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._aop1_version = "2.0.2-snapshot"; + variables._fw1_version = "4.0.0-beta2"; + variables._aop1_version = "2.0.2-beta2"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index c740a10a..5a58fb50 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._cfmljure_version = "1.1.0-snapshot"; + variables._fw1_version = "4.0.0-beta2"; + variables._cfmljure_version = "1.1.0-beta2"; /* Copyright (c) 2012-2016, Sean Corfield diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index 9243d033..5c912186 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._ioclj_version = "1.0.1-snapshot"; + variables._fw1_version = "4.0.0-beta2"; + variables._ioclj_version = "1.0.1-beta2"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/facade.cfc b/framework/facade.cfc index b895ac81..05bcc298 100644 --- a/framework/facade.cfc +++ b/framework/facade.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-snapshot"; + variables._fw1_version = "4.0.0-beta2"; /* Copyright (c) 2016, Sean Corfield diff --git a/framework/ioc.cfc b/framework/ioc.cfc index b86e8e26..e2192dec 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-snapshot"; - variables._di1_version = "1.2.0-snapshot"; + variables._fw1_version = "4.0.0-beta2"; + variables._di1_version = "1.2.0-beta2"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index c5ed7b98..2f62746a 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -1,6 +1,6 @@ component extends=framework.ioc { - variables._fw1_version = "4.0.0-snapshot"; - variables._ioclj_version = "1.1.0-snapshot"; + variables._fw1_version = "4.0.0-beta2"; + variables._ioclj_version = "1.1.0-beta2"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/one.cfc b/framework/one.cfc index b72d1b0a..c0604bdb 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-snapshot"; + variables._fw1_version = "4.0.0-beta2"; /* Copyright (c) 2009-2016, Sean Corfield, Marcin Szczepanski, Ryan Cogswell From c1b6607e98e7b4ece11c8641b08dc59e25153a89 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 26 Jul 2016 10:41:42 -0700 Subject: [PATCH 089/100] Adjust subsystem factory setting inheritance #443 Ensure that `loadListener` setting is not inherited by subsystem bean factories but a custom subsystem bean factory configuration can still specify a `loadListener` if that's what the developer wants (be careful!). --- framework/one.cfc | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/framework/one.cfc b/framework/one.cfc index c0604bdb..874cb573 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2886,8 +2886,13 @@ component { } if ( len( sublocations ) ) { var diComponent = structKeyExists( subsystemConfig, 'diComponent' ) ? subsystemConfig : variables.framework.diComponent; - var cfg = structKeyExists( subsystemConfig, 'diConfig' ) ? - subsystemConfig.diConfig : structCopy( variables.framework.diConfig ); + var cfg = { }; + if ( structKeyExists( subsystemConfig, 'diConfig' ) ) { + cfg = subsystemConfig.diConfig; + } else { + cfg = structCopy( variables.framework.diConfig ); + structDelete( cfg, 'loadListener' ); + } cfg.noClojure = true; var ioc = new "#diComponent#"( subLocations, cfg ); ioc.setParent( getDefaultBeanFactory() ); From 77481c1a953e848f05388c5d87e4f2f34ed3166b Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 26 Jul 2016 10:45:23 -0700 Subject: [PATCH 090/100] Bump FW/1 version for #443 Assume RC1. --- framework/one.cfc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/one.cfc b/framework/one.cfc index 874cb573..083f924a 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-beta2"; + variables._fw1_version = "4.0.0-rc1"; /* Copyright (c) 2009-2016, Sean Corfield, Marcin Szczepanski, Ryan Cogswell From 759f633e24ea23a5adb2548a103d8dcd18c32629 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Tue, 26 Jul 2016 10:55:17 -0700 Subject: [PATCH 091/100] Prep for 4.0.0 RC 1 Also update example app home page links to documentation and blog. --- framework/WireBoxAdapter.cfc | 2 +- framework/aop.cfc | 4 ++-- framework/beanProxy.cfc | 4 ++-- framework/cfmljure.cfc | 4 ++-- framework/cljcontroller.cfc | 4 ++-- framework/facade.cfc | 2 +- framework/ioc.cfc | 4 ++-- framework/ioclj.cfc | 4 ++-- introduction/views/main/default.cfm | 8 ++++---- 9 files changed, 18 insertions(+), 18 deletions(-) diff --git a/framework/WireBoxAdapter.cfc b/framework/WireBoxAdapter.cfc index 05a3cc47..385c13a7 100644 --- a/framework/WireBoxAdapter.cfc +++ b/framework/WireBoxAdapter.cfc @@ -1,5 +1,5 @@ component extends="wirebox.system.ioc.Injector" { - variables._fw1_version = "4.0.0-beta2"; + variables._fw1_version = "4.0.0-rc1"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/aop.cfc b/framework/aop.cfc index f9d486de..147ca491 100755 --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -1,6 +1,6 @@ component extends="framework.ioc" { - variables._fw1_version = "4.0.0-beta2"; - variables._aop1_version = "2.0.2-beta2"; + variables._fw1_version = "4.0.0-rc1"; + variables._aop1_version = "2.0.2-rc1"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/beanProxy.cfc b/framework/beanProxy.cfc index 77119806..037bd395 100755 --- a/framework/beanProxy.cfc +++ b/framework/beanProxy.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-beta2"; - variables._aop1_version = "2.0.2-beta2"; + variables._fw1_version = "4.0.0-rc1"; + variables._aop1_version = "2.0.2-rc1"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index 5a58fb50..00a99eea 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-beta2"; - variables._cfmljure_version = "1.1.0-beta2"; + variables._fw1_version = "4.0.0-rc1"; + variables._cfmljure_version = "1.1.0-rc1"; /* Copyright (c) 2012-2016, Sean Corfield diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index 5c912186..d63e8c33 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-beta2"; - variables._ioclj_version = "1.0.1-beta2"; + variables._fw1_version = "4.0.0-rc1"; + variables._ioclj_version = "1.0.1-rc1"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/facade.cfc b/framework/facade.cfc index 05bcc298..ba4d33c7 100644 --- a/framework/facade.cfc +++ b/framework/facade.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-beta2"; + variables._fw1_version = "4.0.0-rc1"; /* Copyright (c) 2016, Sean Corfield diff --git a/framework/ioc.cfc b/framework/ioc.cfc index e2192dec..d008f789 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-beta2"; - variables._di1_version = "1.2.0-beta2"; + variables._fw1_version = "4.0.0-rc1"; + variables._di1_version = "1.2.0-rc1"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index 2f62746a..d33e58f6 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -1,6 +1,6 @@ component extends=framework.ioc { - variables._fw1_version = "4.0.0-beta2"; - variables._ioclj_version = "1.1.0-beta2"; + variables._fw1_version = "4.0.0-rc1"; + variables._ioclj_version = "1.1.0-rc1"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/introduction/views/main/default.cfm b/introduction/views/main/default.cfm index 093ac542..9cc61209 100644 --- a/introduction/views/main/default.cfm +++ b/introduction/views/main/default.cfm @@ -1,9 +1,9 @@

Welcome to Framework One!

#view('about/default')#

Documentation

-

The documentation for FW/1 can be found on its github wiki page. - The latest news about FW/1 can always be found on its RIAForge project page - which also has links to the mailing list and - blog.

+

Read the documentation for FW/1. + The latest news can always be found on the FW/1 blog + which also has links to the FW/1 mailing list + and other avenues for community support.

Examples

#view('main/examples')# From 21a6c59be2a79f6437a443499750e9ecb777e142 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Thu, 25 Aug 2016 14:08:06 -0700 Subject: [PATCH 092/100] Allow deserializeJSON() to be overridden, to match serializeJSON() override --- framework/one.cfc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/framework/one.cfc b/framework/one.cfc index 083f924a..1b4c4b62 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -2126,6 +2126,10 @@ component { return resourceCache[ cacheKey ]; } + private any function read_json( string json ) { + return deserializeJSON( json ); + } + private struct function render_json( struct renderData ) { return { contentType = 'application/json; charset=utf-8', @@ -2786,7 +2790,7 @@ component { case "application/json": case "text/json": try { - var bodyStruct = deserializeJSON( body ); + var bodyStruct = read_json( body ); structAppend( request.context, bodyStruct ); } catch ( any e ) { throw( type = "FW1.JSONPOST", From 80a2913f6e8fa41c3a332d16fcd3ce838fa0b828 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Thu, 25 Aug 2016 20:01:30 -0700 Subject: [PATCH 093/100] Address #446 by expanding hashcode for $RESOURCES Also changed version to SNAPSHOT again. --- framework/WireBoxAdapter.cfc | 2 +- framework/aop.cfc | 4 ++-- framework/beanProxy.cfc | 4 ++-- framework/cfmljure.cfc | 4 ++-- framework/cljcontroller.cfc | 4 ++-- framework/facade.cfc | 2 +- framework/ioc.cfc | 4 ++-- framework/ioclj.cfc | 4 ++-- framework/one.cfc | 4 ++-- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/framework/WireBoxAdapter.cfc b/framework/WireBoxAdapter.cfc index 385c13a7..181ce70a 100644 --- a/framework/WireBoxAdapter.cfc +++ b/framework/WireBoxAdapter.cfc @@ -1,5 +1,5 @@ component extends="wirebox.system.ioc.Injector" { - variables._fw1_version = "4.0.0-rc1"; + variables._fw1_version = "4.0.0-SNAPSHOT"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/aop.cfc b/framework/aop.cfc index 147ca491..9b6fb1a7 100755 --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -1,6 +1,6 @@ component extends="framework.ioc" { - variables._fw1_version = "4.0.0-rc1"; - variables._aop1_version = "2.0.2-rc1"; + variables._fw1_version = "4.0.0-SNAPSHOT"; + variables._aop1_version = "2.0.2-SNAPSHOT"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/beanProxy.cfc b/framework/beanProxy.cfc index 037bd395..bc8e188a 100755 --- a/framework/beanProxy.cfc +++ b/framework/beanProxy.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-rc1"; - variables._aop1_version = "2.0.2-rc1"; + variables._fw1_version = "4.0.0-SNAPSHOT"; + variables._aop1_version = "2.0.2-SNAPSHOT"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index 00a99eea..55f597ab 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-rc1"; - variables._cfmljure_version = "1.1.0-rc1"; + variables._fw1_version = "4.0.0-SNAPSHOT"; + variables._cfmljure_version = "1.1.0-SNAPSHOT"; /* Copyright (c) 2012-2016, Sean Corfield diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index d63e8c33..cb6c2fdc 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-rc1"; - variables._ioclj_version = "1.0.1-rc1"; + variables._fw1_version = "4.0.0-SNAPSHOT"; + variables._ioclj_version = "1.0.1-SNAPSHOT"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/facade.cfc b/framework/facade.cfc index ba4d33c7..4cdbc46c 100644 --- a/framework/facade.cfc +++ b/framework/facade.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-rc1"; + variables._fw1_version = "4.0.0-SNAPSHOT"; /* Copyright (c) 2016, Sean Corfield diff --git a/framework/ioc.cfc b/framework/ioc.cfc index d008f789..fbf5c9fe 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-rc1"; - variables._di1_version = "1.2.0-rc1"; + variables._fw1_version = "4.0.0-SNAPSHOT"; + variables._di1_version = "1.2.0-SNAPSHOT"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index d33e58f6..4a93bd57 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -1,6 +1,6 @@ component extends=framework.ioc { - variables._fw1_version = "4.0.0-rc1"; - variables._ioclj_version = "1.1.0-rc1"; + variables._fw1_version = "4.0.0-SNAPSHOT"; + variables._ioclj_version = "1.1.0-SNAPSHOT"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/one.cfc b/framework/one.cfc index 1b4c4b62..7cfc44d5 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-rc1"; + variables._fw1_version = "4.0.0-SNAPSHOT"; /* Copyright (c) 2009-2016, Sean Corfield, Marcin Szczepanski, Ryan Cogswell @@ -2071,7 +2071,7 @@ component { private array function getResourceRoutes( any resourcesToRoute, string subsystem = '', string pathRoot = '', string targetAppend = '' ) { var resourceCache = isFrameworkInitialized() ? getFw1App().cache.routes.resources : { }; - var cacheKey = hash( serializeJSON( resourcesToRoute ) ); + var cacheKey = hash( serializeJSON( { rtr = resourcesToRoute, ss = subsystem, pr = pathRoot, ta = targetAppend } ) ); if ( !structKeyExists( resourceCache, cacheKey ) ) { // get passed in resourcesToRoute (string,array,struct) to match following struct var resources = { resources = [ ], subsystem = subsystem, pathRoot = pathRoot, methods = [ ], nested = [ ] }; From 60665c7a5fdc75f47f27dae8aeb21d89f03acc8c Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Mon, 29 Aug 2016 17:54:17 -0700 Subject: [PATCH 094/100] Remove references to RIAForge Point to GitHub instead --- README.md | 3 +-- examples/layouts/default.cfm | 2 +- introduction/layouts/default.cfm | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5759a3d1..c46a3c73 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ Please read the [Framework One Code of Conduct](CODE_OF_CONDUCT.md) - we want FW # Resources -**Project home:** http://fw1.riaforge.org +**Project home:** https://github.com/framework-one/fw1 **Documentation / Wiki:** http://framework-one.github.io/documentation/ / http://github.com/framework-one/fw1/wiki @@ -48,4 +48,3 @@ See the `run-tests-example.sh` file for a template (for Mac/Linux). Copyright (c) 2009-2016 Sean Corfield (and others -- see individual files for additional copyright holders). All rights reserved. The use and distribution terms for this software are covered by the Apache Software License 2.0 (http://www.apache.org/licenses/LICENSE-2.0) which can also be found in the file LICENSE at the root of this distribution and in individual licensed files. By using this software in any fashion, you are agreeing to be bound by the terms of this license. You must not remove this notice, or any other, from this software. - diff --git a/examples/layouts/default.cfm b/examples/layouts/default.cfm index 4ca572f7..d0f85065 100644 --- a/examples/layouts/default.cfm +++ b/examples/layouts/default.cfm @@ -12,7 +12,7 @@

Examples Home

diff --git a/introduction/layouts/default.cfm b/introduction/layouts/default.cfm index e9da1cee..0283b989 100644 --- a/introduction/layouts/default.cfm +++ b/introduction/layouts/default.cfm @@ -11,7 +11,7 @@ #body# From 8f0408b03dde691d1f18099639d74265cc7bd405 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 7 Sep 2016 18:34:37 -0700 Subject: [PATCH 095/100] Remove Windows linebreaks! --- framework/aop.cfc | 624 ++++++------- framework/beanProxy.cfc | 1872 +++++++++++++++++++-------------------- 2 files changed, 1248 insertions(+), 1248 deletions(-) mode change 100755 => 100644 framework/aop.cfc mode change 100755 => 100644 framework/beanProxy.cfc diff --git a/framework/aop.cfc b/framework/aop.cfc old mode 100755 new mode 100644 index 9b6fb1a7..2062c29c --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -1,312 +1,312 @@ -component extends="framework.ioc" { - variables._fw1_version = "4.0.0-SNAPSHOT"; - variables._aop1_version = "2.0.2-SNAPSHOT"; -/* - Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - // Internal cache of interceptor definitions. - variables.interceptorCache = {regex = [], name = {}, type = []}; - - - - - // -------------- // - // PUBLIC METHODS // - // -------------- // - - /** Constructor. */ - public any function init(string folders, struct config = {}) - { - super.init(argumentCollection = arguments); - - if (structKeyExists(arguments.config, "interceptors") && isArray(arguments.config.interceptors) && arrayLen(arguments.config.interceptors)) - { - loadInterceptors(arguments.config.interceptors); - } - } - - - /** Adds an interceptor definition to the definition cache. */ - public any function intercept(string beanName, string interceptorName, string methods = "") - { - var interceptDefinition = - { - name = arguments.interceptorName, - methods = arguments.methods - }; - - - arguments.beanName = trim(arguments.beanName); - - - // Determine if this is a name match or regex match. - if (len(arguments.beanName) && left(arguments.beanName, 1) == "/" && right(arguments.beanName, 1) == "/") - { - // Store regex without the forward slashes. - interceptDefinition.regex = mid(arguments.beanName, 2, len(arguments.beanName) - 2); - - arrayAppend(variables.interceptorCache.regex, interceptDefinition); - } - else - { - if (!structKeyExists(variables.interceptorCache.name, arguments.beanName)) - { - variables.interceptorCache.name[arguments.beanName] = []; - } - - arrayAppend(variables.interceptorCache.name[arguments.beanName], interceptDefinition); - } - - - return this; - } - - - /** Adds an interceptor definition to the definition cache. */ - public any function interceptByType(string type, string interceptorName, string methods = "") - { - var interceptDefinition = - { - type = arguments.type, - name = arguments.interceptorName, - methods = arguments.methods - }; - - arrayAppend(variables.interceptorCache.type, interceptDefinition); - } - - - - - // --------------- // - // PRIVATE METHODS // - // --------------- // - - /** Hook point to wrap bean with proxy. */ - private any function construct(string dottedPath) - { - var bean = super.construct(arguments.dottedPath); - var beanProxy = ""; - - // if it doesn't have a dotted path for us to create a new instance - // or it has no interceptors, we have to leave it alone - if (!hasInterceptors(arguments.dottedPath)) - { - return bean; - } - - // Create and return a proxy wrapping the bean. - beanProxy = new framework.beanProxy(bean, getInterceptorsForBean(arguments.dottedPath), variables.config); - - return beanProxy; - } - - - /** Gets the associated interceptor definitions for a specific bean. */ - private array function getInterceptorsForBean(string dottedPath) - { - // build the interceptor array: - var beanName = listLast(arguments.dottedPath, "."); - var beanNames = getAliases(beanName); - var beanTypes = ""; - var interceptDefinition = ""; - var interceptedBeanName = ""; - var interceptors = []; - - - arrayPrepend(beanNames, beanName); - - - // Grab all name based interceptors that match. - for (interceptedBeanName in beanNames) - { - // Match on name. - if (structKeyExists(variables.interceptorCache.name, interceptedBeanName)) - { - for (interceptDefinition in variables.interceptorCache.name[interceptedBeanName]) - { - arrayAppend(interceptors, {bean = getBean(interceptDefinition.name), methods = interceptDefinition.methods}); - } - } - } - - - // Match on regex. Ensure we only attach each one time. - if (arrayLen(variables.interceptorCache.regex)) - { - for (interceptDefinition in variables.interceptorCache.regex) - { - for (interceptedBeanName in beanNames) - { - if (reFindNoCase(interceptDefinition.regex, interceptedBeanName)) - { - arrayAppend(interceptors, {bean = getBean(interceptDefinition.name), methods = interceptDefinition.methods}); - break; - } - } - } - } - - - // Grab all type based interceptors that match. - if (arrayLen(variables.interceptorCache.type)) - { - beanTypes = getBeanTypes(arguments.dottedPath); - - for (interceptDefinition in variables.interceptorCache.type) - { - if (listFindNoCase(beanTypes, interceptDefinition.type)) - { - arrayAppend(interceptors, {bean = getBean(interceptDefinition.name), methods = interceptDefinition.methods}); - } - } - } - - - return interceptors; - } - - - /** Determines if the bean has interceptor definitions associated with it. */ - private boolean function hasInterceptors(string dottedPath) - { - var interceptedBeanName = ""; - var interceptorDefinition = {}; - var beanName = listLast(arguments.dottedPath, "."); - var beanNames = getAliases(beanName); - var beanTypes = ""; - - - arrayPrepend(beanNames, beanName); - - - for (interceptedBeanName in beanNames) - { - // Look for matches on name first. - if (structKeyExists(variables.interceptorCache.name, interceptedBeanName)) - { - return true; - } - - - // Look for matches on regex. - if (arrayLen(variables.interceptorCache.regex)) - { - for (interceptorDefinition in variables.interceptorCache.regex) - { - if (reFindNoCase(interceptorDefinition.regex, interceptedBeanName)) - { - return true; - } - } - } - - - // Look for matches by bean type. - if (arrayLen(variables.interceptorCache.type)) - { - beanTypes = getBeanTypes(arguments.dottedPath); - - for (interceptorDefinition in variables.interceptorCache.type) - { - if (listFindNoCase(beanTypes, interceptorDefinition.type)) - { - return true; - } - } - } - } - - - return false; - } - - - /** Finds all aliases for the given beanName. */ - private array function getAliases(string beanName) - { - var aliases = []; - var beanData = ""; - var key = ""; - - - if (structKeyExists(variables.beanInfo, arguments.beanName)) - { - beanData = variables.beanInfo[arguments.beanName]; - - for (key in variables.beanInfo) - { - // Same cfc dotted path, must be an alias. - if ( - key != arguments.beanName && - structKeyExists(variables.beanInfo[key], "cfc") && - structKeyExists(variables.beanInfo[arguments.beanName], "cfc") && - variables.beanInfo[key].cfc == variables.beanInfo[arguments.beanName].cfc) - { - arrayAppend(aliases, key); - } - } - } - - return aliases; - } - - - /** Returns a list of bean types (both name and dotted path) for a given bean. */ - private string function getBeanTypes(string dottedPath) - { - var beanTypes = ""; - var metadata = getComponentMetadata(arguments.dottedPath); - - while (!len(beanTypes) || structKeyExists(metadata, "extends")) - { - beanTypes = listAppend(beanTypes, listLast(metadata.name, ".")); - beanTypes = listAppend(beanTypes, metadata.name); - - if (structKeyExists(metadata, "extends")) - { - metadata = metadata.extends; - } - } - - return beanTypes; - } - - - /** Loads an array of interceptor definitions into the interceptor definition cache. */ - private void function loadInterceptors(array interceptors) - { - var interceptor = false; - - for (interceptor in interceptors) - { - if (structKeyExists(interceptor, "beanName")) - { - intercept(argumentCollection = interceptor); - } - else - { - interceptByType(argumentCollection = interceptor); - } - } - } - - - private void function setupFrameworkDefaults() - { - super.setupFrameworkDefaults(); - variables.config.version = variables._aop1_version & " (" & variables._di1_version & ")"; - } -} +component extends="framework.ioc" { + variables._fw1_version = "4.0.0-SNAPSHOT"; + variables._aop1_version = "2.0.2-SNAPSHOT"; +/* + Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + // Internal cache of interceptor definitions. + variables.interceptorCache = {regex = [], name = {}, type = []}; + + + + + // -------------- // + // PUBLIC METHODS // + // -------------- // + + /** Constructor. */ + public any function init(string folders, struct config = {}) + { + super.init(argumentCollection = arguments); + + if (structKeyExists(arguments.config, "interceptors") && isArray(arguments.config.interceptors) && arrayLen(arguments.config.interceptors)) + { + loadInterceptors(arguments.config.interceptors); + } + } + + + /** Adds an interceptor definition to the definition cache. */ + public any function intercept(string beanName, string interceptorName, string methods = "") + { + var interceptDefinition = + { + name = arguments.interceptorName, + methods = arguments.methods + }; + + + arguments.beanName = trim(arguments.beanName); + + + // Determine if this is a name match or regex match. + if (len(arguments.beanName) && left(arguments.beanName, 1) == "/" && right(arguments.beanName, 1) == "/") + { + // Store regex without the forward slashes. + interceptDefinition.regex = mid(arguments.beanName, 2, len(arguments.beanName) - 2); + + arrayAppend(variables.interceptorCache.regex, interceptDefinition); + } + else + { + if (!structKeyExists(variables.interceptorCache.name, arguments.beanName)) + { + variables.interceptorCache.name[arguments.beanName] = []; + } + + arrayAppend(variables.interceptorCache.name[arguments.beanName], interceptDefinition); + } + + + return this; + } + + + /** Adds an interceptor definition to the definition cache. */ + public any function interceptByType(string type, string interceptorName, string methods = "") + { + var interceptDefinition = + { + type = arguments.type, + name = arguments.interceptorName, + methods = arguments.methods + }; + + arrayAppend(variables.interceptorCache.type, interceptDefinition); + } + + + + + // --------------- // + // PRIVATE METHODS // + // --------------- // + + /** Hook point to wrap bean with proxy. */ + private any function construct(string dottedPath) + { + var bean = super.construct(arguments.dottedPath); + var beanProxy = ""; + + // if it doesn't have a dotted path for us to create a new instance + // or it has no interceptors, we have to leave it alone + if (!hasInterceptors(arguments.dottedPath)) + { + return bean; + } + + // Create and return a proxy wrapping the bean. + beanProxy = new framework.beanProxy(bean, getInterceptorsForBean(arguments.dottedPath), variables.config); + + return beanProxy; + } + + + /** Gets the associated interceptor definitions for a specific bean. */ + private array function getInterceptorsForBean(string dottedPath) + { + // build the interceptor array: + var beanName = listLast(arguments.dottedPath, "."); + var beanNames = getAliases(beanName); + var beanTypes = ""; + var interceptDefinition = ""; + var interceptedBeanName = ""; + var interceptors = []; + + + arrayPrepend(beanNames, beanName); + + + // Grab all name based interceptors that match. + for (interceptedBeanName in beanNames) + { + // Match on name. + if (structKeyExists(variables.interceptorCache.name, interceptedBeanName)) + { + for (interceptDefinition in variables.interceptorCache.name[interceptedBeanName]) + { + arrayAppend(interceptors, {bean = getBean(interceptDefinition.name), methods = interceptDefinition.methods}); + } + } + } + + + // Match on regex. Ensure we only attach each one time. + if (arrayLen(variables.interceptorCache.regex)) + { + for (interceptDefinition in variables.interceptorCache.regex) + { + for (interceptedBeanName in beanNames) + { + if (reFindNoCase(interceptDefinition.regex, interceptedBeanName)) + { + arrayAppend(interceptors, {bean = getBean(interceptDefinition.name), methods = interceptDefinition.methods}); + break; + } + } + } + } + + + // Grab all type based interceptors that match. + if (arrayLen(variables.interceptorCache.type)) + { + beanTypes = getBeanTypes(arguments.dottedPath); + + for (interceptDefinition in variables.interceptorCache.type) + { + if (listFindNoCase(beanTypes, interceptDefinition.type)) + { + arrayAppend(interceptors, {bean = getBean(interceptDefinition.name), methods = interceptDefinition.methods}); + } + } + } + + + return interceptors; + } + + + /** Determines if the bean has interceptor definitions associated with it. */ + private boolean function hasInterceptors(string dottedPath) + { + var interceptedBeanName = ""; + var interceptorDefinition = {}; + var beanName = listLast(arguments.dottedPath, "."); + var beanNames = getAliases(beanName); + var beanTypes = ""; + + + arrayPrepend(beanNames, beanName); + + + for (interceptedBeanName in beanNames) + { + // Look for matches on name first. + if (structKeyExists(variables.interceptorCache.name, interceptedBeanName)) + { + return true; + } + + + // Look for matches on regex. + if (arrayLen(variables.interceptorCache.regex)) + { + for (interceptorDefinition in variables.interceptorCache.regex) + { + if (reFindNoCase(interceptorDefinition.regex, interceptedBeanName)) + { + return true; + } + } + } + + + // Look for matches by bean type. + if (arrayLen(variables.interceptorCache.type)) + { + beanTypes = getBeanTypes(arguments.dottedPath); + + for (interceptorDefinition in variables.interceptorCache.type) + { + if (listFindNoCase(beanTypes, interceptorDefinition.type)) + { + return true; + } + } + } + } + + + return false; + } + + + /** Finds all aliases for the given beanName. */ + private array function getAliases(string beanName) + { + var aliases = []; + var beanData = ""; + var key = ""; + + + if (structKeyExists(variables.beanInfo, arguments.beanName)) + { + beanData = variables.beanInfo[arguments.beanName]; + + for (key in variables.beanInfo) + { + // Same cfc dotted path, must be an alias. + if ( + key != arguments.beanName && + structKeyExists(variables.beanInfo[key], "cfc") && + structKeyExists(variables.beanInfo[arguments.beanName], "cfc") && + variables.beanInfo[key].cfc == variables.beanInfo[arguments.beanName].cfc) + { + arrayAppend(aliases, key); + } + } + } + + return aliases; + } + + + /** Returns a list of bean types (both name and dotted path) for a given bean. */ + private string function getBeanTypes(string dottedPath) + { + var beanTypes = ""; + var metadata = getComponentMetadata(arguments.dottedPath); + + while (!len(beanTypes) || structKeyExists(metadata, "extends")) + { + beanTypes = listAppend(beanTypes, listLast(metadata.name, ".")); + beanTypes = listAppend(beanTypes, metadata.name); + + if (structKeyExists(metadata, "extends")) + { + metadata = metadata.extends; + } + } + + return beanTypes; + } + + + /** Loads an array of interceptor definitions into the interceptor definition cache. */ + private void function loadInterceptors(array interceptors) + { + var interceptor = false; + + for (interceptor in interceptors) + { + if (structKeyExists(interceptor, "beanName")) + { + intercept(argumentCollection = interceptor); + } + else + { + interceptByType(argumentCollection = interceptor); + } + } + } + + + private void function setupFrameworkDefaults() + { + super.setupFrameworkDefaults(); + variables.config.version = variables._aop1_version & " (" & variables._di1_version & ")"; + } +} diff --git a/framework/beanProxy.cfc b/framework/beanProxy.cfc old mode 100755 new mode 100644 index bc8e188a..f781a83c --- a/framework/beanProxy.cfc +++ b/framework/beanProxy.cfc @@ -1,936 +1,936 @@ -component { - variables._fw1_version = "4.0.0-SNAPSHOT"; - variables._aop1_version = "2.0.2-SNAPSHOT"; -/* - Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - - - - variables.afterInterceptors = []; - variables.aroundInterceptors = []; - variables.beforeInterceptors = []; - variables.errorInterceptors = []; - variables.interceptedMethods = ""; - variables.interceptID = createUUID(); - variables.preName = "___"; - variables.targetBean = ""; - variables.targetBeanPath = ""; - - - - - // -------------- // - // PUBLIC METHODS // - // -------------- // - - /** Constructor. */ - public any function init(required any bean, required array interceptors, required struct config) - { - variables.targetBean = arguments.bean; - - populateInterceptorCache(arguments.interceptors); - morphTargetBean(arguments.config); - morphProxy(arguments.config); - cleanVarScope(); - - return this; - } - - - /** Entry point for all publically accessible intercepted methods. */ - public any function onMissingMethod(string missingMethodName, struct missingMethodArguments = {}) - { - // Prevent infinite loop and make sure the method is publically accessible. - if (!structKeyExists(variables.targetBean, arguments.missingMethodName) && !structKeyExists(variables.targetBean, variables.preName & arguments.missingMethodName)) - { - objectName = listLast(getMetadata(this).name, "."); - throw( message="Unable to locate method in (" & objectName & ").", - detail="The method (" & arguments.missingMethodName & ") could not be found. Please verify the method exists and is publically accessible."); - } - - - local.result = runStacks(arguments.missingMethodName, arguments.missingMethodArguments); - - if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; - } - - - /** Runs all the interceptor stacks. */ - public any function runStacks(string methodName, struct args = {}) - { - var objectName = ""; - - - // Prevent infinite loop and make sure the method exists (public or private) - if (!variables.targetBean.$methodExists(arguments.methodName) && !variables.targetBean.$methodExists(variables.preName & arguments.methodName)) - { - objectName = listLast(getMetadata(this).name, "."); - throw(message="Unable to locate method in (" & objectName & ").", detail="The method (" & arguments.methodName & ") could not be found."); - } - - - try - { - // Intercepted method call - if (variables.interceptedMethods == "*" || listFindNoCase(variables.interceptedMethods, arguments.methodName)) - { - runBeforeStack(arguments.methodName, arguments.args); - local.result = runAroundStack(arguments.methodName, arguments.args); - local.result = runAfterStack(arguments.methodName, arguments.args, !structKeyExists(local, "result") || isNull(local.result) ? javacast("null", 0) : local.result); - } - - // Non-intercepted method call - else - { - local.result = variables.targetBean.$call(arguments.methodName, arguments.args); - } - } - catch (any exception) - { - if (arrayLen(variables.errorInterceptors)) - { - runOnErrorStack(arguments.methodName, arguments.args, exception); - } - else - { - rethrow; - } - } - - - if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; - } - - - - - // --------------- // - // PRIVATE METHODS // - // --------------- // - - // --- Interceptor Augmentation Methods --- // - - /** Used to setup intercepted method lists on a per bean basis. */ - public any function _addInterceptedMethods(required string interceptID, required string methods) - { - var interceptedMethods = ""; - var methodName = ""; - - - if (!structKeyExists(variables, "interceptedMethods")) - { - variables.interceptedMethods = {}; - } - - if (!structKeyExists(variables.interceptedMethods, arguments.interceptID)) - { - variables.interceptedMethods[arguments.interceptID] = ""; - } - - interceptedMethods = variables.interceptedMethods[arguments.interceptID]; - - - if (interceptedMethods != "*") - { - if (arguments.methods == "" || arguments.methods == "*") - { - variables.interceptedMethods[arguments.interceptID] = "*"; - } - else - { - for (methodName in listToArray(arguments.methods)) - { - if (!listFindNoCase(variables.interceptedMethods[arguments.interceptID], methodName)) - { - interceptedMethods = listAppend(interceptedMethods, methodName); - } - } - - - interceptedMethods = listSort(interceptedMethods, "textnocase"); - variables.interceptedMethods[arguments.interceptID] = interceptedMethods; - } - } - } - - - /** Used to setup intercepted method lists on a per bean basis. */ - public any function _getInterceptedMethods(string interceptID) - { - var methods = {}; - - if (structKeyExists(variables, "interceptedMethods")) - { - methods = variables.interceptedMethods; - } - - if (!structKeyExists(arguments, "interceptID")) - { - return methods; - } - - - if (structKeyExists(methods, arguments.interceptID)) - { - return methods[arguments.interceptID]; - } - - return ""; - } - - - /** Used to inject methods and data. */ - public any function _inject(required string key, required any value, required string access="public") - { - if (arguments.access == "public") - { - this[arguments.key] = arguments.value; - } - - variables[arguments.key] = arguments.value; - } - - - /** Determines if an around interceptor is the last in the call chain. */ - public boolean function _isLast() - { - return isSimpleValue(variables.nextInterceptor); - } - - - /** Runs the 'Around' method, skips to the next interceptor in the chain if the 'Around' should not be run, or calls the actual method. */ - public any function _preAround(required any targetBean, required string methodName, struct args = {}) - { - var interceptedMethods = getInterceptedMethods(arguments.targetBean.interceptID); - - // Match if method is to be intercepted by this interceptor. - if (interceptedMethods == "*" || listFindNoCase(interceptedMethods, arguments.methodName)) - { - local.result = around(arguments.targetBean, arguments.methodName, arguments.args); - } - else - { - local.result = proceed(arguments.targetBean, arguments.methodName, arguments.args); - } - - if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; - } - - - /** Runs the next around interceptor or processes the method if it is the final interceptor in the call chain. */ - public any function _proceed(required any targetBean, required string methodName, struct args = {}) - { - if (isLast()) - { - local.result = arguments.targetBean.$call(arguments.methodName, arguments.args, true); - } - else - { - local.result = variables.nextInterceptor.preAround(arguments.targetBean, arguments.methodName, arguments.args); - } - - - if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; - } - - - /** Helper method for use inside of (after, around, before) to translate position based 'args' into name based. */ - private any function _translateArgs(required any targetBean, required string methodName, required struct args, boolean replace = false) - { - var i = 1; - var key = ""; - var argumentInfo = arguments.targetBean.$getArgumentInfo(arguments.methodName); - var resultArgs = {}; - - if (structIsEmpty(arguments.args) || !structKeyExists(arguments.args, "1")) - { - return arguments.args; - } - - for (i = 1; i <= arrayLen(argumentInfo); i++) - { - resultArgs[argumentInfo[i].name] = arguments.args[i]; - } - - if (arguments.replace) - { - structAppend(arguments.args, resultArgs, true); - - for (key in arguments.args) - { - if (isNumeric(key)) - { - structDelete(arguments.args, key); - } - } - } - - return resultArgs; - } - - - - - // --- Target Bean and Proxy Bean Augmentation Methods --- // - - /** Runs the appropriate method on the target bean. */ - private any function $call(required string methodName, struct args = {}, boolean original = false) - { - if (arguments.original) - { - local.result = evaluate(variables.preName & arguments.methodName & "(argumentCollection = arguments.args)"); - } - else - { - local.result = evaluate(arguments.methodName & "(argumentCollection = arguments.args)"); - } - - if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; - } - - - /** Used to replace any 'private' methods on the target bean that are being intercepted. Creates an intercept point. */ - public any function $callPrivateMethod() - { - local.methodName = getFunctionCalledName(); - local.result = $callStacks(local.methodName, arguments); - - if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; - } - - - /** Used to replace any 'public' methods on the target bean that are being intercepted. Creates an intercept point. */ - public any function $callPublicMethod() - { - local.methodName = getFunctionCalledName(); - local.result = $callStacks(local.methodName, arguments); - - if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; - } - - - /** Method called by the intercept points to start the stack run if needed. */ - private any function $callStacks(string methodName, struct args = {}) - { - local.result = variables.beanProxy.runStacks(arguments.methodName, arguments.args); - - if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; - } - - - /** Gets arguments information for a method. */ - private array function $getArgumentInfo(required string methodName) - { - var method = ""; - var methodMetadata = ""; - - - if (structKeyExists(this, variables.preName & arguments.methodName)) - { - method = this[variables.preName & arguments.methodName]; - } - else if (structKeyExists(this, arguments.methodName)) - { - method = this[arguments.methodName]; - } - else if (structKeyExists(variables, variables.preName & arguments.methodName)) - { - method = variables[variables.preName & arguments.methodName]; - } - else if (structKeyExists(variables, arguments.methodName)) - { - method = variables[arguments.methodName]; - } - - - if (!isSimpleValue(method)) - { - methodMetadata = getMetadata(method); - - if (structKeyExists(methodMetadata, "parameters") && arrayLen(methodMetadata.parameters)) - { - return methodMetadata.parameters; - } - } - - - return []; - } - - - /** Runs the appropriate method on the target bean. */ - private boolean function $methodExists(required string methodName) - { - return structKeyExists(this, arguments.methodName) || structKeyExists(variables, arguments.methodName); - } - - - /** A pass through method placed on the proxy bean (used primarily for 'init', 'set..', and 'initMethod' on target bean). */ - private any function $passThrough() - { - local.methodName = getFunctionCalledName(); - local.result = evaluate("variables.targetBean." & local.methodName & "(argumentCollection = arguments)"); - - if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; - } - - - /** Used to inject methods on the target bean. */ - public any function $replaceMethod(required string methodName, required any implementedMethod, required string access="public") - { - var method = ""; - - if (arguments.access == "public") - { - method = this[arguments.methodName]; - - if (isCustomFunction(method)) - { - this[variables.preName & arguments.methodName] = this[arguments.methodName]; - this[arguments.methodName] = arguments.implementedMethod; - } - } - - method = variables[arguments.methodName]; - - if (isCustomFunction(method)) - { - variables[variables.preName & arguments.methodName] = variables[arguments.methodName]; - variables[arguments.methodName] = arguments.implementedMethod; - } - } - - - - - // --- Local Private Methods --- // - - /** Adds an interceptor definition and bean to the interceptor cache for the proxied bean. */ - private void function addInterceptor(required any interceptor) - { - // If someone decides to have an interceptor handle multiple interceptor types, go for it. - - if (hasAfterMethod(arguments.interceptor)) - { - arrayAppend(variables.afterInterceptors, arguments.interceptor); - } - - - if (hasAroundMethod(arguments.interceptor)) - { - arrayAppend(variables.aroundInterceptors, arguments.interceptor); - } - - - if (hasBeforeMethod(arguments.interceptor)) - { - arrayAppend(variables.beforeInterceptors, arguments.interceptor); - } - - - if (hasOnErrorMethod(arguments.interceptor)) - { - arrayAppend(variables.errorInterceptors, arguments.interceptor); - } - - - if (!structKeyExists(arguments.interceptor.bean, "interceptorAugmented")) - { - augmentInterceptor(arguments.interceptor); - } - - - // Maintain the list of intercepted methods. '*' and blank means all. - if (variables.interceptedMethods != "*") - { - if (!len(arguments.interceptor.methods) || arguments.interceptor.methods == "*") - { - variables.interceptedMethods = "*"; - } - else - { - variables.interceptedMethods = listSort(listAppend(variables.interceptedMethods, arguments.interceptor.methods), "textnocase"); - } - } - - - // Update the interceptor itself. - arguments.interceptor.bean.addInterceptedMethods(variables.interceptID, arguments.interceptor.methods); - } - - - /** Adds variables and methods needed by Around interceptors. */ - private void function augmentAroundInterceptor(required any interceptor) - { - var prevInterceptor = ""; - - - // Add additional methods for an around interceptor. - arguments.interceptor.bean._inject("isLast", _isLast); - arguments.interceptor.bean._inject("preAround", _preAround); - arguments.interceptor.bean._inject("proceed", _proceed); - arguments.interceptor.bean._inject("nextInterceptor", "", "private"); - - - // Add a link in the call chain from the previous interceptor to the one just added. - if (1 < arrayLen(variables.aroundInterceptors)) - { - prevInterceptor = variables.aroundInterceptors[arrayLen(variables.aroundInterceptors) - 1]; - - prevInterceptor.bean._inject = _inject; - - prevInterceptor.bean._inject("nextInterceptor", arguments.interceptor.bean, "private"); - - structDelete(prevInterceptor.bean, "_inject"); - } - } - - - /** Adds variables and methods needed by all interceptors. */ - private void function augmentInterceptor(required any interceptor) - { - var interceptorVarScope = ""; - - - if (!structKeyExists(arguments.interceptor, "methods") || !len(arguments.interceptor.methods)) - { - arguments.interceptor.methods = "*"; - } - - arguments.interceptor.bean._inject = _inject; - - arguments.interceptor.bean._inject("interceptorAugmented", true); - arguments.interceptor.bean._inject("addInterceptedMethods", _addInterceptedMethods); - arguments.interceptor.bean._inject("getInterceptedMethods", _getInterceptedMethods); - arguments.interceptor.bean._inject("translateArgs", _translateArgs, "private"); - - if (hasAroundMethod(arguments.interceptor)) - { - augmentAroundInterceptor(arguments.interceptor); - } - - structDelete(arguments.interceptor.bean, "_inject"); - } - - - /** Cleans up temporary methods from the variables scope. */ - private void function cleanVarScope() - { - var key = ""; - - for (key in variables) - { - if (left(key, 1) == "_" || left(key, 1) == "$") - { - structDelete(variables, key); - } - } - } - - - /** Returns whether a method's access is public or private. */ - private string function getMethodAccess(any method) - { - var access = "public"; - var methodMetadata = getMetadata(method); - - if (structKeyExists(methodMetadata, "access") && methodMetadata.access == "private") - { - access = "private"; - } - - return access; - } - - - /** Retrieves property and method info about the targetBean. */ - private struct function getTargetBeanMetadata(any beanMetadata) - { - var beanInfo = {accessors = false, methods = {}, name = "", properties = {}}; - var i = 0; - var method = {}; - var property = {}; - var tmpBeanInfo = {}; - - - if (isObject(arguments.beanMetadata)) - { - arguments.beanMetadata = getMetadata(arguments.beanMetadata); - } - - - if (structKeyExists(arguments.beanMetadata, "accessors")) - { - beanInfo.accessors = arguments.beanMetadata.accessors; - } - - - if (structKeyExists(arguments.beanMetadata, "name")) - { - beanInfo.name = arguments.beanMetadata.name; - } - - - // Gather method information. - if (structKeyExists(arguments.beanMetadata, "functions")) - { - // ACF 9 did NOT like using a for-in loop here. - for (i = 1; i <= arrayLen(arguments.beanMetadata.functions); i++) - { - method = arguments.beanMetadata.functions[i]; - beanInfo.methods[method.name] = {}; - - if (structKeyExists(method, "access")) - { - beanInfo.methods[method.name]["access"] = method.access; - } - } - } - - - // Gather property information. - if (structKeyExists(arguments.beanMetadata, "properties")) - { - // ACF 9 did NOT like using a for-in loop here. - for (i = 1; i <= arrayLen(arguments.beanMetadata.properties); i++) - { - property = arguments.beanMetadata.properties[i]; - beanInfo.properties[property.name] = {}; - - if (structKeyExists(property, "access")) - { - beanInfo.properties[property.name]["access"] = property.access; - } - } - } - - - // Handle 'extends' hierarchy info. - if (structKeyExists(arguments.beanMetadata, "extends")) - { - tmpBeanInfo = getTargetBeanMetadata(arguments.beanMetadata.extends); - structAppend(beanInfo.properties, tmpBeanInfo.properties); - structAppend(beanInfo.methods, tmpBeanInfo.methods); - } - - - return beanInfo; - } - - - /** Gathers all the method information for the targetBean. */ - private struct function getTargetBeanMethodInfo() - { - var beanInfo = getTargetBeanMetadata(variables.targetBean); - var key = ""; - var methodInfo = {}; - - - variables.targetBeanPath = beanInfo.name; - - - // Locate methods in 'this' scope of targetBean. - for (key in variables.targetBean) - { - if (!structKeyExists(methodInfo, key) && isCustomFunction(variables.targetBean[key])) - { - methodInfo[key] = {access = "public", discoveredIn = "this", isPropertyAccessor = false}; - } - } - - - // Locate any missing 'set' and 'get' methods only present in the metadata. - if (beanInfo.accessors) - { - for (key in beanInfo.methods) - { - if (!structKeyExists(methodInfo, key)) - { - methodInfo[key] = {access = beanInfo.methods[key].access, discoveredIn = "metadata", isPropertyAccessor = false}; - } - } - } - - - // Determine if any of the 'set' or 'get' methods match a property. - if (beanInfo.accessors) - { - for (key in beanInfo.properties) - { - if (structKeyExists(methodInfo, "set" & key)) - { - methodInfo["set" & key].isPropertyAccessor = true; - } - - - if (structKeyExists(methodInfo, "get" & key)) - { - methodInfo["get" & key].isPropertyAccessor = true; - } - } - } - - - return methodInfo; - } - - - /** Determines if an interceptor has an After method. */ - private boolean function hasAfterMethod(required any interceptor) - { - return structKeyExists(arguments.interceptor.bean, "after"); - } - - - /** Determines if an interceptor has an Around method. */ - private boolean function hasAroundMethod(required any interceptor) - { - return structKeyExists(arguments.interceptor.bean, "around"); - } - - - /** Determines if an interceptor has a Before method. */ - private boolean function hasBeforeMethod(required any interceptor) - { - return structKeyExists(arguments.interceptor.bean, "before"); - } - - - /** Determines if an interceptor has an onError method. */ - private boolean function hasOnErrorMethod(required any interceptor) - { - return structKeyExists(arguments.interceptor.bean, "onError"); - } - - - /** Determines if a 'methodName' is in a list of methods. A blank list of method matches will be an automatic match. */ - private boolean function methodMatches(string methodName, string matchers) - { - // Match on: 1) No matches provided 2) Method name in matchers - return !listLen(arguments.matchers) || arguments.matchers == "*" || listFindNoCase(arguments.matchers, arguments.methodName); - } - - - /** Alters the proxy bean so the factory still sees the set..(), init(), and initMethod() and so these methods get called on the target bean. */ - private void function morphProxy(required struct config) - { - var key = ""; - - // Handle the 'set...' methods. - for (key in variables.targetBean) - { - if (left(key, 3) == "set") - { - this[key] = $passThrough; - } - } - - - // Checks to see if the 'initMethod' was defined in the config and handles if it exists on the target bean. - if (structKeyExists(arguments.config, "initMethod") && len(arguments.config.initMethod) && structKeyExists(variables.targetBean, arguments.config.initMethod)) - { - this[arguments.config.initMethod] = $passThrough; - } - - - // Passes the init() if it exists, otherwise removes it. - if (structKeyExists(variables.targetBean, "init")) - { - this["init"] = $passThrough; - } - else - { - structDelete(this, "init"); - } - } - - - /** Alters the target bean by adding intercept points. */ - private void function morphTargetBean(required struct config) - { - var access = ""; - var beanMethodInfo = getTargetBeanMethodInfo(); - var initMethod = ""; - var key = ""; - var method = ""; - var methodInfo = ""; - - - if (structKeyExists(arguments.config, "initMethod")) - { - initMethod = arguments.config.initMethod; - } - - - variables.targetBean.$inject = _inject; - variables.targetBean.$replaceMethod = $replaceMethod; - - - // Setup internal variables and methods on the target bean. - variables.targetBean.$inject("beanProxy", this, "private"); - variables.targetBean.$inject("preName", variables.preName, "private"); - variables.targetBean.$inject("$callStacks", $callStacks, "private"); - variables.targetBean.$inject("$call", $call); - variables.targetBean.$inject("$methodExists", $methodExists); - variables.targetBean.$inject("$getArgumentInfo", $getArgumentInfo); - variables.targetBean.$inject("interceptID", variables.interceptID); - - - for (key in beanMethodInfo) - { - methodInfo = beanMethodInfo[key]; - - - // Only alter methods that should be intercepted. 'init()', accessors, and 'initMethod' are ignored unless specified in the methods list. - if ( - (variables.interceptedMethods == "*" && key != "init" && key != initMethod && !methodInfo.isPropertyAccessor) || - listFindNoCase(variables.interceptedMethods, key) - ) - { - // Handle methods listed in a scope. - if (listFindNoCase("this,variables", methodInfo.discoveredIn)) - { - // Handle methods found in 'this' scope. - if (methodInfo.access == "public") - { - variables.targetBean.$replaceMethod(key, $callPublicMethod); - } - - // Handle methods in the variables scope. - else - { - variables.targetBean.$replaceMethod(key, $callPublicMethod, "private"); - } - } - - - // Handle methods found only in the metadata. - else - { - try - { - if (methodInfo.access == "public") - { - variables.targetBean.$replaceMethod(key, $callPublicMethod); - } - else - { - variables.targetBean.$replaceMethod(key, $callPublicMethod, "private"); - } - } - catch (any exception) - { - throw(message="Unable to locate the method (" & key & ") on target bean (" & variables.targetBeanPath & ")."); - } - } - } - } - - - structDelete(variables.targetBean, "$inject"); - structDelete(variables.targetBean, "$replaceMethod"); - } - - - /** Adds an array of interceptor definitions to the interceptor definition cache. */ - private void function populateInterceptorCache(required array interceptors) - { - var interceptor = ""; - - for (interceptor in arguments.interceptors) - { - addInterceptor(interceptor); - } - } - - - private any function runAfterStack(string methodName, struct args, any result) - { - if (structKeyExists(arguments, "result") && !isNull(arguments.result)) - { - local.result = arguments.result; - } - - - for (local.interceptor in variables.afterInterceptors) - { - if (methodMatches(methodName, local.interceptor.methods)) - { - local.tempResult = local.interceptor.bean.after(variables.targetBean, arguments.methodName, args, isNull(arguments.result) ? javacast("null", 0) : arguments.result); - } - - if (structKeyExists(local, "tempResult")) - { - if (!isNull(local.tempResult)) - { - local.result = local.tempResult; - } - - structDelete(local, "tempResult"); - } - } - - - if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; - } - - - private any function runAroundStack(string methodName, struct args) - { - if (arrayLen(variables.aroundInterceptors)) - { - // Only need to call the first one in the chain to start the process. - local.result = variables.aroundInterceptors[1].bean.preAround(variables.targetBean, arguments.methodName, arguments.args); - } - else - { - local.result = variables.targetBean.$call(arguments.methodName, arguments.args, true); - } - - if (structKeyExists(local, "result") and !isNull(local.result)) return local.result; - } - - - private void function runBeforeStack(string methodName, struct args) - { - var inteceptor = ""; - - for (inteceptor in variables.beforeInterceptors) - { - if (structKeyExists(inteceptor.bean, "before")) - { - if (methodMatches(arguments.methodName, inteceptor.methods)) - { - inteceptor.bean.before(variables.targetBean, arguments.methodName, arguments.args); - } - } - } - } - - - private void function runOnErrorStack(string methodName, struct args, any exception) - { - var interceptor = ""; - - for (interceptor in variables.errorInterceptors) - { - if (methodMatches(arguments.methodName, interceptor.methods)) - { - interceptor.bean.onError(variables.targetBean, arguments.methodName, arguments.args, arguments.exception); - } - } - } -} +component { + variables._fw1_version = "4.0.0-SNAPSHOT"; + variables._aop1_version = "2.0.2-SNAPSHOT"; +/* + Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + + + + variables.afterInterceptors = []; + variables.aroundInterceptors = []; + variables.beforeInterceptors = []; + variables.errorInterceptors = []; + variables.interceptedMethods = ""; + variables.interceptID = createUUID(); + variables.preName = "___"; + variables.targetBean = ""; + variables.targetBeanPath = ""; + + + + + // -------------- // + // PUBLIC METHODS // + // -------------- // + + /** Constructor. */ + public any function init(required any bean, required array interceptors, required struct config) + { + variables.targetBean = arguments.bean; + + populateInterceptorCache(arguments.interceptors); + morphTargetBean(arguments.config); + morphProxy(arguments.config); + cleanVarScope(); + + return this; + } + + + /** Entry point for all publically accessible intercepted methods. */ + public any function onMissingMethod(string missingMethodName, struct missingMethodArguments = {}) + { + // Prevent infinite loop and make sure the method is publically accessible. + if (!structKeyExists(variables.targetBean, arguments.missingMethodName) && !structKeyExists(variables.targetBean, variables.preName & arguments.missingMethodName)) + { + objectName = listLast(getMetadata(this).name, "."); + throw( message="Unable to locate method in (" & objectName & ").", + detail="The method (" & arguments.missingMethodName & ") could not be found. Please verify the method exists and is publically accessible."); + } + + + local.result = runStacks(arguments.missingMethodName, arguments.missingMethodArguments); + + if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; + } + + + /** Runs all the interceptor stacks. */ + public any function runStacks(string methodName, struct args = {}) + { + var objectName = ""; + + + // Prevent infinite loop and make sure the method exists (public or private) + if (!variables.targetBean.$methodExists(arguments.methodName) && !variables.targetBean.$methodExists(variables.preName & arguments.methodName)) + { + objectName = listLast(getMetadata(this).name, "."); + throw(message="Unable to locate method in (" & objectName & ").", detail="The method (" & arguments.methodName & ") could not be found."); + } + + + try + { + // Intercepted method call + if (variables.interceptedMethods == "*" || listFindNoCase(variables.interceptedMethods, arguments.methodName)) + { + runBeforeStack(arguments.methodName, arguments.args); + local.result = runAroundStack(arguments.methodName, arguments.args); + local.result = runAfterStack(arguments.methodName, arguments.args, !structKeyExists(local, "result") || isNull(local.result) ? javacast("null", 0) : local.result); + } + + // Non-intercepted method call + else + { + local.result = variables.targetBean.$call(arguments.methodName, arguments.args); + } + } + catch (any exception) + { + if (arrayLen(variables.errorInterceptors)) + { + runOnErrorStack(arguments.methodName, arguments.args, exception); + } + else + { + rethrow; + } + } + + + if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; + } + + + + + // --------------- // + // PRIVATE METHODS // + // --------------- // + + // --- Interceptor Augmentation Methods --- // + + /** Used to setup intercepted method lists on a per bean basis. */ + public any function _addInterceptedMethods(required string interceptID, required string methods) + { + var interceptedMethods = ""; + var methodName = ""; + + + if (!structKeyExists(variables, "interceptedMethods")) + { + variables.interceptedMethods = {}; + } + + if (!structKeyExists(variables.interceptedMethods, arguments.interceptID)) + { + variables.interceptedMethods[arguments.interceptID] = ""; + } + + interceptedMethods = variables.interceptedMethods[arguments.interceptID]; + + + if (interceptedMethods != "*") + { + if (arguments.methods == "" || arguments.methods == "*") + { + variables.interceptedMethods[arguments.interceptID] = "*"; + } + else + { + for (methodName in listToArray(arguments.methods)) + { + if (!listFindNoCase(variables.interceptedMethods[arguments.interceptID], methodName)) + { + interceptedMethods = listAppend(interceptedMethods, methodName); + } + } + + + interceptedMethods = listSort(interceptedMethods, "textnocase"); + variables.interceptedMethods[arguments.interceptID] = interceptedMethods; + } + } + } + + + /** Used to setup intercepted method lists on a per bean basis. */ + public any function _getInterceptedMethods(string interceptID) + { + var methods = {}; + + if (structKeyExists(variables, "interceptedMethods")) + { + methods = variables.interceptedMethods; + } + + if (!structKeyExists(arguments, "interceptID")) + { + return methods; + } + + + if (structKeyExists(methods, arguments.interceptID)) + { + return methods[arguments.interceptID]; + } + + return ""; + } + + + /** Used to inject methods and data. */ + public any function _inject(required string key, required any value, required string access="public") + { + if (arguments.access == "public") + { + this[arguments.key] = arguments.value; + } + + variables[arguments.key] = arguments.value; + } + + + /** Determines if an around interceptor is the last in the call chain. */ + public boolean function _isLast() + { + return isSimpleValue(variables.nextInterceptor); + } + + + /** Runs the 'Around' method, skips to the next interceptor in the chain if the 'Around' should not be run, or calls the actual method. */ + public any function _preAround(required any targetBean, required string methodName, struct args = {}) + { + var interceptedMethods = getInterceptedMethods(arguments.targetBean.interceptID); + + // Match if method is to be intercepted by this interceptor. + if (interceptedMethods == "*" || listFindNoCase(interceptedMethods, arguments.methodName)) + { + local.result = around(arguments.targetBean, arguments.methodName, arguments.args); + } + else + { + local.result = proceed(arguments.targetBean, arguments.methodName, arguments.args); + } + + if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; + } + + + /** Runs the next around interceptor or processes the method if it is the final interceptor in the call chain. */ + public any function _proceed(required any targetBean, required string methodName, struct args = {}) + { + if (isLast()) + { + local.result = arguments.targetBean.$call(arguments.methodName, arguments.args, true); + } + else + { + local.result = variables.nextInterceptor.preAround(arguments.targetBean, arguments.methodName, arguments.args); + } + + + if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; + } + + + /** Helper method for use inside of (after, around, before) to translate position based 'args' into name based. */ + private any function _translateArgs(required any targetBean, required string methodName, required struct args, boolean replace = false) + { + var i = 1; + var key = ""; + var argumentInfo = arguments.targetBean.$getArgumentInfo(arguments.methodName); + var resultArgs = {}; + + if (structIsEmpty(arguments.args) || !structKeyExists(arguments.args, "1")) + { + return arguments.args; + } + + for (i = 1; i <= arrayLen(argumentInfo); i++) + { + resultArgs[argumentInfo[i].name] = arguments.args[i]; + } + + if (arguments.replace) + { + structAppend(arguments.args, resultArgs, true); + + for (key in arguments.args) + { + if (isNumeric(key)) + { + structDelete(arguments.args, key); + } + } + } + + return resultArgs; + } + + + + + // --- Target Bean and Proxy Bean Augmentation Methods --- // + + /** Runs the appropriate method on the target bean. */ + private any function $call(required string methodName, struct args = {}, boolean original = false) + { + if (arguments.original) + { + local.result = evaluate(variables.preName & arguments.methodName & "(argumentCollection = arguments.args)"); + } + else + { + local.result = evaluate(arguments.methodName & "(argumentCollection = arguments.args)"); + } + + if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; + } + + + /** Used to replace any 'private' methods on the target bean that are being intercepted. Creates an intercept point. */ + public any function $callPrivateMethod() + { + local.methodName = getFunctionCalledName(); + local.result = $callStacks(local.methodName, arguments); + + if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; + } + + + /** Used to replace any 'public' methods on the target bean that are being intercepted. Creates an intercept point. */ + public any function $callPublicMethod() + { + local.methodName = getFunctionCalledName(); + local.result = $callStacks(local.methodName, arguments); + + if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; + } + + + /** Method called by the intercept points to start the stack run if needed. */ + private any function $callStacks(string methodName, struct args = {}) + { + local.result = variables.beanProxy.runStacks(arguments.methodName, arguments.args); + + if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; + } + + + /** Gets arguments information for a method. */ + private array function $getArgumentInfo(required string methodName) + { + var method = ""; + var methodMetadata = ""; + + + if (structKeyExists(this, variables.preName & arguments.methodName)) + { + method = this[variables.preName & arguments.methodName]; + } + else if (structKeyExists(this, arguments.methodName)) + { + method = this[arguments.methodName]; + } + else if (structKeyExists(variables, variables.preName & arguments.methodName)) + { + method = variables[variables.preName & arguments.methodName]; + } + else if (structKeyExists(variables, arguments.methodName)) + { + method = variables[arguments.methodName]; + } + + + if (!isSimpleValue(method)) + { + methodMetadata = getMetadata(method); + + if (structKeyExists(methodMetadata, "parameters") && arrayLen(methodMetadata.parameters)) + { + return methodMetadata.parameters; + } + } + + + return []; + } + + + /** Runs the appropriate method on the target bean. */ + private boolean function $methodExists(required string methodName) + { + return structKeyExists(this, arguments.methodName) || structKeyExists(variables, arguments.methodName); + } + + + /** A pass through method placed on the proxy bean (used primarily for 'init', 'set..', and 'initMethod' on target bean). */ + private any function $passThrough() + { + local.methodName = getFunctionCalledName(); + local.result = evaluate("variables.targetBean." & local.methodName & "(argumentCollection = arguments)"); + + if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; + } + + + /** Used to inject methods on the target bean. */ + public any function $replaceMethod(required string methodName, required any implementedMethod, required string access="public") + { + var method = ""; + + if (arguments.access == "public") + { + method = this[arguments.methodName]; + + if (isCustomFunction(method)) + { + this[variables.preName & arguments.methodName] = this[arguments.methodName]; + this[arguments.methodName] = arguments.implementedMethod; + } + } + + method = variables[arguments.methodName]; + + if (isCustomFunction(method)) + { + variables[variables.preName & arguments.methodName] = variables[arguments.methodName]; + variables[arguments.methodName] = arguments.implementedMethod; + } + } + + + + + // --- Local Private Methods --- // + + /** Adds an interceptor definition and bean to the interceptor cache for the proxied bean. */ + private void function addInterceptor(required any interceptor) + { + // If someone decides to have an interceptor handle multiple interceptor types, go for it. + + if (hasAfterMethod(arguments.interceptor)) + { + arrayAppend(variables.afterInterceptors, arguments.interceptor); + } + + + if (hasAroundMethod(arguments.interceptor)) + { + arrayAppend(variables.aroundInterceptors, arguments.interceptor); + } + + + if (hasBeforeMethod(arguments.interceptor)) + { + arrayAppend(variables.beforeInterceptors, arguments.interceptor); + } + + + if (hasOnErrorMethod(arguments.interceptor)) + { + arrayAppend(variables.errorInterceptors, arguments.interceptor); + } + + + if (!structKeyExists(arguments.interceptor.bean, "interceptorAugmented")) + { + augmentInterceptor(arguments.interceptor); + } + + + // Maintain the list of intercepted methods. '*' and blank means all. + if (variables.interceptedMethods != "*") + { + if (!len(arguments.interceptor.methods) || arguments.interceptor.methods == "*") + { + variables.interceptedMethods = "*"; + } + else + { + variables.interceptedMethods = listSort(listAppend(variables.interceptedMethods, arguments.interceptor.methods), "textnocase"); + } + } + + + // Update the interceptor itself. + arguments.interceptor.bean.addInterceptedMethods(variables.interceptID, arguments.interceptor.methods); + } + + + /** Adds variables and methods needed by Around interceptors. */ + private void function augmentAroundInterceptor(required any interceptor) + { + var prevInterceptor = ""; + + + // Add additional methods for an around interceptor. + arguments.interceptor.bean._inject("isLast", _isLast); + arguments.interceptor.bean._inject("preAround", _preAround); + arguments.interceptor.bean._inject("proceed", _proceed); + arguments.interceptor.bean._inject("nextInterceptor", "", "private"); + + + // Add a link in the call chain from the previous interceptor to the one just added. + if (1 < arrayLen(variables.aroundInterceptors)) + { + prevInterceptor = variables.aroundInterceptors[arrayLen(variables.aroundInterceptors) - 1]; + + prevInterceptor.bean._inject = _inject; + + prevInterceptor.bean._inject("nextInterceptor", arguments.interceptor.bean, "private"); + + structDelete(prevInterceptor.bean, "_inject"); + } + } + + + /** Adds variables and methods needed by all interceptors. */ + private void function augmentInterceptor(required any interceptor) + { + var interceptorVarScope = ""; + + + if (!structKeyExists(arguments.interceptor, "methods") || !len(arguments.interceptor.methods)) + { + arguments.interceptor.methods = "*"; + } + + arguments.interceptor.bean._inject = _inject; + + arguments.interceptor.bean._inject("interceptorAugmented", true); + arguments.interceptor.bean._inject("addInterceptedMethods", _addInterceptedMethods); + arguments.interceptor.bean._inject("getInterceptedMethods", _getInterceptedMethods); + arguments.interceptor.bean._inject("translateArgs", _translateArgs, "private"); + + if (hasAroundMethod(arguments.interceptor)) + { + augmentAroundInterceptor(arguments.interceptor); + } + + structDelete(arguments.interceptor.bean, "_inject"); + } + + + /** Cleans up temporary methods from the variables scope. */ + private void function cleanVarScope() + { + var key = ""; + + for (key in variables) + { + if (left(key, 1) == "_" || left(key, 1) == "$") + { + structDelete(variables, key); + } + } + } + + + /** Returns whether a method's access is public or private. */ + private string function getMethodAccess(any method) + { + var access = "public"; + var methodMetadata = getMetadata(method); + + if (structKeyExists(methodMetadata, "access") && methodMetadata.access == "private") + { + access = "private"; + } + + return access; + } + + + /** Retrieves property and method info about the targetBean. */ + private struct function getTargetBeanMetadata(any beanMetadata) + { + var beanInfo = {accessors = false, methods = {}, name = "", properties = {}}; + var i = 0; + var method = {}; + var property = {}; + var tmpBeanInfo = {}; + + + if (isObject(arguments.beanMetadata)) + { + arguments.beanMetadata = getMetadata(arguments.beanMetadata); + } + + + if (structKeyExists(arguments.beanMetadata, "accessors")) + { + beanInfo.accessors = arguments.beanMetadata.accessors; + } + + + if (structKeyExists(arguments.beanMetadata, "name")) + { + beanInfo.name = arguments.beanMetadata.name; + } + + + // Gather method information. + if (structKeyExists(arguments.beanMetadata, "functions")) + { + // ACF 9 did NOT like using a for-in loop here. + for (i = 1; i <= arrayLen(arguments.beanMetadata.functions); i++) + { + method = arguments.beanMetadata.functions[i]; + beanInfo.methods[method.name] = {}; + + if (structKeyExists(method, "access")) + { + beanInfo.methods[method.name]["access"] = method.access; + } + } + } + + + // Gather property information. + if (structKeyExists(arguments.beanMetadata, "properties")) + { + // ACF 9 did NOT like using a for-in loop here. + for (i = 1; i <= arrayLen(arguments.beanMetadata.properties); i++) + { + property = arguments.beanMetadata.properties[i]; + beanInfo.properties[property.name] = {}; + + if (structKeyExists(property, "access")) + { + beanInfo.properties[property.name]["access"] = property.access; + } + } + } + + + // Handle 'extends' hierarchy info. + if (structKeyExists(arguments.beanMetadata, "extends")) + { + tmpBeanInfo = getTargetBeanMetadata(arguments.beanMetadata.extends); + structAppend(beanInfo.properties, tmpBeanInfo.properties); + structAppend(beanInfo.methods, tmpBeanInfo.methods); + } + + + return beanInfo; + } + + + /** Gathers all the method information for the targetBean. */ + private struct function getTargetBeanMethodInfo() + { + var beanInfo = getTargetBeanMetadata(variables.targetBean); + var key = ""; + var methodInfo = {}; + + + variables.targetBeanPath = beanInfo.name; + + + // Locate methods in 'this' scope of targetBean. + for (key in variables.targetBean) + { + if (!structKeyExists(methodInfo, key) && isCustomFunction(variables.targetBean[key])) + { + methodInfo[key] = {access = "public", discoveredIn = "this", isPropertyAccessor = false}; + } + } + + + // Locate any missing 'set' and 'get' methods only present in the metadata. + if (beanInfo.accessors) + { + for (key in beanInfo.methods) + { + if (!structKeyExists(methodInfo, key)) + { + methodInfo[key] = {access = beanInfo.methods[key].access, discoveredIn = "metadata", isPropertyAccessor = false}; + } + } + } + + + // Determine if any of the 'set' or 'get' methods match a property. + if (beanInfo.accessors) + { + for (key in beanInfo.properties) + { + if (structKeyExists(methodInfo, "set" & key)) + { + methodInfo["set" & key].isPropertyAccessor = true; + } + + + if (structKeyExists(methodInfo, "get" & key)) + { + methodInfo["get" & key].isPropertyAccessor = true; + } + } + } + + + return methodInfo; + } + + + /** Determines if an interceptor has an After method. */ + private boolean function hasAfterMethod(required any interceptor) + { + return structKeyExists(arguments.interceptor.bean, "after"); + } + + + /** Determines if an interceptor has an Around method. */ + private boolean function hasAroundMethod(required any interceptor) + { + return structKeyExists(arguments.interceptor.bean, "around"); + } + + + /** Determines if an interceptor has a Before method. */ + private boolean function hasBeforeMethod(required any interceptor) + { + return structKeyExists(arguments.interceptor.bean, "before"); + } + + + /** Determines if an interceptor has an onError method. */ + private boolean function hasOnErrorMethod(required any interceptor) + { + return structKeyExists(arguments.interceptor.bean, "onError"); + } + + + /** Determines if a 'methodName' is in a list of methods. A blank list of method matches will be an automatic match. */ + private boolean function methodMatches(string methodName, string matchers) + { + // Match on: 1) No matches provided 2) Method name in matchers + return !listLen(arguments.matchers) || arguments.matchers == "*" || listFindNoCase(arguments.matchers, arguments.methodName); + } + + + /** Alters the proxy bean so the factory still sees the set..(), init(), and initMethod() and so these methods get called on the target bean. */ + private void function morphProxy(required struct config) + { + var key = ""; + + // Handle the 'set...' methods. + for (key in variables.targetBean) + { + if (left(key, 3) == "set") + { + this[key] = $passThrough; + } + } + + + // Checks to see if the 'initMethod' was defined in the config and handles if it exists on the target bean. + if (structKeyExists(arguments.config, "initMethod") && len(arguments.config.initMethod) && structKeyExists(variables.targetBean, arguments.config.initMethod)) + { + this[arguments.config.initMethod] = $passThrough; + } + + + // Passes the init() if it exists, otherwise removes it. + if (structKeyExists(variables.targetBean, "init")) + { + this["init"] = $passThrough; + } + else + { + structDelete(this, "init"); + } + } + + + /** Alters the target bean by adding intercept points. */ + private void function morphTargetBean(required struct config) + { + var access = ""; + var beanMethodInfo = getTargetBeanMethodInfo(); + var initMethod = ""; + var key = ""; + var method = ""; + var methodInfo = ""; + + + if (structKeyExists(arguments.config, "initMethod")) + { + initMethod = arguments.config.initMethod; + } + + + variables.targetBean.$inject = _inject; + variables.targetBean.$replaceMethod = $replaceMethod; + + + // Setup internal variables and methods on the target bean. + variables.targetBean.$inject("beanProxy", this, "private"); + variables.targetBean.$inject("preName", variables.preName, "private"); + variables.targetBean.$inject("$callStacks", $callStacks, "private"); + variables.targetBean.$inject("$call", $call); + variables.targetBean.$inject("$methodExists", $methodExists); + variables.targetBean.$inject("$getArgumentInfo", $getArgumentInfo); + variables.targetBean.$inject("interceptID", variables.interceptID); + + + for (key in beanMethodInfo) + { + methodInfo = beanMethodInfo[key]; + + + // Only alter methods that should be intercepted. 'init()', accessors, and 'initMethod' are ignored unless specified in the methods list. + if ( + (variables.interceptedMethods == "*" && key != "init" && key != initMethod && !methodInfo.isPropertyAccessor) || + listFindNoCase(variables.interceptedMethods, key) + ) + { + // Handle methods listed in a scope. + if (listFindNoCase("this,variables", methodInfo.discoveredIn)) + { + // Handle methods found in 'this' scope. + if (methodInfo.access == "public") + { + variables.targetBean.$replaceMethod(key, $callPublicMethod); + } + + // Handle methods in the variables scope. + else + { + variables.targetBean.$replaceMethod(key, $callPublicMethod, "private"); + } + } + + + // Handle methods found only in the metadata. + else + { + try + { + if (methodInfo.access == "public") + { + variables.targetBean.$replaceMethod(key, $callPublicMethod); + } + else + { + variables.targetBean.$replaceMethod(key, $callPublicMethod, "private"); + } + } + catch (any exception) + { + throw(message="Unable to locate the method (" & key & ") on target bean (" & variables.targetBeanPath & ")."); + } + } + } + } + + + structDelete(variables.targetBean, "$inject"); + structDelete(variables.targetBean, "$replaceMethod"); + } + + + /** Adds an array of interceptor definitions to the interceptor definition cache. */ + private void function populateInterceptorCache(required array interceptors) + { + var interceptor = ""; + + for (interceptor in arguments.interceptors) + { + addInterceptor(interceptor); + } + } + + + private any function runAfterStack(string methodName, struct args, any result) + { + if (structKeyExists(arguments, "result") && !isNull(arguments.result)) + { + local.result = arguments.result; + } + + + for (local.interceptor in variables.afterInterceptors) + { + if (methodMatches(methodName, local.interceptor.methods)) + { + local.tempResult = local.interceptor.bean.after(variables.targetBean, arguments.methodName, args, isNull(arguments.result) ? javacast("null", 0) : arguments.result); + } + + if (structKeyExists(local, "tempResult")) + { + if (!isNull(local.tempResult)) + { + local.result = local.tempResult; + } + + structDelete(local, "tempResult"); + } + } + + + if (structKeyExists(local, "result") && !isNull(local.result)) return local.result; + } + + + private any function runAroundStack(string methodName, struct args) + { + if (arrayLen(variables.aroundInterceptors)) + { + // Only need to call the first one in the chain to start the process. + local.result = variables.aroundInterceptors[1].bean.preAround(variables.targetBean, arguments.methodName, arguments.args); + } + else + { + local.result = variables.targetBean.$call(arguments.methodName, arguments.args, true); + } + + if (structKeyExists(local, "result") and !isNull(local.result)) return local.result; + } + + + private void function runBeforeStack(string methodName, struct args) + { + var inteceptor = ""; + + for (inteceptor in variables.beforeInterceptors) + { + if (structKeyExists(inteceptor.bean, "before")) + { + if (methodMatches(arguments.methodName, inteceptor.methods)) + { + inteceptor.bean.before(variables.targetBean, arguments.methodName, arguments.args); + } + } + } + } + + + private void function runOnErrorStack(string methodName, struct args, any exception) + { + var interceptor = ""; + + for (interceptor in variables.errorInterceptors) + { + if (methodMatches(arguments.methodName, interceptor.methods)) + { + interceptor.bean.onError(variables.targetBean, arguments.methodName, arguments.args, arguments.exception); + } + } + } +} From 5df6e633df6ee98ca30fe5deffe4bd14bb5c7055 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 7 Sep 2016 18:36:55 -0700 Subject: [PATCH 096/100] Integrate callClojure() extension point #448 Merged from World Singles enhancement. --- framework/cljcontroller.cfc | 7 +++++-- framework/ioclj.cfc | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index cb6c2fdc..b4cd05d5 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -29,7 +29,7 @@ component { var rc = missingMethodArguments.rc; try { var rcClj = variables.cfmljure.toClojure( rc ); - var rawResult = evaluate( "variables.ns.#missingMethodName#( rcClj )" ); + var rawResult = callClojure( missingMethodName, rcClj ); var result = variables.cfmljure.toCFML( rawResult ); structClear( rc ); structAppend( rc, result ); @@ -52,7 +52,6 @@ component { if ( structKeyExists( rc, "render" ) && isStruct( rc.render ) && structKeyExists( rc.render, "type" ) && structKeyExists( rc.render, "data" ) ) { if ( isObject( variables.fw ) ) { - var core = variables.cfmljure.clojure.core; var walk = variables.cfmljure.clojure.walk; var renderer = variables.fw.renderData( core.name( rc.render["type"] ), @@ -97,4 +96,8 @@ component { function __dummy() { } + function callClojure( string qualifiedName, any rcClj ) { + return evaluate( "variables.ns.#qualifiedName#( rcClj )" ); + } + } diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index 4a93bd57..fe972c48 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -46,6 +46,7 @@ component extends=framework.ioc { if ( structKeyExists( config, "noClojure" ) && config.noClojure ) return; var lein = structKeyExists( config, "lein" ) ? config.lein : "lein"; var boot = structKeyExists( config, "boot" ) ? config.boot : ""; // default is not Boot + var cljcontroller = structKeyExists( config, "cljcontroller" ) ? config.cljcontroller : "framework.cljcontroller"; // find the first folder that includes project.clj (or build.boot) - that's our project variables.project = findProjectFile( len( boot ) ? "build.boot" : "project.clj" ); discoverClojureFiles(); @@ -93,7 +94,7 @@ component extends=framework.ioc { ( containsBean( "fw1" ) ? getBean( "fw1" ) : ( containsBean( "framework" ) ? getBean( "framework" ) : "" ) ); - var controller = new framework.cljcontroller( + var controller = new "#cljcontroller#"( fw, variables.cfmljure, ns ); bean = controller; From 57f336db676c7e1ee3ac034dfe5b918e966b207d Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Wed, 14 Sep 2016 14:34:27 -0700 Subject: [PATCH 097/100] Improve debuggability of calling Clojure from CFML --- framework/cfmljure.cfc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index 55f597ab..9c010e1c 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -24,11 +24,11 @@ component { string boot = "", // to allow Boot to be selected instead string ns = "", any root = 0 ) { variables.refCache = { }; + var javaLangSystem = createObject( "java", "java.lang.System" ); + variables.out = javaLangSystem.out; if ( project != "" ) { variables._clj_root = this; variables._clj_ns = ""; - var javaLangSystem = createObject( "java", "java.lang.System" ); - variables.out = javaLangSystem.out; var nl = javaLangSystem.getProperty( "line.separator" ); var fs = javaLangSystem.getProperty( "file.separator" ); var nixLike = fs == "/"; @@ -395,7 +395,11 @@ component { if ( left( missingMethodName, 1 ) == "_" ) { return __( right( missingMethodName, len( missingMethodName ) - 1 ), true ); } else { - return __call( __( missingMethodName, false ), missingMethodArguments ); + var clj_var = __( missingMethodName, false ); + if ( isNull( clj_var ) ) { + throw "Unable to resolve #variables._clj_ns#/#missingMethodName#"; + } + return __call( clj_var, missingMethodArguments ); } } From afce80a99a17f5a0ea22a7554496a2fa4c0c1567 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 16 Sep 2016 17:44:40 -0700 Subject: [PATCH 098/100] Ignore server.json for local dev/testing --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 61cc0f2d..69569d9c 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ settings.xml run-tests.sh /examples/6helloclojure/target/stale/extract-native.dependencies /taskmanager/ +/server.json From 775c5b71ce7e0a9624f1da6a8014da7a440ca950 Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 16 Sep 2016 18:00:55 -0700 Subject: [PATCH 099/100] Address #449 by not caching bean in accumulator This modifies `resolveBeanCreate()` to no longer cache the bean in the accumulator and instead return a fresh structure each time (whilst still caching the other metadata from the accumulator). This should solve the general problem but still leaves a potential edge case when transients have transients as dependencies (because the injected transients are still going to be cached... I think). --- framework/ioc.cfc | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/framework/ioc.cfc b/framework/ioc.cfc index fbf5c9fe..9ed7334f 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -890,12 +890,11 @@ component { } } } - accumulator.bean = bean; if ( !isSingleton( beanName ) && structKeyExists( accumulator.injection, beanName ) ) { - accumulator.injection[ beanName ].bean = accumulator.bean; + accumulator.injection[ beanName ].bean = bean; } } else if ( isConstant( beanName ) ) { - accumulator.bean = info.value; + bean = info.value; accumulator.injection[ beanName ] = { bean = info.value, setters = { } }; } else if ( structKeyExists( info, 'factory' ) ) { var fmBean = isSimpleValue( info.factory ) ? this.getBean( info.factory ) : info.factory; @@ -910,11 +909,11 @@ component { } } if ( isCustomFunction( fmBean ) || isClosure( fmBean ) ) { - accumulator.bean = fmBean( argumentCollection = argStruct ); + bean = fmBean( argumentCollection = argStruct ); } else { - accumulator.bean = evaluate( 'fmBean.#info.method#( argumentCollection = argStruct )' ); + bean = evaluate( 'fmBean.#info.method#( argumentCollection = argStruct )' ); } - accumulator.injection[ beanName ] = { bean = accumulator.bean, setters = { } }; + accumulator.injection[ beanName ] = { bean = bean, setters = { } }; } else { throw 'internal error: invalid metadata for #beanName#'; } @@ -926,10 +925,13 @@ component { } if ( !isNull( bean ) ) { accumulator.injection[ beanName ] = { bean = bean, setters = { } }; - accumulator.bean = bean; } } - return accumulator; + return { + bean = bean, + injection = accumulator.injection, + dependencies = accumulator.dependencies + }; } From ee55f3c68bbb5e26e7784efdd5ee54e39f48481d Mon Sep 17 00:00:00 2001 From: Sean Corfield Date: Fri, 16 Sep 2016 20:15:37 -0700 Subject: [PATCH 100/100] Prep for 4.0.0 release --- framework/Application.cfc | 2 +- framework/MyApplication.cfc | 2 +- framework/WireBoxAdapter.cfc | 2 +- framework/aop.cfc | 4 ++-- framework/beanProxy.cfc | 4 ++-- framework/cfmljure.cfc | 4 ++-- framework/cljcontroller.cfc | 4 ++-- framework/facade.cfc | 2 +- framework/ioc.cfc | 4 ++-- framework/ioclj.cfc | 4 ++-- framework/one.cfc | 2 +- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/framework/Application.cfc b/framework/Application.cfc index 62549953..fcc531c8 100644 --- a/framework/Application.cfc +++ b/framework/Application.cfc @@ -1,5 +1,5 @@ component { - // Version: FW/1 4.0.0 Beta 1 + // Version: FW/1 4.0.0 // copy this to your application root to use as your Application.cfc // or incorporate the logic below into your existing Application.cfc diff --git a/framework/MyApplication.cfc b/framework/MyApplication.cfc index 39bf4197..e4da02a5 100644 --- a/framework/MyApplication.cfc +++ b/framework/MyApplication.cfc @@ -1,5 +1,5 @@ component extends="framework.one" { - // Version: FW/1 4.0.0 Beta 1 + // Version: FW/1 4.0.0 // if you need to provide extension points, copy this to // your web root, next to your Application.cfc, and add diff --git a/framework/WireBoxAdapter.cfc b/framework/WireBoxAdapter.cfc index 181ce70a..02240f58 100644 --- a/framework/WireBoxAdapter.cfc +++ b/framework/WireBoxAdapter.cfc @@ -1,5 +1,5 @@ component extends="wirebox.system.ioc.Injector" { - variables._fw1_version = "4.0.0-SNAPSHOT"; + variables._fw1_version = "4.0.0"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/aop.cfc b/framework/aop.cfc index 2062c29c..c80a60ce 100644 --- a/framework/aop.cfc +++ b/framework/aop.cfc @@ -1,6 +1,6 @@ component extends="framework.ioc" { - variables._fw1_version = "4.0.0-SNAPSHOT"; - variables._aop1_version = "2.0.2-SNAPSHOT"; + variables._fw1_version = "4.0.0"; + variables._aop1_version = "2.0.2"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/beanProxy.cfc b/framework/beanProxy.cfc index f781a83c..c799fd5b 100644 --- a/framework/beanProxy.cfc +++ b/framework/beanProxy.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-SNAPSHOT"; - variables._aop1_version = "2.0.2-SNAPSHOT"; + variables._fw1_version = "4.0.0"; + variables._aop1_version = "2.0.2"; /* Copyright (c) 2013-2016, Mark Drew, Sean Corfield, Daniel Budde diff --git a/framework/cfmljure.cfc b/framework/cfmljure.cfc index 9c010e1c..86c3c780 100644 --- a/framework/cfmljure.cfc +++ b/framework/cfmljure.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-SNAPSHOT"; - variables._cfmljure_version = "1.1.0-SNAPSHOT"; + variables._fw1_version = "4.0.0"; + variables._cfmljure_version = "1.1.0"; /* Copyright (c) 2012-2016, Sean Corfield diff --git a/framework/cljcontroller.cfc b/framework/cljcontroller.cfc index b4cd05d5..18c41853 100644 --- a/framework/cljcontroller.cfc +++ b/framework/cljcontroller.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-SNAPSHOT"; - variables._ioclj_version = "1.0.1-SNAPSHOT"; + variables._fw1_version = "4.0.0"; + variables._ioclj_version = "1.0.1"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/facade.cfc b/framework/facade.cfc index 4cdbc46c..50026271 100644 --- a/framework/facade.cfc +++ b/framework/facade.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-SNAPSHOT"; + variables._fw1_version = "4.0.0"; /* Copyright (c) 2016, Sean Corfield diff --git a/framework/ioc.cfc b/framework/ioc.cfc index 9ed7334f..b02016c2 100644 --- a/framework/ioc.cfc +++ b/framework/ioc.cfc @@ -1,6 +1,6 @@ component { - variables._fw1_version = "4.0.0-SNAPSHOT"; - variables._di1_version = "1.2.0-SNAPSHOT"; + variables._fw1_version = "4.0.0"; + variables._di1_version = "1.2.0"; /* Copyright (c) 2010-2016, Sean Corfield diff --git a/framework/ioclj.cfc b/framework/ioclj.cfc index fe972c48..ebc5af8f 100644 --- a/framework/ioclj.cfc +++ b/framework/ioclj.cfc @@ -1,6 +1,6 @@ component extends=framework.ioc { - variables._fw1_version = "4.0.0-SNAPSHOT"; - variables._ioclj_version = "1.1.0-SNAPSHOT"; + variables._fw1_version = "4.0.0"; + variables._ioclj_version = "1.1.0"; /* Copyright (c) 2015-2016, Sean Corfield diff --git a/framework/one.cfc b/framework/one.cfc index 7cfc44d5..0b7901b4 100644 --- a/framework/one.cfc +++ b/framework/one.cfc @@ -1,5 +1,5 @@ component { - variables._fw1_version = "4.0.0-SNAPSHOT"; + variables._fw1_version = "4.0.0"; /* Copyright (c) 2009-2016, Sean Corfield, Marcin Szczepanski, Ryan Cogswell
timedeltatypeactionmessage
timedeltatypeactionmessage