WhatsNew:3.0.0

last edited byusericonlmajano on 08-Aug-2010

<< Back to Dashboard

Contents

What's New With ColdBox 3.0.0

Introduction

ColdBox 3.0.0 is an entirely new release and you must take care when updating from older releases as there might be come compatibility issues that can be found here: Compatibility Guide for pre 3.0.0. Below is the new configuration file XSD documentation:

Bug Fixes

You can see our defect report for ColdBox 3.0.0 at our Assembla workspace.

Application Template Updates

Major New Features

This section lists the major updates to the ColdBox 3.0.0 Platform

Programmatic Configuration

Look Ma! No more XML! That's right, you can leave your XML worries behind and configure the entire application using a simple configuration CFC. Not only that, but we added environment detection right into the new programmatic CFC. To learn more about it, please visit our configuration CFC page.

CacheBox: The Enterprise ColdFusion Cache Engine, Aggregator and API

We are opening our enterprise level cache as a standalone application but we are also enhancing it in order to create CacheBox. CacheBox is not only an enterprise level caching engine but also functions as a cache aggregator and API. You can aggregate different caching engines or types of the same engine into one single umbrella. It also gives you built in logging, events, synchronization, shutdown/startup procedures and best of all a cache agnostic API. You can then build your applications based on an abstract API and then be able to configure the caches for your applications at runtime. This gives you greater flexibility and scalability when planning and writing your applications. Our caching engine has also been revamped with new events and we now have flexible and customizable object storages. From concurrent object stores to simple JDBC replicated storages. We also opened our storage and cache provider API so you can even build your own and extend the framework. Read our CacheBox Documentation

MockBox: The ColdBox Mocking/Stubbing Framework

In one of our most ambitious releases, we are also introducing one of the newest standalone services/frameworks that are now bundled with ColdBox. MockBox is a next generation mocking/stubbing framework that is a necessity when unit testing ColdFusion components. Read our MockBox Documentation

LogBox: The Enterprise ColdFusion Logging Library

We are opening up our internal logging framework for usage not only within ColdBox applications but for ANY ColdFusion application. It becomes another addition to our standalone services/frameworks that are bundled with the ColdBox Platform. LogBox is an enterprise ColdFusion logging library designed to give you flexibility, simplicity and power when logging or tracing is needed in your applications. LogBox allows you to easily build upon its logging framework in order to meet any logging or reporting needs your applications has. LogBox allows you to create multiple destinations for your loggings and even be able to configure them or change them at runtime. LogBox was inspired by the original logging capabilities in ColdBox and in the Log4j project. Read our LogBox Documentation

ColdBox Modules

You can now build your ColdBox applications in a more modular and segregated approach. ColdBox Modules are self-contained subsets of a ColdBox application that can be dropped in to any ColdBox application and become alive as part of the host application. They will bring re-usability and extensibility to any ColdBox application, as now you can break them down further into a collection of modules. This concept has been around in software design for a long time as it is always essential to partition a system into manageable modules or parts.

"In structured design and data-driven design, a module is a generic term used to describe a named and addressable group of program statements" by Craig Borysowich (Chief Technology Tactician)

ColdBox Modules will change the way you approach application development as you can now have a foundation architecture that can scale easily and provide you enough manageability to reduce maintenance and increase development. Such a design means that development, testing and maintenance becomes easier, faster and with a much lower cost. Welcome to a brave new world! Read our Modules Documentation


ColdBox Module Debugger Panel

ColdBox Flash RAM

ColdBox has Flash RAM capabilities since version 2.0 (long long time ago). However, in this release we are taking a step further and enhancing the capabilities. Now every handler, plugin, interceptor, layout and view will have a flash object in their variables scope already configured for usage. The entire flash RAM capabilities are now encapsulated in a flash object that you can use in your entire ColdBox code. Not only that, we based our Flash object on an interface and now you can build your own Flash RAM implementations very easily. What this means is that you can build a Flash RAM implementation that can store the Flashed variables wherever you want.


ColdBox Flash RAM Sequence Diagram

We also added the capabilities to maintain flows or conversations between requests by using our discard() and keep() methods in our Flash objects. This will continue to expand in our 3.X releases as we build our own conversations DSL to define programmatically wizard like or complex web conversations.

For those who do not know what Flash RAM does, please read our Flash RAM Documentation. A brief introduction is that there are times where you need to store user related variables in some kind of permanent storage then relocate the user into another section of your application, be able to retrieve the data, use it and then clean it. All of these tedious operations are definitely doable by why reinvent the wheel if we can have the platform give us a tool for maintaing conversation variables across requests.

The included implementations for ColdBox 3.0.0 are:

You will find all of these implementations in the following directory: coldbox.system.web.flash. Please read our Flash RAM Documentation

// handler code:
function saveForm(event){
	// save post
	flash.put("notice","Saved the form baby!");
	// relocate to another event
	setNextEvent('user.show');	
}
function show(event){
	// Nothing to do with flash, inflating by flash object automatically
	event.setView('user.show');
}

// User/show.cfm template using if statements
<cfif flash.exists("notice")>
	<div class="notice">#flash.get("notice")#</div>
