What's New With 7.0.0
Discover the power of ColdBox 7.0.0
ColdBox 7.0.0 is a major release for the ColdBox HMVC platform. It has some dramatic new features as we keep pushing for more modern and sustainable approaches to web development and tons of bug fixes and improvements.
We break down the major areas of development below, and you can also find the full release notes per library at the end.
Engine Support
This release drops support for Adobe 2016 and adds support for Adobe 2023 and Lucee 6 (Beta). Please note that there are still issues with Adobe 2023 as it is still in Beta.
ColdBox CLI
We now have an official CLI for ColdBox, which lives outside CommandBox. It will always be included with CommandBox, but it now has its own life cycles, and it will support each major version of ColdBox as well.
The new CLI has all the previous goodness but now also v7 support and many other great features like migration creation, API testing, and more. You can find the source for the CLI here: https://github.com/coldbox/coldbox-cli
VSCode ColdBox Extension
We have updated our VSCode ColdBox extension now to support the latest version of snippets and skeletons for code generation.
Application Templates
All the application templates have been updated in our ColdBox Templates Org: https://github.com/coldbox-templates. They have been updated to support our LTS strategy, so now we can have templates based on each major iteration of ColdBox.
Here is a listing of the latest supported templates:
Template | Slug | Description |
---|---|---|
Default |
| The default ColdBox application template |
Elixir |
| The |
Modern (experimental) |
| A fresh new approach to ColdBox applications that are non-root based. Still experimental |
Rest |
| A base REST API using ColdBox |
Rest HMVC |
| An HMVC REST API using modules |
Super Simple |
| Barebones conventions baby! |
WireBox Updates
WireBox has gotten tons of love in this release, with several additions, bug fixes, and improvements.
Transient Request Cache
This feature is one of the most impactful for applications that leverage DI on transient objects, especially ORM-related applications. WireBox will now, by default, cache the signatures of the injections and delegations for you, so they are only done once per instance type. This addition has brought speed improvements of over 585% in Lucee and Adobe ColdFusion. You read that right, 585% performance increases. This is really a game changer for ORM-heavy applications that use DI and delegations. Go try it; you don't have to do a thing. Install and run it!
If this is not for you or there are issues in your system because of it, we have a setting for it to turn it off. Open the WireBox.cfc
binder and add it as a config item.
You can also disable the cache on a per-CFC basis by using the transientCache=false
annotation in your component declaration:
Known Issues
Even though the transient cache can help tremendously with performance, there is a price to pay. All the injections and delegations will be cached on a per-cfc definition basis. Thus, if you are injecting transients, those transients will become singletons. Therefore you have two options to alleviate this side effect:
Disable the cache entirely (The heavy-handed approach)
Disable the cache on that specific entity via the
transientCache
annotation (The surgical approach)Add the transient injection as a
provider
injection (The ninja approach)Add the property as a lazy property and add a builder that will construct it when called (The Jedi approach)
WireBox Delegators
WireBox Delegators
WireBox supports the concept of object delegation in a simple, expressive DSL. You can now add a delegate
annotation to injections or use the delegates
annotations to components to inject and absorb the object's methods into yourself.
In object-oriented programming, an object delegator is a programming technique where an object delegates some of its responsibilities to another object. The delegating object passes the responsibility for handling a particular task to the delegate object. This allows the delegating object to focus on its core responsibilities while the delegate object handles the delegated task.
Basically, a way to inject/proxy calls from one object to the other and avoid the overuse of inheritance, and avoid runtime mixins. WireBox provides a set of rules for method lookup and dispatching that will allow you to provide delegation easily in your CFML applications. This feature is similar to traits in PHP or object delegators in Kotlin.
You can use it to encapsulate behavior on small, focused, and testable classes that can be brought in as traits into ANY component without abusing inheritance. In contrast, object delegation is a more flexible approach that allows objects to delegate tasks to any other object, regardless of its class hierarchy. Finally, object delegation can help to improve code performance by allowing objects to use specialized delegate objects for specific tasks.
Let's look at an example of how we would use delegation without this feature:
Now let's look at the computer
As you can see, in the traditional approach we must type and inject and know every detail of the delegated methods. Now let's delegalize it via WireBox:
Or use the shorthand notation via the delegates
annotation of components:
You can also do prefixes, suffixes, method includes, excludes, and even add as many delegates as you want:
Read more about delegates here: https://wirebox.ortusbooks.com/usage/wirebox-delegators
Core Delegates
Core Delegates
Now that we have seen what delegators are, WireBox offers core delegators to your application via the @coreDelegates
namespace
Async - This delegate is useful to interact with the AsyncManager and the most used functionality for asynchronous programming
DateTime - Leverage the date time helper
Env - Talk to environment variables
Flow - Several fluent flow methods
JsonUtil - JSON utilities
StringUtil - String utilities
Population - Population utilities
So let's say you have a service that needs to populate objects and work with the system environment:
WireBox Property Observers
WireBox Property Observers
WireBox supports the concepts of component property observers. Meaning that you can define a function that will be called for you when the setter
for that property has been called and thus observe the property changes.
You will accomplish this by tagging a property with an annotation called observed
then by convention, it will look for a function called: {propertyName}Observer
by convention. This function will receive three arguments:
newValue
: The value is set into the propertyoldValue
: The old value of the property, including nullproperty
: The name of the property
If you don’t like the convention and want to name the function as you see fit, then you can place the value of the observed annotation as the function's name to call.
property name="data" observed="myObserver"
Read more here: https://wirebox.ortusbooks.com/advanced-topics/property-observers
WireBox Lazy Properties
WireBox Lazy Properties
WireBox supports the concept of marking properties in your components as lazy
. This will allow the property to be constructed ONCE when requested ONLY (lazy loaded). This way, you can take advantage of the construction of the property being lazy-loaded.
Internally, we will generate a getter method for you that will make sure to construct your property via a builder function you will provide, lock the request (by default), store it in the variables
scope, and return it to you.
Note: With lazy properties, you must use the getter only to retrieve the property
Read more about Lazy Properties here: https://wirebox.ortusbooks.com/advanced-topics/lazy-properties
onInjectorMissingDependency
event
onInjectorMissingDependency
eventA new event called onInjectorMissingDependency
is now registered in Wirebox. It will be called whenever a dependency cannot be located. The data
sent into the event will contain:
name - The name of the requested dependency
initArguments - The
init
arguments, if passedtargetObject - The target object that requested the dependency
injector - The injector in use building the dependency
If you return in the data
struct an element called, instance
we will return that as the dependency, else the normal exception will be thrown.
Population Enhancements
The object populator now caches ORM entity maps, so they are only loaded once, and the population with ORM objects accelerates tremendously.
The object populator caches relational metadata for a faster population of the same type of objects
Mass Population Config: this.population
this.population
This new convention allows for objects to encapsulate the way the mass population of data is treated. This way, you don’t have to scrub or pass include excludes lists via population arguments; it can all be nicely encapsulated in the targeted objects:
The populator will look for a this.population
struct with the following keys:
include
: an array of property names to allow population ONLYexclude
: an array of property names to NEVER allow population
The population methods also get a new argument called: ignoreTargetLists
which defaults to false, meaning it inspects the objects for these population markers. If you pass it as true
then the markers will be ignored. This is great for the population of objects from queries or an array of structs or mementos that YOU have control of.
Inline Configuration
You can now instantiate an Injector with the binder
argument being the config structure instead of creating a binder. This will allow you to create injectors and pass the configuration structure as well:
Injector Names
Each injector can now have a human-friendly name via the name
property
Clear Objects From Scopes
You can also now use a clear( key )
method in ALL scopes so you can remove a-la-carte objects from any supported scope.
Root Injector
In a ColdBox or WireBox context, there will always be a root
injector in a hierarchy. This root injector can have children, and all of these children can have a direct link to its root via a hasRoot() and getRoot()
methods.
Methods
hasRoot()
getRoot()
setRoot()
Injection DSL
There are also injection DSLs for retrieving the root injector:
Schedule Tasks Revamped
Thanks to Giancarlo Gomez, scheduled tasks get a big revamp in ColdBox 7. Here are the updates:
New Properties
annually
You can now task on an annual basisdelayTimeUnit
used to work with delays regardless of the setting in the chaindebug
used for debug output during task executionsfirstBusinessDay
boolean to flag the task as on the first business day schedulelastBusinessDay
boolean to flag the task as on the last business day scheduletaskTime
log of time of day for first business day and last business day tasksstartTime
limits tasks to run on or after a specific time of dayendTime
limits tasks to run on or before a specific time of daymeta
The user can use this struct to store any data for the task ( helpful for building UIs and not having to manage outside of the task )stats.nextRun
ColdBox now determines the next run for the task in its scheduler interval
Updated Functions
run( boolean force = false )
Added theforce
argument, to allow running the task on demand, even if it is paused.validateTime()
Sets minutes if missing from time entry and returns time value if successful - removes a lot of repetitive code
New Functions
debug()
Used for setting debug setting ( can also be done in task init )startOnTime()
used to set variables.startTimeendOnTime()
used to set variables.endTimebetween()
used to set variables.startTime and variables.endTime in one callsetMeta()
used to set variables.metasetMetaKey()
used to add / save a key to variables.metadeleteMetaKey()
used to delete a key from variables.meta
New Private Functions
getLastBusinessDayOfTheMonth()
getFirstBusinessDayOfTheMonth()
setInitialNextRunTime()
setInitialDelayPeriodAndTimeUnit()
setNextRunTime()
debugLog()
Module Updates
Config Object Override
In ColdBox 7, you can now store the module configurations outside of the config/Coldbox.cfc
. Especially in an application with many modules and many configs, the modulesettings
would get really unruly and long. Now you can bring separation. This new convention will allow module override configurations to exist as their own configuration file within the application’s config folder.
The configuration CFC will have one configure()
method that is expected to return a struct of configuration settings as you did before in the moduleSettings
Injections
Just like a ModuleConfig
this configuration override also gets many injections:
Env Support
This module configuration object will also inherit the ModuleConfig.cfc
behavior that if you create methods with the same name as the environment you are on, it will execute it for you as well.
Then you can change the original
struct as you see fit for that environment.
Module Injectors
We have an experimental feature in ColdBox 7 to enable per-module injectors. This will create a hierarchy of injections and dependency lookups for modules. This is in preparation for future capabilities to allow for multi-named modules in an application. In order to activate this feature you need to use the this.moduleInjector = true
in your ModuleConfig.cfc
Once enabled, please note that your module injector will have a unique name, and a link to the parent and root injectors and will ONLY know about itself and its children. Everything will be encapsulated under itself. No more global dependencies, we are now in module-only dependencies. This means each module must declare its dependencies beforehand.
ModuleConfig Injections
If the module injector is enabled, you will also get different injections in your ModuleConfig
Injection | Description |
---|---|
| The root injector binder |
| The root injector or global injector. |
| This is now a reference to the module's injector. |
Root Helpers
The supertype also has methods to interact with the root and module injector getRootWireBox()
and getWireBox() for the module injector.
Injection Awareness
Every module also can inject/use its models
without the need to namespace them.
The injector will look into the models
of the module first and then it's children. Parent lookups are not guaranteed yet.
Please note that this is still experimental and there could be issues of not finding models or DSLs.
Config/Settings Awareness
You can now use the {this}
placeholder in injections for module configurations-settings, and ColdBox will automatically replace it with the current module it's being injected in:
This is a great way to keep your injections clean without adhering to the module name.
Interceptor listen()
Order
listen()
OrderYou can now use listen( closure, point )
or listen( point, closure)
when registering closure interception points.
ColdBox Delegates
Since WireBox introduced delegates, we have taken advantage of this great reusable feature throughout ColdBox. We have also created several delegates for your convenience that can be used via its name and the @cbDelegates
namespace:
Delegate | Purpose |
---|---|
| Methods to let you know in which tier you are on and more |
| Announce interceptions |
| Locate files and/or directories |
| Populate objects |
| Render views and layouts |
| Interact with ColdBox/Module Settings |
Let's say you have a security service that needs to get settings, announce events and render views:
Here you can find more information about the CBDelegates: https://s3.amazonaws.com/apidocs.ortussolutions.com/coldbox/7.0.0/coldbox/system/web/delegates/package-summary.html
User Identifier Providers
In previous versions of ColdBox, it would auto-detect unique request identifiers for usage in Flash Ram, storages, etc., following this schema:
If we have
session
enabled, use thejessionId
orsession
URL TokenIf we have cookies enabled, use the
cfid/cftoken
If we have in the
URL
thecfid/cftoken
Create a unique request-based tracking identifier:
cbUserTrackingId
However, you can now decide what will be the unique identifier for requests, flash RAM, etc by providing it via a coldbox.identifierProvider
as a closure/lambda in your config/Coldbox.cfc
If this closure exists, ColdBox will use the return value as the unique identifier. A new method has also been added to the ColdBox Controller so you can retrieve this value:
The supertype as well so all handlers/layouts/views/interceptors can get the user identifier:
App Mode Helpers
ColdBox 7 introduces opinionated helpers to the FrameworkSuperType
so you can determine if you are in three modes: production, development, and testing by looking at the environment
setting:
Mode | Environment |
---|---|
|
|
|
|
|
|
You can also find these methods in the controller
object.
These super-type methods delegate to the ColdBox Controller. So that means that if you needed to change their behavior, you could do so via a Controller Decorator.
You can also use them via our new AppModes@cbDelegates
delegate in any model:
Resource Route Names
When you register resourceful routes now, they will get assigned a name so you can use it for validation via routeIs()
or for route link creation via route()
Verb | Route | Event | Route Name |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Baby got back()!
back()!
The framework super type now sports a back()
function so you can use it to redirect back to the referer in a request.
Here is the method signature:
RequestContext
Routing/Pathing Enhancements
RequestContext
Routing/Pathing EnhancementsThe RequestContext has a few new methods to assist you when working with routes, paths, and URLs.
Method | Purpose |
---|---|
| Verify if the passed |
| Returns the entire URL, including the protocol, host, mapping, path info, and query string. |
| Return the relative path of the current request. |
| Get all of the URL path segments from the requested path. |
| Get a single path segment by position |
Native DateHelper
The FrameworkSuperType
now has a getDateTimeHelper(), getIsoTime()
methods to get access to the ColdBox coldbox.system.async.time.DateTimeHelper
to assist you with all your date/time/timezone needs and generate an iso8601 formatted string from the incoming date/time.
You can also use the new date time helper as a delegate in your models:
Check out the API Docs for the latest methods in the helper
View variables
Scope Variables
variables
Scope VariablesYou can now influence any view by injecting your own variables into a view's variables
scope using our new argument: viewVariables
. This is great for module developers that want native objects or data to exist in a view's varaibles
scope.
Then you can use the wire
and client
objects in your views natively:
Whoops! Upgrades
Whoops got even more love:
SQL Syntax Highlighting
JSON Pretty Printing
JSON highlighting
Debug Mode to show source code
Production detection to avoid showing source code
Rendering performance improvements
RESTFul Exception Responses
The RESTFul handlers have been updated to present more useful debugging when exceptions are detected in any call to any resource. You will now get the following new items in the response:
environment
A snapshot of the current routes, URLs and event
exception
A stack frame, detail, and type
ColdBox DebugMode
DebugMode
ColdBox now has a global debugMode
setting, which is used internally for presenting sources in Whoops and extra debugging parameters in our RESTFul handler. However, it can also be used by module developers or developers to dictate if your application is in debug mode or not. This is just a fancy way to say we trust the user doing the requests.
The default value is false
You also have a inDebugMode()
method in the main ColdBox Controller and the FrameworkSuperType that will let you know if you are in that mode or not.
You can also use the AppModes@cbDelegates
delegate to get access to the inDebugMode
and other methods in your models:
Testing Enhancements
Unload ColdBox is now false
All integration tests now won't unload ColdBox after execution. Basically, the this.unloadColdBox = false
is now the default. This is to increase performance where each request run tries only to load the ColdBox virtual app once.
Environment Detection Method
All test bundles now get a getEnv()
method to retrieve our environment delegate so you can get env settings and properties:
LogBox Updates
JSON Pretty Output
All logging messages in LogBox are now inspected for simplicity or complexity. If any extra argument is a complex variable, then LogBox will now serialize the output to JSON, and it will prettify it. This will allow for your log messages in your console to now look pretty!
Exception Improvements
If exception objects are being sent to any logger, LogBox will detect it and pretty print it back to the console instead of dumping a massive exception object.
Closure Logging
In previous versions, you had to use the canxxxx
method to determine if you can log at a certain level:
In ColdBox 7, you can use a one-liner by leveraging a lambda function:
Integration Testing Request Timeout
The RequestContext now has a setRequestTimeout()
function that can provide timeouts in your application, and when in testing mode, it will mock the request timeout. This is essential to encapsulate this setting as if you have requested timeout settings in your app; they will override the ones in testing.
Release Notes
You can find the release notes on the page below:
Release NotesLast updated