After almost 7 months of development, testing and revamping of our 3.1.0 release, we have reached our first milestone for our 3.5.0 ColdBox generation, 3.5.0 Beta CODENAME: SEEK-JEREMIAH 29:13. As you can see we skipped a few minor versions and did not want to introduce it as 4.0, but it will sure feel like the 4.0 release. This version is a major overhaul of the internal engine as we sweep out the old (ColdFusion 7 and Compatibility Modes) and sweep in the new. We dropped an incredible amount of backwards compatibility code and we have seen tremendous updates not only to performance but to size as well (Over 4 mb reduction). Our new release is meaner and faster than any release. We can provide you with our JMeter testing suite and configurations if you are interested, but in some cases we had requestes executing in less than 1ms for vanilla applications (Railo) under a 50,000 user load. So we are extremely happy to introduce 3.5.0 beta.
Download Now!
So get your hands on the new 3.5.0 beta goodness!
So apart from the tremendous amounts of fat cutting and fixes we have introduced some great enhancements, so let's talk about a few, or you can read the entire What's New in 3.5.0!
Removals-Migrations-Deprecations
- ColdFusion 7 support
- Archive Cache implementations
- Custom JSON support in favor of ColdFusion functions for JSON
- Environment Control interceptor in favor of ConfigurationCFC
- Configuration of a ColdBox application via an XML file
- TransactionAspect in our ORM package in favor of WireBox-AOP
- StringBuffer plugin moved to ForgeBox
- BeanFactory compatibility mode removed
- SideBar interceptor removed from core and moved to ForgeBox
- Deploy interceptor removed from core and moved to http://www.coldbox.org/forgebox/view/deploy
- Setting flashURLPersistScope removed in favor of theFlash RAM configuration structure
- Setting ConfigAutoReload removed
- Autowire Interceptor has been deprecated in favor of WireBox and the ColdBox ORM Event Handler Entity Injector
These deprecations and removals have made our core incredibly small and fast!
ColdBox Fixes
- Mark modules as activated=true once they are activated (ticket #1252)
- When rendering entity fields using the HTMLHelper, it needed to consider "insert" and "update" fields for display purposes (ticket #1253)
- Exception on array locations when removing module routes when autoreload is configured for SES interceptor (ticket #1254)
- HTMLHelper entityfields not treating properties with formulas correctly (ticket #1256)
- HTMLHelper cdata fixes on JavaScript (ticket #1275)
- Exception bean throws an exception on the $toString method when extraInfo is complex, it serializes to JSON now to avoid this (ticket #1272)
- View all
Major Enhancements
To read all the enhancements, please refer to our What's New in 3.5.0.
- The setting UDFLibraryFile can now be more than one template that can be mixed in to your handlers, layouts and views. It can be a list or an array of templates to mixin.
- Internal configuration data is now a CFC instead of XML, which makes loading the framework much faster as no XML parsing is needed at boot time.
- ColdBox XML configuration is now completely removed. ColdBox CFC is the only way now.
- Environment control has now been added to modules in their ModuleConfig.cfc. So the same conventions that are used in the parent configuration can be used in the module; having the name of the environment match a method name in your module config. So if the following environments are declared in your parent:
environments = { dev = "^railo.*,^cf.*,^local.*" }; function dev(){ // my overrides here coldbox.handlerCaching = false; }
Then the same convention translates to the ModuleConfig.cfc, so you can also create a dev() function in the module.
function dev(){ // my overrides here settings.moduleOverride = true; }
- Both setLayout() and setView() have a module argument now, so you can set any view or layout to be rendered out from ANY module
- New RequestContext method: noLayout() to tell the framework not render to a layout with the view
- The collection rendering mechanisms have been updated to allow for two new arguments; to allow for capping the rows or records to be iterated through and to specify a start row.
- The getSetting() methods now get a new argument: defaultValue so you can get a default value back if the setting does not exist
- New rendering interception points: preLayoutRender and postLayoutRender. This will allow you to intercept right before ANY layout is rendered and after it gets rendered.
- JSONP is now fully supported by event.renderData(). If you want to learn more about JSONP please visit the Wikipedia entry. In a nutshell, JSONP denotes that the JSON packet will be wrapped in a JavaScript callback function of your choice. You will do this by using the type="JSONP" and jsonCallback="mycallback"' arguments.
event.renderData(type="jsonp", data=data, jsonCallback="myCallback");
- event.renderdata() now also supports a custom data conversion convention when marshalling CFCs. If you pass in a CFC that has a method called $renderdata(), then the marshalling utility will call that function for you instead of using the internal JSON/XML/WDDX marshalling utilities. You can pass in the custom content type for encoding:
event.renderData(type="plain", data=myCFC, contentType="text");
component accessors="true"{ property name="name"; property name="age"; property name="cool"; function init(){ return this; } function config(name,age,cool){ setName( arguments.name ); setAge( arguments.age ); setCool( arguments.cool ); return this; } function $renderdata(){ var d = { n = variables.name, a = variables.age, c = variables.cool, today = now() }; return d.toString(); } }
In this approach your $renderdata() function can be much more customizable than our internal serializers. Just remember to use the right contentType argument.
SES Enhancements
We have done incredible strides in perfecting our already amazing URL rewriting engine. Here are the updates:
- All void or set methods now return itself so methods can be concatenated for building a URL rewriting DSL
Including Routes
We have created a new method: includeRoutes( includePath ) that will allow you to load multiple configuration files on demand from anywhere in your application. This can easily help you manage tons of routes in a system.
includeRoutes('/config/mypackageRoutes') .includeRoutes('/config/adminRoutes');
The modules routes array definition can now also contain simple values that are pointers to configuration files to load using the includeRoutes() method behind the scenes. So now you don't have to define all the routes in your module configuration object (ModuleConfig.cfc) but abstract them into their own routes configuraiton template files and you can do more than 1 of course.
routes = [ "config/routes.cfm", "config/routes2.cfm" {pattern="/sample", handler="test", action="execute"} ];
This will load the routes.cfm and routes2.cfm templates in the module's config directory. The SES interceptor is smart enough to detect you want to include those routing templates and also knows they are located within the module.
With Closures
We have created some cool context methods to allow for the prefixing of any of the addRoute() arguments by using what we call with closures. This allows you to prefix repetitive patterns in route declarations. The best way to see how it works is by example:
addRoute(pattern="/news", handler="public.news", action="index"); addRoute(pattern="/news/recent", handler="public.news", action="recent"); addRoute(pattern="/news/removed", handler="public.news", action="removed"); addRoute(pattern="/news/add/:title", handler="public.news", action="add"); addRoute(pattern="/news/delete/:slug", handler="public.news", action="remove"); addRoute(pattern="/news/v/:slug", handler="public.news", action="view");
As you can see from the routes above, we have lots of repetitive code that we can clean out. So let's look at the same routes but using some nice with closures.
with(pattern="/news", handler="public.news") .addRoute(pattern="/", action="index") .addRoute(pattern="/recent", action="recent") .addRoute(pattern="/removed", action="removed") .addRoute(pattern="/add/:title", action="add") .addRoute(pattern="/delete/:slug", action="remove") .addRoute(pattern="/v/:slug", action="view") .endWith();
As you can see, we start our URL mapping DSL with the with() method and pass in any argument the addRoute() method declares. In this case we pass a pattern and a handler. Meaning any addRoutes() that are concatenated in the with closure will be prefixed with that pattern and handler. Once we concatenate the last addRoute(), then we finalize the closure with an .endWith(); demarcation. BOOM! The patterns look so much manageable and declarable.
URL Mapping Namespaces
You can now create a-la-carte namespaces for URL routes. Namespaces are cool groupings of routes according to a specific URL entry point. So you can say that all URLs that start with /testing will be found in the testingnamespace and it will iterate through the namespace routes until it matches one of them. Much how modules work, where you have a module entry point, now you can create virtual entry point to ANY route by namespacing it. This route can be a module a non-module, package, or whatever you like.
You start off by registering the namespace using our new addNamespace(pattern, namespace) method:
addNamespace(pattern="/testing", namespace="test"); addNamespace(pattern="/news", namespace="blog");
Once a namespace is registered you can add routes to it via the addRoute() method or the with() closure.
// Via addRoute addNamespace(pattern="/news", namespace="blog") .addRoute(pattern="/",handler="blog",action="index",namespace="blog") .addRoute(pattern="/:year-numeric?/:month-numeric?/:day-numeric?",handler="blog",action="index",namespace="blog"); // Via with closure addNamespace(pattern="/news", namespace="blog"); with(namespace="blog", handler="blog") .addRoute(pattern="/",action="index") .addRoute(pattern="/:year-numeric?/:month-numeric?/:day-numeric?",action="index"); .endWith();
ORM Integration
- Updated the Base ORM Service createCriteriaQuery to accept a new convention; if an array element is a simple value it denotes to create another association query via createCriteria() for association criteria queries (see Hibernate Association Criteria Queries).
- The method executeQuery() now has a unique argument for retreiving a unique object
- New method: getEntityGivenName(entity) to retrieve the name of the entity when you only have an instance of the entity.
- New property: defaultAsQuery on the Base ORM Service and Virtual Entity Service so that you specify whether list(), createCriteriaQuery() and executeQuery() will return a query by default or an array of objects.
- Migrated transactioning to native ColdFusion transactions.
- New orm configuration structure that enables entity injection and future orm integrations. This is a compatibility item also as the Autowire interceptor has been deprecated.
orm = { injection = { // enable entity injection enabled = true, // a list of entity names to include in the injections include = "", // a list of entity names to exclude from injection exclude = "" } }
Flash RAM Updates
The Flash RAM has been considerably updated and you can now configure all of its operations at a granular level.
- Auto-save of Flash RAM on request end is now automatic
- You can now configure the Flash RAM via the new flash configuration structure in your ConfigurationCFC
// flash scope configuration flash = { scope = "session,client,cluster,ColdboxCache,or full path", properties = {}, // constructor properties for the flash scope implementation inflateToRC = true, // automatically inflate flash data into the RC scope inflateToPRC = false, // automatically inflate flash data into the PRC scope autoPurge = true, // automatically purge flash data for you autoSave = true // automatically save flash scopes at end of a request and on relocations. };
Well, if you want all the goodness, then head over to our What's New in 3.5.0 so you can start playing with this release. Please note that this is a beta release and our final release will most likely occurr after your feedback. Thanks again and enjoy!
Add Your Comment