</cfif>

// User/show.cfm using defaults
<div class="notice">#flash.get(name="notice",default="")</div>

ColdBox Extensions

You can now very easily override the functionality of core plugins without touching them and you can also add more plugins to the core by using our new ColdBox Extensions. This is basically a folder in the core called coldbox/system/extensions. In this folder (as of now), you can drop in your plugins in the plugins directory. What this does is that the framework will look for core plugins in this location FIRST and then if not found look in the core plugins directory: /coldbox/system/plugins. This is extermely useful if you want to override the behavior of internal plugins or just have a location for your own global core plugins.

You can also customize the location to ANYWHERE you like by adding a simple setting to your configuration: ColdBoxExtensionsLocation. This setting is a dot notation path or cf maping to where your plugins and (*ahem*) future services (announced later), will be placed.

coldbox = {
	coldboxExtensionsLocation = "shared.extensions"
};
//or
<Setting name="ColdBoxExtensionsLocation" value="shared.extensions" />

LayoutsExternalLocation

Long overdue external location setting that can act as a secondary lookup location for layouts.

ColdFusion ORM Integrations

We have created several classes that can give you greater support for ColdFusion ORM integrations via Hibernate. Read the documentation here: CF ORM Extras

This is a great way to start off your ColdFusion ORM projects. We believe these base service layers can give you a good 85% of functionality right off the bat.

ColdBox Cache Enhancements

ColdBox Factory Enhancements

You can now use the following new functions in the ColdBox Factory

ColdBox Proxy Enhancements

You can now use the following new functions in the ColdBox Proxy

New interception point: preProxyResults.

Plugin Enhancements

No More Inheritance

The major change to plugins is that now plugins DO NOT need to inherit from the Core ColdBox Plugin. You can now build simple CFCs and they will be decorated at runtime and adapted for usage within ColdBox. Ahh, doesn't that feel great, less coupling! Not only that, we are still backwards compatible and if you prefer to do inheritance, then go for it, we are not prejudice to your decision. You can still use ALL of the methods you are used to when building plugins, we take care of the hard stuff to make your code look nicer and easier. Also, if you declare a constructor in your plugin, ColdBox will call it for you. We also create a virtual super scope for your inheritless components so you can still provide overrides. The name of the scope is called: $super so you can talk to the base class.

**
* My awesome plugin
* @singleton 
*/
component{
	function init(){
		setPluginName("Cool Awesome");
		setPluginAuthor("Lui Majano");
		return this;
	}
}

Persistence Metadata

You can now use the singleton annotation on the cfcomponent tag to denote that this object will live for the entire application. This is the same as adding cache=true cacheTimeout=0 to the component tag:

**
* @singleton true
*/
component{
}

// OR
<cfcomponent singleton=true></cfcomponent>

By pass Plugin Constructor

You can also bypass an automatic init() call on the plugin by passing a new argument to the getMyPlugin() or getPlugin() calls.

// get plugin with no constructor called, I will do it
oPlugin = getMyPlugin(plugin="AwesomePlugin", init=false).init("Yeaaaa!");

Persistence Updates

You can now declare a singleton annotation on the plugin declaration and it will be treated as a singleton.


**
* My awesome plugin
* @singleton 
*/
component{
	
	function init(){
		return this;
	}
}

Retrieve Module Plugins

You can now pass in a module argument to all plugin functions to retrieve a plugin directly from a module.

// Get the Smiley plugin from the fun module
smiley = getMyPlugin(name="Smiley",module="Fun");

New Object Properties

All plugins will have certain objects in the variables scope available to them for usage. The following is a table listing of all the objects you can use:

Property New Description
logBox Yes The reference to the LogBox library
log Yes A pre-configured LogBox Logger object for this specific class object
flash Yes A reference to the current configured Flash Object Implementation that inherits from the AbstractFlashScope (coldbox.system.web.flash.AbstractFlashScope)
controller No The main application ColdBox controller (coldbox.system.web.Controller)

New Methods

You now have a few extra methods when dealing with plugin development:

New Cluster Scope Plugin

New plugin for using the cluster scope on railo enabled CFML engines.

New Validator Plugin

A plugin that helps you do any kind of data validation, check its API out: http://www.coldbox.org/api (coldbox.system.plugins.Validator)

New ORMService Plugin

We added a new plugin that will act as a generic ORM service layer for your applications. This generic service layer will give you a nice way for you to build application using a simple service layer already pre-coded. This service layer is based on our BaseORMService class.

Utilities Plugin Refactored

The Utilities plugin has been refactored into several new plugins:

New HTMLHelper Plugin

This cool new little plugin will help you out when dealing with HTML. It can help you keep track of js or css assets and make sure they get loaded only once, render strict html css or javascript, render lists from arrays or queries, render tables from arrays or queries, create cool dynamic meta tags, create doctype headers and so much more. Please read our HTMLHelper Documenation for more information and awesomeness!

New MailService Plugin

You can now be cool and send emails via our very own MailService plugin. Use it in script and in any CFML engine. Not only that but this service can actually do token replacements for you at the time of sending emails. So what is this? Well, once you tell the mail service to give you a new mail object, you can set attachments on it, headers, etc but you can also pass in a structure of tokens. Once the email is about to be sent, the MailService will do token replacements on the body of the email agains these tokens.

// Get new mail object
mail = getPlugin("MailService").newMail().config(from="info@coldbox.org",
												 to="awesome@coldbox.org",
												 type="html",
												 subject="Mail For You Captain Awesome");
// create some tokens
tokens = {name="Luis Majano",time=dateformat(now(),"full")};
mail.setBodyTokens(tokens);

// Set some body text
mail.setBody("<h1>Hello @name@, how are you today?</h1>  <p>Today is the <b>@time@</b>.</p> <br/><br/><a href=""http://www.coldbox.org"">ColdBox Rules!</a>");

// Send the mail
results = getPlugin("MailService").send(mail);

MessageBox Updates

Some new convenience methods:

If you want to style your own messagebox you will need to create a setting called: messagebox_style_override and set it to true. Then make sure the css for the messagebox exists.

settings = {
	messagebox_style_override = true
};

You can now also render messages a-la-carte or on-demand by using the renderMessage method:

#getPlugin("MessageBox").renderMessage("info","Info Message")#

#getPlugin("MessageBox").renderMessage("warning",flash.get('notice'))#

AntiSamy XSS Plugin Updated

The AntiSamy XSS plugin has been updated from the latest AntiSamy release project: http://www.owasp.org/index.php/Category:OWASP_AntiSamy_Project

Yes, ColdBox has XSS Cleanup built in since version 2.0

Renderer Plugin

View Folder Helper

We also introduce view folder helpers. We already have view helpers by conventions, basically if you have a view called home.cfm and you create alongside it a file called homeHelper.cfm that helper's UDFs will be injected at runtime into the home.cfm and you can use those UDFs. For 3.0.0 we expanded on this (thanks to an awesome student at one of our trainings) and we introduce a view folder helper. What this means is that you can create a convention based on the folder name and all the views within that folder will get to use the helper UDFs. So if I have the following layout:

/views
	/users
		index.cfm
		editor.cfm
		permissions.cfm
		listings.cfm
		usersHelper.cfm *

You can see that all the views are in the users folder, and I created a usersHelper.cfm. Basically: {name of folder}Helper.cfm. The Renderer now sees a folder helper and will inject it for all the renderings within this folder ONLY.

Query Helper

Feed Generator

Feed Reader

i18n & Resource Bundle

Both the i18n and ResourceBundle plugins have been completely reworked and reconstructed for this release. They are now faster, leaner and meaner! Their performance is substanially faster and more stable under race conditions as lots of thread conditions have been eliminated. Below are some of the major enhancements apart from performance and reworking.

// show a resource and if it does not exist, return a value
#getResource('name.button','Click')#

// Show the resource from the spanish locale, I am building a spanish email ala carte
<div>
	#getResource(resource="welcome",locale="es")# Francisco,
	
	Gracias!
</div>

JavaLoader Enhancements

Interceptor Enhancements

// Declare interceptor
interceptors = [
	{name="AwesomeInterceptor", class="coldbox.system.interceptors.Awesome"},
	{name="MoreAwesomeInterceptor", class="coldbox.system.interceptors.Awesome",
	 properties={ awesomeCheck=true, configLoading=false }
	}
];

<-- OOr XML-->
<Interceptors>
	<Interceptor name="AwesomeInterceptor" class="coldbox.system.interceptors.Awesome" />
	<Interceptor name="AwesomeInterceptor" class="coldbox.system.interceptors.Awesome">
		<Property name="awesomeCheck">true</Property>
		<Property name="configLoading">false</Property>
	</Interceptor>
</Interceptors>

No More Inheritance

The major change to interceptors is that now they DO NOT need to inherit from the Core ColdBox Interceptor class if you like. You can now build simple CFCs and they will be decorated at runtime and adapted for usage within ColdBox. Ahh, doesn't that feel great, less coupling! Not only that, we are still backwards compatible and if you prefer to do inheritance, then go for it, we are not prejudice to your decision. You can still use ALL of the methods you are used to when building interceptors, we take care of the hard stuff to make your code look nicer and easier. We also create a virtual super scope for your inheritless components so you can still provide overrides. The name of the scope is called: $super so you can talk to the base class.

**
* My awesome Interceptor
*/
component{
	//Autowire Dependencies
	property name="userService" inject;

	function configure(){
		// configure my interceptor
	}
	function preProcess(event,interceptData){
	
	}
}

New Interception Points

Interception Point Description
preLayout Occurs before any rendering is done or determined. Remember that the preRender point has the entire rendered content passed into it already. So this happens before rendering of content
preViewRender Occurs before ANY view is executed. Keys received in the interception: renderedView,view,cache,cacheTimeout,cacheLastAccessTimeout,cacheSuffix,module
postViewRender Occurs after the view has been executed and is awaiting to be rendered. Receives all the view rendering arguments and the view content that will be rendered. Keys: renderedView,view,cache,cacheTimeout,cacheLastAccessTimeout,cacheSuffix,module
preProxyResults Occurs before the results of a proxy call are sent back to the external caller. This can be used for marshalling or transformation of data, logging, etc. Keys received are: proxyResults
afterModelCreation Occurs after ANY model object gets created and can be used to post-process objects after creation and dependency injections.
onReinit Occurs right before the application will be reinitialized.
onApplicationEnd Fires whenever the ColdFusion application stops. A great place to shutdown services such as EHCache, etc.
beforeDebuggerPanel Fires right before the debugger panels get rendered in debug mode
afterDebuggerPanel Fires right after the last debugger panel gets rendered in debug mode.

Extending The Debugger

Thanks to two new interception points: beforeDebuggerPanel,afterDebuggerPanel, you can easily build interceptors that can act as debugger panels in your ColdBox applications. Time to start getting creative folks!

New Object Properties

All interceptors will have certain objects in the variables scope available to them for usage. The following is a table listing of all the objects you can use:

Property New Description
logBox Yes The reference to the LogBox library
log Yes A pre-configured LogBox Logger object for this specific class object
flash Yes A reference to the current configured Flash Object Implementation that inherits from the AbstractFlashScope (coldbox.system.web.flash.AbstractFlashScope)
controller No The main application ColdBox controller (coldbox.system.web.Controller)

eventPattern annotation

You can now add a new metadata argument (annotation) called eventPattern to any interceptor method in order to tell the framework that it should execute that interception point ONLY if the incoming event matches that regular expression. Let's say that you have an interceptor with a preProcess method and you would like to provide security with it, but only to events that are in the admin package. Then I can do this:

/**
* @eventPattern ^admin
*/
function preProcess(event,interceptData){
}

//OR
<cffunction name="preProcess" output="false" eventPattern="^admin">

</cffunction>

The eventPattern annotation is a regular expression that MUST match the incoming event variable. If it does, then the interception point fires, else it is ignored.

New Interceptor: ReactorLoader

The reactor extras have been expanded and a new interceptor has been born called: !ReactorLoader. This nice interceptor will configure your application to use Reactor and cache it in the ColdBox cache. Easily configure your app for Reactor ORM

Interceptor Properties :

Property Type Required Default Description
dsnAlias string true N/A The datasource alias name to use, as defined in your datasources section of your config. Make sure you define the dbtype also
pathToConfigXML string true N/A The path of the Reactor config file
project string true N/A The name of the Reactor project
mapping string true N/A The relative path or mapping to the directory where reactor will write generated files
mode string true N/A The reactor mode: always,development,production
ReactorCacheKey string false Reactor The key used to store Reactor as a singleton in the coldbox cache. (case sensitive)
ReactorConfigClassPath string false reactor.config.config The class path override of the reactor configuration object
ReactorFactoryClassPath string false reactor.reactorFactory The class path override of the reactor factory object

So just remember to add the datasource element with a valid dbType as Reactor needs it; Options are:

Sample:

<Datasources>
	<Datasource alias="blogDSN" name="simpleblog"   dbtype="mssql" />
</Datasources>

<Interceptors>
	<Interceptor class="coldbox.system.orm.reactor.ReactorLoader">
	  <Property name="dsnAlias">blogDSN</Property>
	  <Property name="pathToConfigXML">config/reactor.xml.cfm</Property>
	  <Property name="project">MyBlog</Property>
	  <Property name="mapping">${AppMapping}/model/reactor</Property>
	  <Property name="mode">development</Property>
	</Interceptor>
</Interceptors>

This declaration will create the following objects in the ColdBox cache:

Cache Key Object
Reactor The reactor factory object


So do you still think there is more? Well, there is no more, that's it, a few lines of code and you are cooking with Reactor.

Autowire Interceptor Updates

The autowire interceptor has been udpated to allow for the configuration of dependency injection of ORM entities. This feature ONLY works if you are using our new ORM Event Handler to ColdBox bridge. If enabled you can configure the following settings:

Property Type Default Description
entityInjection Boolean false Enable the entity injection events
entityInclude List [empty] A list of entity names to include in the injections ONLY!
entityExclude List [empty] A list of entity names to exclude from dependency injection!

Security Interceptor Updates

<rule>
	<whitelist>user\.login,user\.logout,^main.*</whitelist>
	<securelist>^user\..*, ^admin</securelist>
	<roles>admin</roles>
	<permissions></permissions>
	<redirect>user.login</redirect>
	<useSSL>false</useSSL>
</rule>
<rule>
	<whitelist>user\.login,user\.logout,^main.*</whitelist>
	<securelist>^user\..*, ^admin</securelist>
	<roles>admin</roles>
	<permissions></permissions>
	<redirect>user.login</redirect>
	<useSSL>false</useSSL>
	<--C<ustom Elements-->
	<Filters>auth,cookie,ip</Filters>
	<CustomChecks>true</CustomChecks>
</rule>

Deploy Interceptor Updates

SES Interceptor Updates

// view dispatch, no event execution
addRoute(pattern='/AboutUs',view='about',viewNolayout=true);
addRoute(pattern="/:name-alpha",handler="show");

// Easily split actions on a handler according to HTTP methods detected.
addRoute(pattern="/api/user/:username",
		 handler="rest.User",
		 action={
			GET = "show",
			DELETE = "remove",
			POST = "create",
			PUT  = "update"
		 });
// route with custom constraints
addRoute(pattern="/api/:format/",
		 handler="api",
		 action="execute",
		 contraints={
			format = "(xml|json)"
		 });

Important : The value of the constraint MUST be enclosed in parenthesis ()

This gives the user the ability to use ANY regular expression for any placeholder or for the entire URL string by just writing the namespace: regex:() and then placing the regular expression within the parenthesis. This is similar to the constraints functionality but targeted directly to a placeholder reference within the pattern. This is more readable and directed but it does not set the regular expression to a variable. It just identifies and matches.

// route with custom constraints
addRoute(pattern="/api/regex:(^(xml|json))/",
		 handler="api",
		 action="execute");

Event Handler Enhancements

No More Inheritance

The major change to handlers is that now they DO NOT need to inherit from the Core ColdBox EventHandler if you like. You can now build simple CFCs and they will be decorated at runtime and adapted for usage within ColdBox. Ahh, doesn't that feel great, less coupling! Not only that, we are still backwards compatible and if you prefer to do inheritance, then go for it, we are not prejudice to your decision. You can still use ALL of the methods you are used to when building event handlers, we take care of the hard stuff to make your code look nicer and easier. Also, if you declare a constructor in your handler, ColdBox will call it for you. We also create a virtual super scope for your inheritless components so you can still provide overrides. The name of the scope is called: $super so you can talk to the base class.

**
* My awesome Handler
*/
component{
	//Autowire Dependencies
	property name="userService" inject;

	function init(){
		// do some init stuff
		return this;
	}
}

Persistence Metadata

You can now use the singleton annotation on the cfcomponent tag to denote that this object will live for the entire application. This is the same as adding cache=true cacheTimeout=0 to the component tag:

**
* @singleton true
*/
component{
}

// OR
<cfcomponent singleton=true></cfcomponent>

New Object Properties

All handlers will have certain objects in the variables scope available to them for usage. The following is a table listing of all the objects you can use:

Property New Description
logBox Yes The reference to the LogBox library
log Yes A pre-configured LogBox Logger object for this specific class object
flash Yes A reference to the current configured Flash Object Implementation that inherits from the AbstractFlashScope (coldbox.system.web.flash.AbstractFlashScope)
controller No The main application ColdBox controller (coldbox.system.web.Controller)

Pre/Post Interceptors Updated

These local interceptors now take in the current executing action they are intercepting as a new argument called: action

<---  preHandler --->
<cffunction name="preHandler" returntype="void" output="false" hint="Executes before any event in this handler">
	<cfargument name="event" required="true">
	<cfargument name="action" hint="The intercepted action"/>
	<cfscript>	
		var rc = event.getCollection();
	</cfscript>
</cffunction>

<---  postHandler --->
<cffunction name="postHandler" returntype="void" output="false" hint="Executes after any event in this handler">
	<cfargument name="event" required="true">
	<cfargument name="action" hint="The intercepted action"/>
	<cfscript>	
		var rc = event.getCollection();
	</cfscript>
</cffunction>

New implicit handler method: onError()

You can now create another implicit method on your event handlers with the following signature:

function onError(event,faultAction,exception){
}

This method will be executed if there is ANY exception executing the curent event handler. This can give you very nice controlled exceptions at a handler or base handler level.

// Implicit Error Handler
function onError(event,action,exception){
	var validator = getInterceptor("APIValidator");
	var append = structnew();
	
	// Log Error
	append.exception = arguments.exception;
	logger.fatal("Error executing resource: #event.getValue("_route")#. Error: #arguments.exception.message#",validator.getLogJSON(append));
	
	// Send error to screen
	validator.renderError(message="#arguments.exception.message# #arguments.exception.detail#",
						  code="500",
						  format=arguments.event.getValue("format"),
						  event=arguments.event);
}

Handler Action HTTP Security

All event handlers can now declare a public variable called allowedMethods (struct) that contains a map of allowed methods and their appropriate HTTP methods. This has been done to provide a nice way to secure RESTful API's built on ColdBox. You can now control what HTTP method operations can be done on specific handler actions. Let's say we want to protect our actions on our handler, delete(),add() and index() with different http methods.

Action Allowed Methods
delete delete
add get,post
index get
//public property to allow method executions
this.allowedMethods = {
	delete='delete', 
	add='get,post', 
	index='get'
};

New Implicit Handler: MissingTemplateHandler

You can now declare a coldbox setting called: missingTemplateHandler that points to an event. This event will be fired whenever the framework detects a missing template call in your application, that is for ANY cfm template. The request context will have the missing template value in a variable called missingTemplate.

// Declare the missing template handler in your Coldbox.cfc or your coldbox.xml.cfm
coldbox = {
	missingTemplateHandler = "main.missingTemplate"
};
// XML
<Settings>
	<Setting name="missingTemplateHandler" value="main.missingTemplate" />
</Settings>

// The handler event
function missingTemplate(event){
	var rc = event.getCollection();
	
	rc.page = pageService.findByName( rc.missingTemplate );
	
	if( rc.page.isValid() ){
		// Render the page
		event.setView('page.show');
	}
	else{
		// invalid page
		setNextEvent('page.invalid');
	}
}

New Implicit Handler: applicationEndHandler

You can now declare a coldbox setting called: applicationEndHandler that points to an event. This event will be fired whenever the ColdFusion Application expires or stops.

// Declare the missing template handler in your Coldbox.cfc or your coldbox.xml.cfm
coldbox = {
	applicationEndHandler = "main.onAppEnd"
};
// XML
<Settings>
	<Setting name="applicationEndHandler" value="main.onAppEnd" />
</Settings>

// The handler event
function onAppEnd(event){
	var rc = event.getCollection();
	
	//maybe shutdown things
	getModel("EHCache").shutdown();
}

Model Integration Enhancements (WireBox)

New/Updated Settings

The new/updated settings you can use when configuring the ColdBox Model Integration:

// Programmatic aproach
//Model Integration
models = {
	objectCaching = true,
	definitionFile = "config/modelMappings.cfm",
	externalLocation = "coldbox.testing.testmodel,shared,transfer",
	SetterInjection = false,
	DICompleteUDF = "onDIComplete",
	StopRecursion = ""
};

// XML Approach 
<Models>
	<DefinitionFile>config/ModelMappings.cfm</DefinitionFile>
	<SetterInjection>false</SetterInjection>
	<ObjectCaching>true</ObjectCaching>
	<ExternalLocation></ExternalLocation>
	<DICompleteUDF>onDIComplete</DICompleteUDF>
	<StopRecursion></StopRecursion>		
</Models>

Dependency DSL Metadata

The dependency DSL metadata marker has been standardized to the keyword inject. You can now use this metadata annotation on

// some examples
property name="userService" inject="model";
//same as inject by default references a model object
property name="userService" inject;

// seter method
/**
* @inject Model
*/
function setUserService(userService){
	variables.userService = arguments.userService;
}

// Constructor arguments
<cffunction name="init" output="false" returntype="any">
	<cfargument name="userService" type="any" inject="model">
	
</cffunction>

Updated Dependency Injection DSL

More updates will be shown once the WireBox Documentation is finalized. Below are some new autowire DSL types:

Type Description
cachebox Get a reference to the application's CacheBox instance
cachebox:{name} Get a reference to a named cache inside of CacheBox
cachebox:{name}:{objectKey} Get an object from the named cache inside of CacheBox according to the objectKey
coldbox:moduleService Get a reference to the ColdBox Module Service
coldbox:interceptor:{name} Get a reference of a named interceptor
coldbox:fwConfigBean Get a configuration bean object with ColdBox settings instead of Application settings
coldbox:fwSetting:{setting} Get a setting from the ColdBox settings instead of the Application settings
javaLoader:{class} Create an object from the JavaLoader plugin and its set of loaded java libraries
entityService Inject a BaseORMService object for usage as a generic service layer
entityService:{entity} Inject a VirtualEntityService object for usage as a service layer based off the name of the entity passed in. logbox Get a reference to the application's LogBox instance
logbox:root Get a reference to the root logger
logbox:logger:{category name} Get a reference to a named logger by its category name
// some examples
property name="logbox" inject="logbox";
property name="rootLogger" inject="logbox:root";
property name="logger" inject="logbox:logger:model.com.UserService";
property name="moduleService" inject="coldbox:moduleService";
property name="producer" inject="coldbox:interceptor:MessageProducer";
property name="configBean" inject="coldbox:fwConfigBean";
property name="producer" inject="interceptor:MessageProducer";
property name="appPath" inject="coldbox:fwSetting:ApplicationPath";

// JavaLoader goodness
property name="binaryHeap" inject="javaLoader:org.apache.commons.collections.BinaryHeap";
property name="email" inject="javaLoader:org.apache.commons.mail.SimpleEmail";

// Generic ORM service layer
property name="genericService" inject="entityService";
// Virtual service layer based on the User entity
property name="userService" inject="entityService:User";

Request Context Enhancements

These are the updates on the request context object:

New Methods

Examples

// build link with query string
<a href="#event.buildLink(linkTo='users.list',queryString='userID=234&page=2')#">Click</a>
<a href="#event.buildLink(linkTo='users.list',queryString='userID=234&page=2',ssl=true)#">Click</a>

// Get HTTP method
if( event.getHTTPMethod() eq "GET" ){ //do something }

// Following in the coldbox.cfc config
layouts = [
	{name="Awesome", file="Layout.Awesome.cfm"},
	{name="login", file="layout.login.cfm", folders="security"}
];

// Then in the handler
event.setLayout('Awesome')
// Same as
event.setLayout('Layout.Awesome');

event.noExecution()

This method is a very cool method to disable the main execution of the incoming event (if any). Why would you want to do this? Well, you can build interceptors that handle the requests instead of main events. This simulates building servlets that react to interceptors instead of events. The most common usage of this method is on handlers or interceptors that occur BEFORE the execution of the main event.

Example: I will build an interceptor that listens on preProcess method and stops execution to render back some data.

function preProcess(event,interceptData){
	// Do some work, we intercepted the request
	
	// Render data
	event.renderData(data=data,type="xml");
	
	// Stop main event execution, there will be none
	event.stopExecution();
}

event.renderData() updated

Our coolest methd of all gets even cooler!

Below is the new signature:

public void renderData(string type, any data, string contentType, [string encoding], [numeric statusCode], [string statusText], [string jsonCase], [string jsonQueryFormat], [boolean jsonAsText], [string xmlColumnList], [boolean xmlUseCDATA], [string xmlListDelimiter], [string xmlRootName]) 

Parameters

event.renderData(data="<h1>Page not found</h1>",statusCode="404",statusMessage="Page Not Found");
event.renderData(type="xml", data=query, xmlRootName="Users",xmlUseCDATA=true);
event.renderData(type="json",data=query,jsonCase="UPPER");

ColdBox BootStrapper Enhancements

ColdBox Main Controller Enhancements

The main coldbox controller which is used by the Application.cfc/cfm now has exposed methods for the locking timeouts.

You can now also choose the name of the key used to store your running ColdBox application. By default it still uses the key: cbController, but now you can choose this by using the following methods:

The setNextEvent() has been updated to support multiple arguments:

Unit/Integration Testing Enhancements

function setup(){
  this.loadColdBox = false;
  super.setup();
}
Annotation Type Required Default Description
appMapping string false / The application mapping of the ColdBox application to test. By defaults it maps to the root
configMapping string false {appMapping}/config/Coldbox.cfc or xml The configuration file to load for this test. If not defined, then by default it looks for the CFC configuration file first and if not found, it tests for the coldbox.xml.cfm second. It searches in the convention of the config folder.
loadColdBox Boolean false true If you call super.init() on the test case, this flag tells the base test case to load up the virtual testing application or not.

So now you can declare an integration test so much cleaner and nicer:


/**
* Integration test for my Users.cfc handler
* setup() is called implicitly from the inherited BaseTestCase, you can also override it.
* @appMapping /my-nested-app
*/
component extends="coldbox.system.testing.BaseTestCase"{

	function testIndex(){
		event = execute('Users.index');
		// Assert here
	}
	
	function testSave(){
		// setup some data for the handler, FORM or URL
		form.firstname = "luis";
		form.lastname = "majano";
		
		// execute integration test
		event = execute('Users.save');
		
		//assert here
	}
}

New Methods

There are some new methods to help you in your unit testing:

Method Description
querySim() enables you to simulate ANY query (Part of MockBox)
getMockBox() Get a reference to MockBox
getMockRequestContext(clearMethods) Builds a new request context object that you can use for mocking purposes
getMockModel(name,clearMethods) Gives you back a mock model object
reset() Clears the application scope of any leftover coldbox bits
getFlashScope() Gives you the current loaded flash scope object

New Targeted Plugin Testing

You can now test plugins directly with no need of doing integration testing. This way you can unit test plugins directly very very easily. All you need to do is the following:

  1. Create a test class that inherits from coldbox.system.testing.BasePluginTest
  2. Create a component annotation called plugin that equals the full path of the plugin to target for testing

This testing support class will create your plugin, and decorate with mocking capabilities via MockBox and mock all the necessary companion objects around plugins. The following are the objects that are placed in the variables scope for you to use:

All of the mock objects are essentially the dependencies of plugin objects. You have complete control over them as they are already mocked for you. We actually use this approach to test all shipped ColdBox plugins.

Basic Setup

/**
* @plugin myApp.plugins.CoolPlugin
*/
component extends="coldbox.system.testing.BasePluginTest"{

  // Just create test methods, no need to use the setup() method unless you want to:
  function setup(){
      super.setup();
      // test custom constructor
      plugin.init();
  }

}

Real Sample

<cfcomponent extends="coldbox.system.testing.BasePluginTest" plugin="coldbox.system.plugins.HTMLHelper">
<cfscript>
function testaddAssetJS(){
	var mockEvent = getMockRequestContext();
	mockRequestService.$("getContext", mockEvent);
	
	// mock the plugin's htmlhead method
	plugin.$("$htmlhead");
	
	// Call method to test
	plugin.addAsset('test.js,luis.js');
	
	debug( plugin.$callLog().$htmlhead);
	
	// test duplicate call
	assertEquals('<script src="test.js" type="text/javascript"></script><script src="luis.js" type="text/javascript"></script>' , plugin.$callLog().$htmlhead[1][1] );
	plugin.addAsset('test.js');
	assertEquals(1, arrayLen(plugin.$callLog().$htmlHead) );
}

function testTableORM(){
	data = entityLoad("User");
	
	str = plugin.table(data=data,includes="firstName");
	debug(str);
	assertEquals('<table><thead><tr><th>firstName</th></tr></thead><tbody><tr><td>Joe</td></tr><tr><td>Luis</td></tr></tbody></table>',str);
}
</cfscript>
</cfcomponent>

New Targeted Handler Testing

You can now test handlers directly with no need of doing integration testing. This way you can unit test handlers directly very very easily. All you need to do is the following:

  1. Create a test class that inherits from coldbox.system.testing.BaseHandlerTest
  2. Create a component annotation called handler that equals the full path of the handler CFC to target for unit testing
  3. Create an optional annotation called UDFLibraryFile that will be the path of the UDF library file that is declared in your ColdBox configuration or any other UDF file you load dynamically in your handlers.

This testing support class will create your handler, and decorate with mocking capabilities via MockBox and mock all the necessary companion objects around handlers. The following are the objects that are placed in the variables scope for you to use:

All of the mock objects are essentially the dependencies of handler objects. You have complete control over them as they are already mocked for you.

Basic Setup

/**
* @handler myApp.handler.User
* @UDFLibraryFile /myApp/includes/helpers/AppHelper.cfm
*/
component extends="coldbox.system.testing.BaseHandlerTest"{

  // Just create test methods, no need to use the setup() method unless you want to:
  function setup(){
      super.setup();
      mockUserService = getMockBox().createEmptyMock("myapp.model.user.UserService");
      // wire in my mock dependencies as this handler already has mocking capabilities
      handler.$property("userService","variables",mockUserService);
  }

}

New Targeted Interceptor Testing

You can now test interceptors directly with no need of doing integration testing. This way you can unit test interceptors directly very very easily. All you need to do is the following:

  1. Create a test class that inherits from coldbox.system.testing.BaseInterceptorTest
  2. Create a component annotation called interceptor that equals the full path of the handler CFC to target for unit testing
  3. Create an optional strcuture variable in the variables scope called configProperties in the setup() method before your super.setup() method, if you would like to test the interceptor with your configuration properties structure.

This testing support class will create your interceptor, and decorate with mocking capabilities via MockBox and mock all the necessary companion objects around interceptors. The following are the objects that are placed in the variables scope for you to use:

All of the mock objects are essentially the dependencies of interceptor objects. You have complete control over them as they are already mocked for you.

Basic Setup

/**
* @interceptor myApp.interceptors.Security
*/
component extends="coldbox.system.testing.BaseInterceptorTest"{

  // Just create test methods, no need to use the setup() method unless you want to:
  function setup(){
      configProperties = {
         roles = "admin,user,moderator",
         security = "active"
      };
      super.setup();
      mockSecurityService = getMockBox().createEmptyMock("myapp.model.SecurityService");
      // wire in my mock dependencies as this interceptor already has mocking capabilities
      interceptor.$property("securityService","variables",mockSecurityService);

      // we are now ready to test this interceptor
  }

}

Real Example

<cfcomponent extends="coldbox.system.testing.BaseInterceptorTest" interceptor="coldbox.system.interceptors.Deploy">
<cfscript>

function setup(){
	super.setup();
	
	// mocks
	mockController.$("getAppRootPath",expandPath('/coldbox/testharness'));
	interceptor.setProperty("tagFile","config/.deploy_tag");
	interceptor.$("locateFilePath","config/.deploy_tag").$("setSetting");
	
}

function testConfigure(){
	interceptor.configure();
}

function testAfterAspectsLoad(){
	mockLogger.$("info");
	interceptor.afterAspectsLoad(getMockRequestContext());
}

function testPostProcess(){

	//mocks
	mockController.$("getColdboxInitiated",true).$("setColdboxInitiated").$("setAspectsInitiated");
	testDate = now();
	mockLogger.$("info").$("error");
	interceptor.$property("tagFilePath","instance",'config/.deployTag');
	interceptor.$property("deployCommandObject","instance",'');
	
	// Test no setting
	interceptor.$("settingExists",false).$("configure");
	
	interceptor.postProcess(getMockRequestContext());
	assertEquals( 1, arrayLen(interceptor.$callLog().configure) );
	
	// Test setting exists but same date
	interceptor.$("getSetting",testDate).$("fileLastModified",testDate).$("settingExists",true);
	interceptor.postProcess(getMockRequestContext());
	assertEquals( 0, arrayLen(mockController.$callLog().setColdboxInitiated) );
	
	// Test it works
	interceptor.$("getSetting",testDate).$("fileLastModified",testDate+10).$("settingExists",true);
	interceptor.postProcess(getMockRequestContext());
	assertEquals( 1, arrayLen(mockController.$callLog().setColdboxInitiated) );
	
	
}	
</cfscript>	
</cfcomponent>

New Targeted Model Testing

You can now test model objects directly with no need of doing integration testing. This way you can unit test model objects very very easily using great mocking capabilities. All you need to do is the following:

  1. Create a test class that inherits from coldbox.system.testing.BaseModelTest
  2. Create a component annotation called model that equals the full path of the model object CFC to target for unit testing

This testing support class will create your model object, and decorate with mocking capabilities via MockBox and create some mocking classes you might find useful in your model object unit testing. The following are the objects that are placed in the variables scope for you to use:

Basic Setup

/**
* @model myApp.model.User
*/
component extends="coldbox.system.testing.BaseModelTest"{

  function setup(){
     super.setup();
     user = model;
  }

  function testisActive(){
    assertEquals( false, user.isActive() );
  }

}

Performance Updates

category Categories:
 
Download in other Formats:
markup Markup | pdf PDF | swf SWF | html HTML | word Word

comments Comments (0)