Only this pageAll pages
Powered by GitBook
Couldn't generate the PDF for 268 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

5.x

Loading...

Intro

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

For Newbies

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Getting Started

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

The Basics

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Introduction

   ____      _     _ ____            
  / ___|___ | | __| | __ )  _____  __
 | |   / _ \| |/ _` |  _ \ / _ \ \/ /
 | |__| (_) | | (_| | |_) | (_) >  < 
  \____\___/|_|\__,_|____/ \___/_/\_\

ColdBox Manual - v5.x

ColdBox Platform

ColdBox is a conventions-based HMVC development framework for ColdFusion (CFML). It provides a set of reusable code and tools that can be used to increase your development productivity as well as a development standard for working in team environments.

ColdBox is natively based on modular architecture which helps address most infrastructure concerns of typical web applications and thus called an HMVC framework.

Versioning

ColdBox is maintained under the Semantic Versioning guidelines as much as possible.Releases will be numbered with the following format:

<major>.<minor>.<patch>

And constructed with the following guidelines:

  • Breaking backward compatibility bumps the major (and resets the minor and patch)

  • New additions without breaking backward compatibility bumps the minor (and resets the patch)

  • Bug fixes and misc changes bumps the patch

License

The ColdBox Platform is open source and licensed under the Apache 2 License. If you use ColdBox, please try to make mention of it in your code or web site or add a Powered By Coldbox icon.

  • Copyright by Ortus Solutions, Corp

  • ColdBox is a registered trademark by Ortus Solutions, Corp

Info: The ColdBox Websites, Documentation, logo and content have a separate license and they are a separate entity.

Discussion & Help

The ColdBox help and discussion group can be found here: https://groups.google.com/forum/#!forum/coldbox

Reporting a Bug

We all make mistakes from time to time :) So why not let us know about it and help us out. We also love pull requests, so please star us and fork us: https://github.com/coldbox/coldbox-platform

  • By Email: [email protected]

  • By Jira: https://ortussolutions.atlassian.net/browse/COLDBOX

Professional Open Source

Ortus Solutions, Corp

ColdBox is a professional open source software backed by Ortus Solutions, Corp offering services like:

  • Custom Development

  • Professional Support & Mentoring

  • Training

  • Server Tuning

  • Security Hardening

  • Code Reviews

  • Much More

Resources

  • Official Site: https://www.coldbox.org

  • Source Code: https://github.com/coldbox/coldbox-platform

  • Bug Tracker: https://ortussolutions.atlassian.net/browse/COLDBOX

  • Twitter: @coldbox

  • Facebook: https://www.facebook.com/coldboxplatform

  • Google+: https://www.google.com/+ColdboxOrg

  • Vimeo Channel: https://vimeo.com/channels/coldbox

HONOR GOES TO GOD ABOVE ALL

Because of His grace, this project exists. If you don't like this, then don't read it, it's not for you.

"Therefore being justified by faith, we have peace with God through our Lord Jesus Christ: By whom also we have access by faith into this grace wherein we stand, and rejoice in hope of the glory of God." Romans 5:5

Introduction

ColdBox Manual - v5.0.0

ColdBox is a development framework for (). It provides a set of reusable code and tools that can be used to increase your development productivity as well as a development standard for working in team environments.

ColdBox is natively based on which helps address most infrastructure concerns of typical web applications and thus called an HMVC framework.

Versioning

ColdBox is maintained under the guidelines as much as possible.Releases will be numbered with the following format:

And constructed with the following guidelines:

  • Breaking backward compatibility bumps the major (and resets the minor and patch)

  • New additions without breaking backward compatibility bumps the minor (and resets the patch)

  • Bug fixes and misc changes bumps the patch

License

The ColdBox Platform is open source and licensed under the License. If you use ColdBox, please try to make mention of it in your code or web site or add a Powered By Coldbox icon.

  • Copyright by Ortus Solutions, Corp

  • ColdBox is a registered trademark by Ortus Solutions, Corp

Info: The ColdBox Websites, Documentation, logo and content have a separate license and they are a separate entity.

Discussion & Help

The ColdBox help and discussion group can be found here:

Reporting a Bug

We all make mistakes from time to time :) So why not let us know about it and help us out. We also love pull requests, so please star us and fork us:

  • By Email:

  • By Jira:

Professional Open Source

ColdBox is a professional open source software backed by offering services like:

  • Custom Development

  • Professional Support & Mentoring

  • Training

  • Server Tuning

  • Security Hardening

  • Code Reviews

Resources

  • Official Site:

  • Source Code:

  • Bug Tracker:

  • Twitter:

  • Facebook:

  • Google+:

  • Vimeo Channel:

HONOR GOES TO GOD ABOVE ALL

Because of His grace, this project exists. If you don't like this, then don't read it, it's not for you.

"Therefore being justified by faith, we have peace with God through our Lord Jesus Christ: By whom also we have access by faith into this grace wherein we stand, and rejoice in hope of the glory of God." Romans 5:5

What's New With 5.1.4

Short and sweet release!

Bug

  • [COLDBOX-718] - Left one encodeforhtml in textarea that was missing.

What's New With 5.1.3

This is a patch release for ColdBox

Bugs

  • [COLDBOX-715] - Elvis operator inconsistencies on Adobe Engines, please Adobe, patch the engines and fix your compiler!

Improvements

  • [COLDBOX-237] - Some HTMLHelper method still need escaping as certain values should never be HTML

  • [COLDBOX-716] - determine session/client state via CF getApplicationMetadata() instead of isDefined() to avoid load issues for flash ram

  • [COLDBOX-717] - RemotingUtil converted to cfscript #367

Conventions

The core conventions delineate the contract between ColdBox and you for file/directory locations and more. Below is a table of the core conventions:

Directory/File Conventions

  • config/Coldbox.cfc - Your application configuration object (optional)

  • config/Router.cfc - Your application URL Router (optional)

  • handlers - This holds the app's controllers

  • layouts - Your HTML layouts

  • models - This holds your app's CFCs

  • modules - This holds the CommandBox tracked modules

  • modules_app - This holds your app's modules

  • views - Your HTML views will go here

Execution Conventions

Convention

Default Value

Description

Default Event

main.index

The default event to execute when no event is specified

Default Action

index()

The default action to execute in an event handler controller if none is specified

Default Layout

layouts/Main.cfm

The default system layout to use

Conventions

This element defines custom conventions for your application. By default, the framework has a default set of conventions that you need to adhere too. However, if you would like to implement your own conventions for a specific application, you can use this setting, otherwise do not declare it:

//Conventions
conventions = {
    handlersLocation = "controllers",
    viewsLocation      = "views",
    layoutsLocation  = "views",
    modelsLocation      = "model",
    modulesLocation  = "modules",
    eventAction      = "index"
};

Layouts

The layouts array element is used to define implicit associations between layouts and views/folders, this does not mean that you need to register ALL your layouts. This is a convenience for pairing them, we are in a conventions framework remember.

Before any renderings occur or lookups, the framework will check this array of associations to try and match in what layout a view should be rendered in. It is also used to create aliases for layouts so you can use aliases in your code instead of the real file name and locations.

//Register Layouts
layouts = [
    { 
        // The alias of a layout
        name="",
        // The layout file
        file="",
        // A list of view names to render within this layout
        views="",
        // A list of regex names to match a view
        folders=""
    }

    // Examples
    { name="tester",file="Layout.tester.cfm",views="vwLogin,test",folders="tags,pdf/single"    },
    { name="login",file="Login.cfm",folders="^admin/security"}
];

60 Minute Quick Start

This guide has been designed to get you started with ColdBox in fewer than 60 minutes. We will take you by the hand and help you build a RESTFul application in 60 minutes or fewer. After you complete this guide, we encourage you to move on to the Getting Started Guide and then to the other guides in this book.

Requirements

  • Please make sure you download and install the latest CommandBox CLI. We will show you how in the Installing ColdBox section.

  • Grab a cup of coffee

  • Get comfortable

Modules

The modules structure is used to configure the behavior of the ColdBox Modules.

modules = {
    // Will auto reload the modules in each request. Great for development but can cause some loading/re-loading issues
    autoReload = true,
    // An array of modules to load ONLY
    include = [],
    // An array of modules to EXCLUDE for operation
    exclude = [ "paidModule1", "paidModule2" ]
};

Danger Please be very careful when using the autoReload flag as module routing can be impaired and thread consistency will also suffer. This is PURELY a development flag that you can use at your own risk.

ModuleSettings

This structure is used to house module configurations. Please refer to each module's documentation on how to create the configuration structures. Usually the keys will match the name of the module to be configured.

component {

     function configure() {

         moduleSettings = {
             myModule = {
                someSetting = "overridden"
             }
        };
    }
}

LayoutSettings

This structure allows you to define a system-wide default layout and view.

//Layout Settings
layoutSettings = {
    // The default layout to use if NOT defined in a request
    defaultLayout = "Main.cfm",
    // The default view to use to render if NOT defined in a request
    defaultView   = "youForgot.cfm"
};

Hint Please remember that the default layout is Main.cfm

HTML Base Tag

The base tag in HTML allows you to tell the browser what is the base URL for assets in your application. This is something that is always missed when using frameworks that enable routing.

base tag defines the base location for links on a page. Relative links within a document (such as <a href="someplace.html"... or <img src="someimage.jpg"... ) will become relative to the URI specified in the base tag. The base tag must go inside the head element.

We definitely recommend using this HTML tag reference as it will simplify your asset retrievals.

<base href="#event.getHTMLBaseURL()#">

Caution If you do not use this tag, then every asset reference must be an absolute URL reference.

Validation

ColdBox Core MVC does not have validation built-in but it is implemented via the offical core cbValidation module. You can easily install the module in your application via:

box install cbvalidation

You can find much more information about this module in the following resources:

  • Source: https://github.com/coldbox/cbox-validation

  • Documentation: https://github.com/coldbox-modules/cbox-validation/wiki

  • ForgeBox : http://forgebox.io/view/cbvalidation

Rendering External Views

So what if I want to render a view outside of my application without using the setting explained above? Well, you use the renderExternalView() method.

<cfoutput>#renderExternalView(view='/myViewsMapping/tags/footer')#</cfoutput>

Settings

These are custom application settings that you can leverage in your application.

// Custom Settings
settings = {
    useSkins = true,
    myCoolArray = [1,2,3,4],
    skinsPath = "views/skins",
    myUtil = createObject("component","#appmapping#.model.util.MyUtility")
};

You can read our Using Settings section to discover how to use all the settings in your application.

   ____      _     _ ____            
  / ___|___ | | __| | __ )  _____  __
 | |   / _ \| |/ _` |  _ \ / _ \ \/ /
 | |__| (_) | | (_| | |_) | (_) >  < 
  \____\___/|_|\__,_|____/ \___/_/\_\
<major>.<minor>.<patch>
conventions-based
HMVC
ColdFusion
CFML
modular architecture
Semantic Versioning
Apache 2
https://groups.google.com/forum/#!forum/coldbox
https://github.com/coldbox/coldbox-platform
[email protected]
https://ortussolutions.atlassian.net/browse/COLDBOX
Ortus Solutions, Corp
Much More
https://www.coldbox.org
https://github.com/coldbox/coldbox-platform
https://ortussolutions.atlassian.net/browse/COLDBOX
@coldbox
https://www.facebook.com/coldboxplatform
https://www.google.com/+ColdboxOrg
https://vimeo.com/channels/coldbox
ColdBox Platform
Ortus Solutions, Corp

What's New With 5.1.1

This is a patch release for ColdBox.

Bug

  • [] - regression onmissingmethod on html helper method was public and changed to private

  • [] - viewModule logic was not working at all yet again.

Improvement

  • [] - Remove setting throwOnInvalidInterceptionStates, makes no sense anymore

  • [] - Moved order of event manager states the injector provides to a ColdBox app so the binder can listen on itself

About This Book

The source code for this book is hosted in GitHub: . You can freely contribute to it and submit pull requests. The contents of this book is copyright by and cannot be altered or reproduced without author's consent. All content is provided "As-Is" and can be freely distributed.

  • The majority of code examples in this book are done in cfscript.

  • The majority of code generation and running of examples are done via CommandBox: The ColdFusion (CFML) CLI, Package Manager, REPL -

  • All ColdFusion examples designed to run on the open soure Lucee Platform or Adobe ColdFusion 11+

External Trademarks & Copyrights

Flash, Flex, ColdFusion, and Adobe are registered trademarks and copyrights of Adobe Systems, Inc.

Notice of Liability

The information in this book is distributed “as is”, without warranty. The author and Ortus Solutions, Corp shall not have any liability to any person or entity with respect to loss or damage caused or alleged to be caused directly or indirectly by the content of this training book, software and resources described in it.

Contributing

We highly encourage contribution to this book and our open source software. The source code for this book can be found in our where you can submit pull requests.

Charitable Proceeds

10% of the proceeds of this book will go to charity to support orphaned kids in El Salvador - . So please donate and purchase the printed version of this book, every book sold can help a child for almost 2 months.

Shalom Children's Home

Shalom Children’s Home () is one of the ministries that is dear to our hearts located in El Salvador. During the 12 year civil war that ended in 1990, many children were left orphaned or abandoned by parents who fled El Salvador. The Benners saw the need to help these children and received 13 children in 1982. Little by little, more children came on their own, churches and the government brought children to them for care, and the Shalom Children’s Home was founded.

Shalom now cares for over 80 children in El Salvador, from newborns to 18 years old. They receive shelter, clothing, food, medical care, education and life skills training in a Christian environment. The home is supported by a child sponsorship program.

We have personally supported Shalom for over 6 years now; it is a place of blessing for many children in El Salvador that either have no families or have been abandoned. This is good earth to seed and plant.

Adding A Layout

Every time the framework renders a view, it will try to leverage the default layout which is located in layouts/Main.cfm by convention. This is an HTML file that gives format to your output and contains the location of where the view you want should be rendered.

Layout Code

This location is identified by the following code:

The call to the renderView() method with no arguments tells the framework to render the view that was set using event.setView(). This is called a rendering region. You can use as many rendering regions within layouts or even within views themselves.

Named Regions: The setView() method even allows you to name these regions and then render them in any layout or other views using their name.

Creating A Layout

Let's create a new simple layout with two rendering regions. Open up CommandBox and issue the following commands:

Open the layouts/Funky.cfm layout and let's modify it a bit by adding the footer view as a rendering region.

Using The Layout

Now, let's open the handler we created before called handlers/hello.cfc and add some code to use our new layout explicitly via adding a layout argument to our setView() call.

Go execute the event now: http://localhost:{port}/hello/index and you will see the view rendered with the words funky layout and footer view at the bottom. Eureka, you have now created a layout.

You can also leverage the function event.setLayout( "Funky" ) to change layouts and even concatenate the calls:

event.setView( "hello/index" ) .setLayout( "Funky" );

Next Steps

Congratulations! Did you finish this guide in less than 60 minutes? If you did please leave us some great feedback below. If you didn't, then please do tell us why, we would love to improve our guides.

You can now move on to the next level in becoming a ColdBox Guru! Choose your path below:

Getting Help

If you run into issues or just have questions, please jump on our and our and ask away.

ColdBox is Professional Open Source under the Apache 2.0 license. We'd love to have your help with the product.

Configuration

In order to create a ColdBox application you must adhere to some naming conventions and a pre-set directory structure. This is needed in order for ColdBox to find what it needs and to help you find modules more easily instead of configuring every single area of your application.

ColdBox relies on conventions instead of configurations.

You can also configure many aspects of ColdBox, third-party modules and application settings in our programmtic configuration object: ColdBox.cfc.

Reinitializing An Application

Please note that anytime you make any configuration changes or there are things in memory you wish to clear out, you will be using a URL action that will tell the ColdBox to reinitialize the application. This special URL variable is called fwreinit and can be any value or a specific password you setup in the .

You can also use CommandBox to reinit your application if you are using its embedded server:

CacheBox

The CacheBox structure is based on the , and it allows you to customize the caches in your application. Below are the main keys you can fill out, but we recommend you review the CacheBox documentation for further detail.

Info : We would recommend you create a config/CacheBox.cfc and put all your caching configuration there instead of in the main ColdBox configuration file. This will give you further portability and decoupling.

ConfigFile

An absolute or relative path to the CacheBox configuration CFC or XML file to use instead of declaring the rest of the keys in this structure. So if you do not define a cacheBox structure, the framework will look for the default value: config/CacheBox.cfc and it will load it if found. If not found, it will use the default CacheBox configuration found in /coldbox/system/web/config/CacheBox.cfc

ScopeRegistration

A structure that enables scope registration of the CacheBox factory in either server, cluster, application or session scope.

DefaultCache

The configuration of the default cache which will have an implicit name of default which is a reserved cache name. It also has a default provider of CacheBox which cannot be changed.

Caches

A structure where you can create more named caches for usage in your CacheBox factory.

WireBox

This configuration structure is used to configure the dependency injection framework embedded in ColdBox.

binder

The location of the WireBox configuration binder to use for the application. If empty, we will use the binder in the config folder called by conventions: WireBox.cfc

singletonReload

A great flag for development. If enabled, WireBox will flush its singleton objects on every request so you can develop without any headaches of reloading.

Warning : This operation can cause some thread issues and it is only meant for development. Use at your own risk.

Flash

This directive is how you will configure the for operation. Below are the configuration keys and their defaults:

Interceptors

This is an array of interceptor definitions that you will use to register in your application. The key about this array is that ORDER matters. The interceptors will fire in the order that you register them whenever their interception points are announced, so please watch out for this caveat. Each array element is a structure that describes what interceptor to register.

Warning : Important: Order of declaration matters! Also, when declaring multiple instances of the same CFC (interceptor), make sure you use the name attribute in order to distinguish them. If not, only one will be registered (the last one declared).

Requirements

Routing is enabled by default in the ColdBox application templates in order to work with URL's like this:

http://localhost/index.cfm/home/about

As you can see they still contain the index.cfm in the URL. In order to enable full URL rewrites that eliminates that index.cfm you must have a rewrite enabled webserver like Apache, nginx or IIS or a Java rewrite filter which ships with CommandBox by default.

http://localhost/home/about

CommandBox has built in rewrites powered by Tuckey and you can enable a server with rewrites by running:

Caution Some J2EE servlet containers do not support the forwarding of SES parameters via the routing template (index.cfm) out of the box. You might need to enable full URL rewriting either through a web server or a J2EE filter.

Some Resources

  • via .htaccess or configuration files (Free)

  • ISAPI rewrite filter for IIS (Paid)

  • native rewrite filter (Free)

  • native web server (free)

  • J2EE rewrite filter (free)

Routing Groups

There will be a time where your routes will become very verbose and you would like to group them into logical declarations. These groupings can also help you prefixes repetitive patterns in many routes with a single declarative construct. These needs are met with the group() method in the router.

The best way to see how it works is by example:

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 grouping action.

The options struct can contain any values that you can use within the closure. Grouping can also be very nice when creating , which is our next section.

Post Advices

With this interceptor you can intercept local event actions and execute things after the requested action executes. You can do it globally by using the postHandler() method or targeted to a specific action post{actionName}().

The arguments received by these interceptors are:

  • event : The request context reference

  • action : The action name that was intercepted

  • eventArguments : The struct of extra arguments sent to an action if executed via runEvent()

  • rc : The RC reference

  • prc : The PRC Reference

Exceptions & Only Lists

You can fine tune these interception methods by leveraging two public properties in the handler:

  • this.posthandler_only : A list of actions that the postHandler() action will fire ONLY!

  • this.posthandler_except : A list of actions that the postHandler() action will NOT fire on

View Helpers

This is a nifty little feature that enables you to create nice helper templates on a per-view, per-folder and per-application basis. If the framework detects the helper, it will inject it into the rendering view so you can use methods, properties or whatever. All you need to do is follow a set of conventions. Let's say we have a view in the following location:

Then we can create the following templates

  • homeHelper.cfm : Helper for the home.cfm view.

  • generalHelper.cfm : Helper for any view in the general folder.

homeHelper.cfm

That's it. Just append Helper to the view or folder name and there you go, the framework will use it as a helper for that view specifically. What can you put in these helper templates:

  • NO BUSINESS CODE

  • UI logic functions or properties

  • Helper functions or properties

  • Dynamic JavaScript or CSS

Hint External views can also use our helper conventions

Application wide helpers

You can also use the coldbox.viewsHelper directive to tell the framework what helper file to use for ALL views and layouts rendered:

Pre Advices

With this interceptor you can intercept local event actions and execute things before the requested action executes. You can do it globally by using the preHandler() method or targeted to a specific action pre{actionName}().

The arguments received by these interceptors are:

  • event : The request context reference

  • action : The action name that was intercepted

  • eventArguments : The struct of extra arguments sent to an action if executed via runEvent()

  • rc : The RC reference

  • prc : The PRC Reference

Here are a few options for altering the default event execution:

  • Use event.overrideEvent('myHandler.myAction') to execute a different event than the default.

  • Use event.noExecution() to halt execution of the current event

See the documentation for more details.

Exceptions & Only Lists

You can fine tune these interception methods by leveraging two public properties in the handler:

  • this.prehandler_only : A list of actions that preHandler() will ONLY fire on

  • this.prehandler_except : A list of actions that preHandler() will NOT fire on

<div id="maincontent">
#renderView()#
</div>
# Create a Funky layout
coldbox create layout name="Funky"

# Create a footer
coldbox create view name="main/footer"
<h1>funky Layout</h1>
<cfoutput>#renderView()#</cfoutput>

<hr>
<cfoutput>#renderView( "main/footer" )#</cfoutput>
function index( event, rc, prc ){
    // param an incoming variable.
    event.paramValue( "name", "nobody" );
    // set a private variable
    prc.when = dateFormat( now(), "full" );

    // set the view to render with our new layout
    event.setView( view="hello/index", layout="Funky" );
}
//Register interceptors as an array, we need order
interceptors = [

    { 
        // The CFC instantiation path
        class="",
        // The alias to register in WireBox, if not defined it uses the name of the CFC
        name="",
        // A struct of data to configure the interceptor with.
        properties={}
    }

    { class="mypath.MyInterceptor",
      name="MyInterceptor",
      properties={useSetterInjection=false}
    },
    { class="coldbox.system.interceptors.SES", name="MySES" }
];
/views
  /general
    +home.cfm
/views
  /general
    +home.cfm
    +homeHelper.cfm
    +generalHelper.cfm
<cfscript>
// facade to get i18n resource
function $r(){ return getResource(argumentCollection=arguments); }
// cool formatted date function
function today(){ return dateFormat(now(),"full"); }
</cfscript>
coldbox.viewsHelper = "includes/helpers/viewsHelper.cfm;
COLDBOX-703
COLDBOX-704
COLDBOX-705
COLDBOX-706
Learn about HMVC via ColdBox Modules
Learn about Dependency Injection
Learn about Caching
Learn about Logging
Learn about Testing
ColdBox Google Group
Slack team
// reinit with no password
index.cfm?fwreinit=1

// reinit with password
index.cfm?fwreinit=mypass
CommandBox>coldbox reinit
Bootstrapper
ColdBox configuration directive
//cachebox configuration
cachebox = {
    // Location of the configuration CFC for CacheBox
    configFile = "config/CacheBox.cfc",
    // Scope registration for CacheBox
    scopeRegistration = {enabled=true,scope=application,key=cacheBox},
    // Default Cache Configuration
    defaultCache  = "views",
    // Caches Configuration
    caches      = {}
};
CacheBox declaration DSL
// wirebox integration
wirebox = {
    binder = 'config.WireBox',
    singletonReload = true
};
WireBox
// flash scope configuration
flash = {
    // Available scopes are: session,client,cache,mock or your own class path
    scope = "session",
    // constructor properties for the flash scope implementation
    properties = {},
    // automatically inflate flash data into the RC scope at the beginning of a request
    inflateToRC = true, 
    // automatically inflate flash data into the PRC scope at the beginning of a request
    inflateToPRC = false, 
    // automatically purge flash data for you
    autoPurge = true, 
    // automatically save flash scopes at end of a request and on relocations.
    autoSave = true 
};
Flash RAM
server start --rewritesEnable
Apache mod_rewrite
Helicon Tech
IIS 7
nginx
Tuckey
function group( struct options={}, body );
route( pattern="/news", target="public.news.index" );
route( pattern="/news/recent", target="public.news.recent" );
route( pattern="/news/removed", target="public.news.removed" );
route( pattern="/news/add/:title", target="public.news.add" );
route( pattern="/news/delete/:slug", target="public.news.remove" );
route( pattern="/news/v/:slug", target="public.news.view" );
group( { pattern="/news", target="public.news." }, function(){
	route( "/", "index" )
	.route( "/recent", "recent" )
	.route( "/removed", "removed" )
	.route( "/add/:title", "add" )
	.route( "/delete/:slug", "remove" )
	.route( "/v/:slug", "view" );
} );
namespaces

What's New With 5.1.2

This is a patch release for ColdBox

Bugs/Regressions

  • [COLDBOX-711] - HTML helper cannot add fontawesome icons in button due to XSS enabled by default

  • [COLDBOX-712] - ColdBox shutdown code that uses CF mappings for modules fails on fwreinit

  • [COLDBOX-713] - addAsset throw error when called from handlers

Improvements

  • [COLDBOX-714] - Too many issues when encoding by default for HTML Helper, revert to non-encoded and provide ways to encode globally and a-la-carte

  • [COLDBOX-702] - Framework setting for the automatic deserialization of JSON payloads to the RC: jsonPayloadToRC

Automatic JSON Payload Setting

The automatic JSON to request collection feature defaults now to false to avoid backwards compatibility. You can easily turn it on via the setting: coldbox.jsonPayloadToRC

coldbox = {
    jsonPayloadToRC = true
}

HTML Helper Changes

The HTML Helper has been migrated to an internal module in this release. It allows you to configure it via the following configuration settings in your ColdBox.cfc.

moduleSettings = {
    htmlHelper = {
        // Base path for loading JavaScript files
        js_path = "",
        // Base path for loading CSS files
        css_path = "",
        // Auto-XSS encode values when using the HTML Helper output methods
        encodeValues = false
    }
}

Injection Shortcut

You can also now inject the HTML helper anywhere using it's injection DSL Shortcut of @HTMLHelper

LogBox

The logBox structure is based on the LogBox declaration DSL, see the LogBox Documentation for much more information.

//LogBox DSL
logBox = {
    // The configuration file without fileextension to use for operation, instead of using this structure
    configFile = "config/LogBox", 
    // Appenders
    appenders = {
        appenderName = {
            class="class.to.appender", 
            layout="class.to.layout",
            levelMin=0,
            levelMax=4,
            properties={
                name  = value,
                prop2 = value 2
            }
    },
    // Root Logger
    root = {levelMin="FATAL", levelMax="DEBUG", appenders="*"},
    // Granualr Categories
    categories = {
        "coldbox.system" = { levelMin="FATAL", levelMax="INFO", appenders="*"},
        "model.security" = { levelMax="DEBUG", appenders="console"}
    }
    // Implicit categories
    debug  = ["coldbox.system.interceptors"],
    info   = ["model.class", "model2.class2"],
    warn   = ["model.class", "model2.class2"],
    error  = ["model.class", "model2.class2"],
    fatal  = ["model.class", "model2.class2"],
    off    = ["model.class", "model2.class2"]
};

Info : If you do not define a logBox DSL structure, the framework will look for the default configuration file config/LogBox.cfc. If it does not find it, then it will use the framework's default logging settings.

ConfigFile

You can use a configuration CFC instead of inline configuration by using this setting. The default value is config/LogBox.cfc, so by convention you can just use that location. If no values are defined or no config file exists, the default configuration file is coldbox/system/web/config/LogBox.cfc.

Building Routable Links

In your views, layouts and handlers you can use the buildLink method provided by the request context object (event) to build routable links in your application.

buildLink(
    // Where to link to
    any to, 
    // Translate periods to slashes
    [boolean translate='true'], 
    // Force or un-force SSL, by default we keep the same protocol of the request
    [boolean ssl], 
    // Update the baseURL, usually used for testing
    [any baseURL=''], 
    // Append a query string to the URL, as convention name value-pairs
    [any queryString='']
)

Just pass in the routed URL or event and it will create the appropriate routed URL for you:

<a href="#event.buildLink( 'home.about' )#">About</a>
<a href="#event.buildLink( 'user.edit.id.#user.getID()#' )#">Edit User</a>

Inspecting The Current Route

The request context object (event) also has some handy methods to tell you the name or even the current route that was selected for execution:

  • getCurrentRouteName() - Gives you the name of the current route, if any

  • getCurrentRoute() - Gives you the currently executed route

  • getCurrentRoutedURL() - Gives you the complete routed URL pattern that matched the route

  • getCurrentRoutedNamespace() - Gives you the current routed namespace, if any

InterceptorSettings

This structure configures the interceptor service in your application.

//Interceptor Settings
interceptorSettings = {
    throwOnInvalidStates = false,
    customInterceptionPoints = "onLogin,onWikiTranslation,onAppClose"
};

throwOnInvalidStates

This tells the interceptor service to throw an exception if the state announced for interception is not valid or does not exist. Defaults to false.

customInterceptionPoints

This key is a comma delimited list or an array of custom interception points you will be registering for custom announcements in your application. This is the way to provide an observer-observable pattern to your applications.

Info Please see the Interceptors section for more information.

HTTP Method Spoofing

Although we have access to all the HTTP verbs, modern browsers still only support GET and POST. With ColdBox and HTTP Method Spoofing, you can take advantage of all the HTTP verbs in your web forms.

By convention, ColdBox will look for an _method field in the FORM scope. If one exists, the value of this field is used as the HTTP method instead of the method from the execution. For instance, the following block of code would execute with the DELETE action instead of the POST action:

<cfoutput>
<form method="POST" action="#event.buildLink( 'posts/#prc.post.getId()#' )#">
    <input type="hidden" name="_method" value="DELETE" />
    <button type="submit">Delete</button>
</form>
</cfoutput>

You can manually add these _method fields yourselves, or you can take advantage of ColdBox's HTML Helper startForm() method. Just pass the method you like, we will take care of the rest:

<cfoutput>
#html.startForm( action = "posts.#prc.post.getId()#", method="DELETE" )#
    #html.submitButton( name = "Delete", class = "btn btn-danger" )#
#html.endForm()#
</cfoutput>

Routing

ColdBox supports a Routing Service that will provide you with robust URL mappings for building expressive applications and RESTFul services. By convention URL routing will allow you to create URL's without using verbose parameter delimiters like ?event=this.that&m1=val and execute ColdBox events.

// Old Style
http://localhost/index.cfm?event=home.about&page=2
http://localhost/index.cfm?city=24&page=3&county=234324324
// New Routing Style
http://localhost/home/about/page/2
http://localhost/dade/miami/page/3

If you are leveraging CommandBox as your server, then full URL rewrites are enabled by default. This means you do not need a web server to remove the index.cfm from the URL.

What is a route?

A route is a declared URL pattern that if matched it will translate the URL into one of the following:

  • A ColdBox event to execute

  • A View/Layout to render

  • A Reponse function to execute

  • A Redirection to occur

It will also inspect the URL for placeholders and translate them into the incoming Request Collection variables (RC).

Examples

config/Router.cfc
function configure(){

    // Routing with placeholders to an event with placeholders
    route( "/blog/:year-numeric{4}/:month?/:day?" )
        .to( "blog.list" );

    // Redirects
    route( "/old/book" )
        .redirect( "/mybook" );

    // Responses
    route( "/echo" ).toResponse( (event,rc,prc) => {
        return "hello luis";
    } );

    // Shortcut to above
    route( "/echo", (event,rc,prc) => {
        return "hello luis";
    } );

    // Show view
    route( "/contact-us" )
        .toView( "main/contact" );

    // Direct to handler with action from URL
    route( "/users/:action" )
        .toHandler( "users" );

    // Inline pattern + target and name
    route( pattern="/wiki/:page", target="wiki.show", name="wikipage" );

}

Routing Benefits

There are several benefits that you will get by using our routing system:

  • Complete control of how URL's are built and managed

  • Ability to create or build URLs' dynamically

  • Technology hiding

  • Greater application portability

  • URL's are more descriptive and easier to remember

Route Visualizer

As you create route-heavy applications visualizing the routes will be challenging especially for HMVC apps with lots of modules. Just install our ColdBox Route Visualizer and you will be able to visually see, test and debug all your routing needs.

box install route-visualizer

Routing Namespaces

You can 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 testing namespace 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, 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 the addNamespace( pattern, namespace ) method or the fluent route().toNamespaceRouting() method.

addNamespace( pattern="/testing", namespace="test" );
route( "/testing" ).toNamespaceRouting( "test" );

addNamespace( pattern="/news", namespace="blog" );
route( "/news" ).toNamespaceRouting( "blog" );

Once you declare the namespace you can use the grouping functionality to declare all the namespace routes or you can use a route().withNamespace() combination.

// Via Grouping
route( "/news" ).toNamespaceRouting( "blog" )
	.group( { namespace = "blog" }, function(){
		route( "/", "blog.index" )
  		.route( "/:year-numeric?/:month-numeric?/:day-numeric?", "blog.archives" );
	} );
  

// Via Routing DSL
addNamespace( "/news", "blog" );
  
route( "/" )
  .withNameSpace( "blog" )
  .to( "blog.index" );

route( "/:year-numeric?/:month-numeric?/:day-numeric?" )
  .withNameSpace( "blog" )
  .to( "blog.archives" );

Hint You can also register multiple URL patterns that point to the same namespace

Rendering With Local Variables

You can pass localized arguments to the renderView() and renderLayout() methods in order to encapsulate the rendering via the args struct argument. Much like how you make method calls with arguments. Inside of your layouts and views you will receive the same args struct reference as well.

This gives you great DRYness (yes that is a word) when building new and edit forms or views as you can pass distinct arguments to distinguish them and keep structure intact.

Universal Form

<h1>#args.type# User</h1>
<form method="post" action="#args.action#">
...
</form>

New Form

#renderView(view='forms/universal',args={type='new',action='user.create'})#

Edit Form

#renderView(view='forms/universal',args={type='edit',action='user.update'})#
// executes after any action
function postHandler( event, rc, prc, action, eventArguments ){
}

// executes after the list() action ONLY
function postList( event, rc, prc, action, eventArguments ){
}

// concrete examples
function postHandler( event, rc, prc, action, eventArguments ){
    log.info("Finalized executing #action#");
}
// only fire for the actions: save(), delete()
this.posthandler_only = "save,delete";
// DO NOT fire for the actions: login(), doLogin(), logout()
this.posthandler_except = "login,doLogin,logout"
// executes before any action
function preHandler( event, rc, prc, action, eventArguments ){
}

// executes before the list() action ONLY
function preList( event, rc, prc, action, eventArguments ){
}

// concrete example
function preHandler( event, rc, prc, action, eventArguments ){
    if( !security.isLoggedIn() ){
        event.overrideEvent( 'security.login' );
        log.info( "Unauthorized accessed detected!", getHTTPRequestData() );
    }
}
function preList( event, rc, prc, action, eventArguments ){
    log.info("Starting executing the list action");
}
// only fire for the actions: save(), delete()
this.prehandler_only = "save,delete";
// DO NOT fire for the actions: login(), doLogin(), logout()
this.prehandler_except = "login,doLogin,logout"
RequestContext
https://github.com/ortus-docs/coldbox-docs
Ortus Solutions, Corp
http://www.ortussolutions.com/products/commandbox
GitHub repository
https://www.harvesting.org/
https://www.harvesting.org/
Shalom Children's Home

Using Settings

The ColdBox Controller (stored in ColdFusion application scope) stores all your application settings and also your system settings:

  • ColdboxSettings : Framework specific system settings

  • ConfigSettings : Your application settings

You can use the following methods to retrieve/set/validate settings in your handlers/layouts/views and interceptors:

FrameworkSuperType.cfc
/**
 * Get a setting from the system
 * @name The key of the setting
 * @fwSetting Retrieve from the config or fw settings, defaults to config
 * @defaultValue If not found in config, default return value
 */
function getSetting( required name, boolean fwSetting=false, defaultValue )

/**
 * Verify a setting from the system
 * @name The key of the setting
 * @fwSetting Retrieve from the config or fw settings, defaults to config
 */
boolean function settingExists( required name, boolean fwSetting=false )

/**
 * Set a new setting in the system
 * @name The key of the setting
 * @value The value of the setting
 *
 * @return FrameworkSuperType
 */
any function setSetting( required name, required value )

You can also get access to these methods via the ColdBox Controller component:

controller.getSetting()
controller.setSetting()
controller.settingExists()
controller.getConfigSettings()
controller.getColdBoxSettings()

Injecting Settings

You can use the WireBox injection DSL to inject settings in your models or non-coldbox objects. Below are the available DSL notations:

  • coldbox:setting:{key} : Inject a specified config setting key

  • coldbox:fwsetting:{key} : Inject a specified system setting key

  • coldbox:configSettings : Inject a reference to the application settings structure

  • coldbox:fwSettings : Inject a reference to the ColdBox System settings structure

component{

    property name="mysetting" inject="coldbox:setting:mysetting";
    property name="path" inject="coldbox:fwSetting:path";
    property name="config" inject="coldbox:configSettings";
    property name="settings" inject="coldbox:fwSettings";

}

Resourceful Routes

In ColdBox, you can register resourceful routes to provide automatic mappings between HTTP verbs and URLs to event handlers and actions by convention. By convention, all resources map to a handler with the same name or they can be customized if needed. This allows for a standardized convention when building routed applications.

You can leverage the resources() method in your router to register resourceful routes.

// Creates all resources that point to a photos event handler by convention
resources( "photos" );

// Register multiple resources either as strings or arrays
resources( "photos,users,contacts" )
resources( [ "photos" , "users", "contacts" ] );

// Register multiple fluently
resources( "photos" )
    .resources( "users" )
    .resources( "contacts" );

// Creates all resources to the event handler of choice instead of convention
resources( route="photos", handler="MyPhotoHandler" );

// All resources in a module
resources( route="photos", handler="photos", module="api" );

// Resources in a ModuleConfig.cfc
router.resources( "photos" )
  .resources( resource="users", handler="user" )

This single resource declaration will create all the necessary variations of URL patterns and HTTP Verbs to actions to handle the resource. Please see the table below with all the permutations it will create for you.

Resources Table

For in-depth usage of the resources() method, let's investigate the API Signature:

/**
* Create all RESTful routes for a resource. It will provide automagic mappings between HTTP verbs and URLs to event handlers and actions.
* By convention, the name of the resource maps to the name of the event handler.
* Example: `resource = photos` Then we will create the following routes:
* - `/photos` : `GET` -> `photos.index` Display a list of photos
* - `/photos/new` : `GET` -> `photos.new` Returns an HTML form for creating a new photo
* - `/photos` : `POST` -> `photos.create` Create a new photo
* - `/photos/:id` : `GET` -> `photos.show` Display a specific photo
* - `/photos/:id/edit` : `GET` -> `photos.edit` Return an HTML form for editing a photo
* - `/photos/:id` : `PUT/PATCH` -> `photos.update` Update a specific photo
* - `/photos/:id` : `DELETE` -> `photos.delete` Delete a specific photo
* 
* @resource         The name of a single resource or a list of resources or an array of resources
* @handler          The handler for the route. Defaults to the resource name.
* @parameterName    The name of the id/parameter for the resource. Defaults to `id`.
* @only             Limit routes created with only this list or array of actions, e.g. "index,show"
* @except           Exclude routes with an except list or array of actions, e.g. "show"
* @module           If passed, the module these resources will be attached to.
* @namespace        If passed, the namespace these resources will be attached to.
*/
function resources(
  required resource,
  handler=arguments.resource,
  parameterName="id",
  only=[],
  except=[],
  string module="",
  string namespace=""
)

Scaffolding Resources

We have created a scaffolding command in CommandBox to help you register and generate resourceful routes. Just run the following command in CommandBox to get all the help you will need in generating resources:

coldbox create resource help

How are events called?

Events are determined via a special variable that can be sent in via the FORM, URL, or REMOTELY called event. If no event is detected as an incoming variable, the framework will look in the configuration directives for the DefaultEvent and use that instead. If you did not set a DefaultEvent setting then the framework will use the following convention for you: main.index

Hint : You can even change the event variable name by updating the EventName setting in your coldbox configuration directive.

Please note that ColdBox supports both normal variable routing and URL mapping routing, usually referred to as pretty URLs.

Event Syntax

In order to call them you will use the following event syntax notation format:

event={module:}{package.}{handler}{.action}
  • no event : Default event by convention is main.index

  • event={handler} : Default action method by convention is index()

  • event={handler}.{action} : Explicit handler + action method

  • event={package}.{handler}.{action} : Packaged notation

  • event={module}:{package}.{handler}.{action} : Module Notation (See ColdBox Modules)

This looks very similar to a Java or CFC method call, example: String.getLength(), but without the parentheses. Once the event variable is set and detected by the framework, the framework will tokenize the event string to retrieve the CFC and action call to validate it against the internal registry of registered events. It then continues to instantiate the event handler CFC or retrieve it from cache, finally executing the event handler's action method.

Examples

// Call the users.cfc index() method
index.cfm?event=users.index
// Call the users.cfc index() method implicitly
index.cfm?event=users
// Call the users.cfc index() method via URL mappings
index.cfm/users/index
// Call the users.cfc index() method implicitly via URL mappings
index.cfm/users

Interception Methods

There are several simple implicit AOP (Aspect Oriented Programming) interceptor methods, usually referred to as advices, that can be declared in your event handler that the framework will use in order to execute them before/after and around an event as its fired from the current handler.

This is great for intercepting calls, pre/post processing, localized security, logging, RESTful conventions, and much more. Yes, you got that right, Aspect Oriented Programming just for you and without all the complicated setup involved! If you declared them, the framework will execute them.

Interceptor Method

Description

preHandler()

Executes before any requested action (In the same handler CFC)

pre{action}()

Executes before the {action} requested ONLY

postHandler()

Executes after any requested action (In the same handler CFC)

post{action}()

Executes after the {action} requested ONLY

aroundHandler()

Executes around any request action (In the same handler CFC)

around{action}()

Executes around the {action} requested ONLY

Author

Luis Fernando Majano Lainez

Luis Majano is a Computer Engineer with over 15 years of software development and systems architecture experience. He was born in in the late 70’s, during a period of economical instability and civil war. He lived in El Salvador until 1995 and then moved to Miami, Florida where he completed his Bachelors of Science in Computer Engineering at . Luis resides in Rancho Cucamonga, California with his beautiful wife Veronica, baby girl Alexia and baby boy Lucas!

He is the CEO of , a consulting firm specializing in web development, ColdFusion (CFML), Java development and all open source professional services under the ColdBox and ContentBox stack. He is the creator of ColdBox, ContentBox, WireBox, MockBox, LogBox and anything “BOX”, and contributes to many open source ColdFusion projects. He is also the Adobe ColdFusion user group manager for the . You can read his blog at

Luis has a passion for Jesus, tennis, golf, volleyball and anything electronic. Random Author Facts:

  • He played volleyball in the Salvadorean National Team at the tender age of 17

  • The Lord of the Rings and The Hobbit is something he reads every 5 years. (Geek!)

  • His first ever computer was a Texas Instrument TI-86 that his parents gave him in 1986. After some time digesting his very first BASIC book, he had written his own tic-tac-toe game at the age of 9. (Extra geek!)

  • He has a geek love for circuits, microcontrollers and overall embedded systems.

  • He has of late (during old age) become a fan of running and bike riding with his family.

Keep Jesus number one in your life and in your heart. I did and it changed my life from desolation, defeat and failure to an abundant life full of love, thankfulness, joy and overwhelming peace. As this world breathes failure and fear upon any life, Jesus brings power, love and a sound mind to everybody!

“Trust in the LORD with all your heart, and do not lean on your own understanding.” – Proverbs 3:5

Contributors

Jorge Emilio Reyes Bendeck

Jorge is an Industrial and Systems Engineer born in El Salvador. After finishing his Bachelor studies at the Monterrey Institute of Technology and Higher Education , Mexico, he went back to his home country where he worked as the COO of. In 2012 he left El Salvador and moved to Switzerland in pursuit of the love of his life. He married her and today he resides in Basel with his lovely wife Marta and their daughter Sofía.

Jorge started working as project manager and business developer at Ortus Solutions, Corp. in 2013, . At Ortus he fell in love with software development and now enjoys taking part on software development projects and software documentation! He is a fellow Christian who loves to play the guitar, worship and rejoice in the Lord!

Therefore, if anyone is in Christ, the new creation has come: The old has gone, the new is here! 2 Corinthians 5:17

Brad Wood

Brad grew up in southern Missouri where he systematically disassembled every toy he ever owned which occasionally led to unintentional shock therapy (TVs hold charge long after they've been unplugged, you know) After high school he majored in Computer Science with a music minor at (Olathe, KS). Today he lives in Kansas City with his wife and three girls where he still disassembles most of his belongings (including automobiles) just with a slightly higher success rate of putting them back together again.) Brad enjoys church, all sorts of international food, and the great outdoors.

Brad has been programming CFML for 12+ years and has used every version of CF since 4.5. He first fell in love with ColdFusion as a way to easily connect a database to his website for dynamic pages. Brad blogs at () and likes to work on solder-at-home digitial and analog circuits with his daughter as well as building projects with Arduino-based microcontrollers.

Brad's CommandBox Snake high score is 141.

My First Handler & View

Now let's create our first controller, which in ColdBox is called Event Handler. Let's go to CommandBox again:

This will generate the following files:

  • A new handler called hello.cfc inside of the handlers folder

  • A view called index.cfm in the views/hello folder

  • An integration test at tests/specs/integration/helloTest.cfc.

Now go to your browser and the following URL to execute the generated event:

You will now see a big hello.index outputted to the screen. You have now created your first handler and view combination.

Handler Code

Let's check out the handler code:

As you can see, a handler is a simple CFC with functions on them. Each function maps to an action that is executed via the URL. The default action in ColdBox is index()which receives three arguments:

  • event - An object that models and is used to work with the current request

  • rc - A struct that contains both URL/FORM variables (unsafe data)

  • prc - A secondary struct that is private only settable from within your application (safe data)

The event object is used for many things, in the case of this function we are calling a setView() method which tells the framework what view to render to the user once execution of the action terminates.

Tip: The view is not rendered in line 7, but rendered after the execution of the action by the framework.

Executing Events

Did you detect a convention here?

The sections in the URL are the same as the name of the event handler CFC (hello.cfc) and method that was generated index(). By convention, this is how you execute events in ColdBox by leveraging the following URL pattern that matches the name of a handler and action function.

You can also nest handlers into folders and you can also pass the name of the folder(s) as well.

If no action is defined in the URL then the default action of index will be used.

All of this URL magic happens thanks to the URL mappings capabilities in ColdBox. By convention, you can write beautiful URLs that are RESTFul and by convention. You can also extend them and create more expressive URL Mappings by leveraging the config/Router.cfc which is your application router.

Tip: Please see the guide for more in-depth information.

My First Virtual Event

Now let's create a virtual event, which is basically just a view we want to execute with no event handler controller needed. This is a great way to incorporate non-mvc files into ColdBox, baby steps!

Open the view now (/views/virtual/hello.cfm) and add the following:

Then go execute the virtual event:

You will get the Hello From ColdBox Land! displayed! This is a great way to create tests or even bring in legacy/procedural templates into an MVC framework.

Tip: You can see our section for more in-depth information.

Working With Event Handlers

Event handlers are the controller layer in ColdBox and is what you will be executing via the URLor a FORMpost. All event handlers are singletons, which means they are cached for the duration of the application, so always remember to var scope your variables in your functions.

Tip: For development we highly encourage you to turn handler caching off or you will have to reinit the application in every request, which is annoying. Open the config/ColdBox.cfc and look for the coldbox.handlerCaching setting.

Once you started the server in the previous section and opened the browser, the default event got executed which maps to an event handler CFC (controller) handlers/main.cfc and the method/action in that CFC called index(). Go open the handlers/main.cfc and let's explore the code.

Handler Code

Every action in ColdBox receives three arguments:

  • event - An object that models and is used to work with the current request

  • rc - A struct that contains both URL/FORM variables (unsafe data)

  • prc - A secondary struct that is private only settable from within your application (safe data)

This line event.setView( "main/index" ) told ColdBox to render a view back to the user found in views/main/index.cfm using a default layout, which by convention is called Main.cfm which can be found in the layouts folder.

Executing Events

We have now seen how to add handlers via CommandBox using the coldbox create handler command and also execute them by convention by leveraging the following URL pattern:

Also remember, that if no action is defined in the incoming URL then the default action of index will be used.

Remember that the URL mappings support in ColdBox is what allows you to execute events in such a way from the URL. These are controlled by your application router: config/Router.cfc

Working With Incoming Data

Now, let's open the handler we created before called handlers/hello.cfc and add some public and private variables to it so our views can render the variables.

Let's open the view now: views/hello/index.cfm and change it to this:

Please note that we used the ColdFusion function encodeForHTML() () on the public variable. Why? Because you can never trust the client and what they send, make sure you use the built-in ColdFusion encoding functions in order to avoid XSS hacks or worse on incoming public (rc) variables.

If you execute the event now: http://localhost:{port}/hello/index you will see a message of Hello nobody.

Now change the incoming URL to this: http://localhost:{port}/hello/index?name=ColdBox and you will see a message of Hello ColdBox.

Tip: Please see the section for in-depth information about them.

System Settings (Java Properties and Environment Variables)

ColdBox makes it easy to access the configuration stored in your Java system properties and your server's environment variables, even if you don't know which one it is in! Three methods are provided for your convenience:

Accessing System Settings in config/ColdBox.cfc or a ModuleConfig.cfc

If you are inside config/ColdBox.cfc or a ModuleConfig.cfc or a config/WireBox.cfc you can use the three system settings functions directly! No additional work required.

Accessing System Settings in Application.cfc

If you would like to access these methods in your Application.cfc, create an instance of coldbox.system.core.util.Util and access them off of that component. This is required when adding a datasource from environment variables.

Example:

Accessing System Settings in other files

If you need to access these configuration values in other components, consider adding the values to your and injecting the values into your other components

Pathinfo Providers

By default, the URL mapping processor will detect routes by looking at the CGI.PATH_INFO variable, but you can override this and provide your own function. This feature can be useful to set flags for each request based on a URL and then clean or parse the URL to a more generic form to allow for simple route declarations. Uses may include internationalization (i18n) and supporting multiple experiences based on devices such as Desktop, Tablet, Mobile and TV.

To modify the URI used by the Routing Services before route detection occurs simply follow the convention of adding a function called pathInfoProvider() to your application Router (config/Router.cfc).

The pathInfoProvider() function is responsible for returning the string used to match a route.

RESTFul Extension Detection

Usage

ColdBox allows you to detect incoming extensions from incoming paths automatically for you. This is great for building multi-type responses or to just create virtual extensions for events.

If an extension is detected in the incoming URL, ColdBox will grab it and store it in the request collection (RC) as the variable format. If there is no extension, then rc.format will not be stored and thus will not exist.

Configuration

You can configure the extension detection using the following configuration methods:

Please note that if you have set to throw exceptions if an invalid extension is detected then a 406 exception will be thrown.

Relocating

The framework provides you with the relocate() method that you can use to relocate to other events thanks to the framework super type object, the grand daddy of all things ColdBox.

Please see the for further investigation of all the goodness of methods you have available.

It is extremely important that you use this method when relocating instead of the native ColdFusion methods as it allows you to gracefully relocate to other events or external URIs. By graceful, we mean it does a lot more behind the scenes like making sure the flash scope is persisted, logging, post processing interceptions can occur and safe relocations.

So always remember that you relocate via relocate() and if I asked you: "Where in the world does event handlers get this method from?", you need to answer: "From the super typed inheritance".

Routing By Convention

Every router has a default route already defined for you in the application templates, which we refer to as routing by convention:

The URL pattern in the default route includes two special position placeholders, meaning that the handler and the action will come from the URL. Also note that the :action has a question mark (?), which makes the placeholder optional, meaning it can exist or not from the incoming URL.

  • :handler - The handler to execute (It can include a Package and/or Module reference)

  • :action - The action to relocate to (See the ?, this means that the action is optional)

Behind the scenes the router creates two routes due to the optional placeholder in the following order:

  1. route( "/:handler/:action" )

  2. route( "/:handler)

Tip The :handler parameter allows you to nest module names and handler names. Ex: /module/handler/action

If no action is passed the default action is index

This route can handle pretty much all your needs by convention:

Convention Name-Value Pairs

Any extra name-value pairs in the remaining URL of a discovered URL pattern will be translated to variables in the request collection (rc) for you automagically.

Tip: You can turn this feature off by using the valuePairTranslator( false ) modifier in the routing DSL on a route by route basis

route( "/pattern" ).to( "users.show" ).valuePairTranslator( false );

What's New With 5.3.0

ColdBox 5.3.0 is a minor version update with lots of fixes, improvements, performance enhancements and some nice new features. Below are the major areas of improvement and the full release notes. To update your ColdBox installation just leverage CommandBox: update coldbox

ColdBox

Bugs

  • [] - Defining UDF as COLDBOX_FAIL_FAST no longer returns false

  • [] - Fixed typo(s) in call of function prePend() on routing

  • [] - base url not eliminating double slashes

  • [] - Setter for valid extensions is setting the wrong variable

  • [] - Cleanup the with closure on group routing to avoid leakage

New Features

  • [] - Add new flag to router: multiDomainDiscovery that can be turned on/off

Improvements

  • [] - Add trimming to asset loader to avoid spaces on links

  • [] - InterceptorState + EventPool uses syncrhonizedMap that has locking issues: refactor to avoid these issues

  • [] - set initiated bit for ColdBox after modules are activated

  • [] - Performance improvements for interception registrations and discovery

LogBox

Improvements

  • [] - Add test and fix for adding a LogBox category after the fact

WireBox

Improvements

  • [] - Account for leading slashes in mapDirectory()

  • [] - Throw a nicer DSL error if ColdBox is not linked

  • [] - Performance improvements for the construction of transients

Getting & Setting Values

We all need values in our applications. That is why we interact with the in order to place data from our model layer into it so our views can display it, or to retrieve data from a user's request. You will either interact with the event object to get/set values or put/read values directly via the received rc and prc references.

We would recommend you use the private request collection (prc) for setting manual data and using the standard request collection (rc) for reading the user's unsafe request variables. This way a clear distinction can be made on what was sent from the user and what was set by your code.

Important The most important paradigm shift from procedural to an MVC framework is that you NO LONGER will be talking to URL, FORM, REQUEST or any ColdFusion scope from within your handlers, layouts, and views. The request collection already has URL, FORM, and REQUEST scope capabilities, so leverage it.

Implicit Methods

Every event handler controller has some implicit methods that if you create them, they come alive. Just like the implicit methods in Application.cfc

onMissingAction()

With this convention you can create virtual events that do not even need to be created or exist in a handler. Every time an event requests an action from an event handler and that action does not exist in the handler, the framework will check if an onMissingAction() method has been declared. If it has, it will execute it. This is very similar to ColdFusion's onMissingMethod() but on an event-driven framework.

This event has an extra argument: missingAction which is the missing action that was requested. You can then do any kind of logic against this missing action and decide to do internal processing, error handling or anything you like. The power of this convention method is extraordinary, you have tons of possibilities as you can create virtual events on specific event handlers.

onError()

This is a localized error handler for your event handler. If any type of runtime error occurs in an event handler and this method exists, then the framework will call your method so you can process the error first. If the method does not exist, then normal error procedures ensue.

Please note that compile time errors will not fire this method, only runtime exceptions.

onInvalidHTTPMethod()

This method will be called for you if a request is trying to execute an action in your handler without the proper approved HTTP Verb. It will then be your job to determine what to do next:

Executing Routes

A part from using runEvent() to execute events, you can also abstract it by using the runRoute() method. This method is fairly similar but with the added benefit of executing a NAMED route instead of the direct event it represents. This gives you the added flexibility of abstracting the direct event and leveraging the named route.

All the same feature of runEvent() apply to runRoute()

RunRoute()

Just like you can create links based on named routes and params, you can execute named routes and params as well internally via runRoute()

Parameters

The params argument you pass to the runRoute() method will be translated into event arguments. Therefore they will be passed as arguments to the event the route represents:

In the example above, the userData named route points to the user.data event.

Module Routes

If you want to execute module routes, no problem! Just use our @ or : notation to tell the controller from which module's router we should pick the route from.

Rendering Collections

You have a few arguments in the renderView() method that deal with collection rendering. Meaning you can pass any array or query and the Renderer will iterate over that collection and render out the view as many times as the records in the colleciton.

  • collection : A data collection that can be a query or an array of objects, structs or whatever

  • collectionAs : The name of the variable in the variables scope that will hold the collection pivot.

  • collectionStartRow : Defaults to 1 or your offset row for the collection rendering

  • collectionMaxRows : Defaults to show all rows or you can cap the rendering display

  • collectionDelim : An optional delimiter to use to separate the collection renderings. By default it is empty.

Once you call renderView() with a collection, the renderer will render the view once for each member in the collection. The views have access to the collection via arguments.collection or the member currently iterating. The name of the member being iterated as is by convention the same name as the view. So if we do this in any layout or simple view:

Then the tags/comment will be rendered as many times as the collection rc.comments has members on it and by convention the name of the variable is comment the same as the view name.

If you don't like that, then use the collectionAs argument:

So let's see the collection view now:

You can see that I just call methods on the member as if I was looping (which we are for you). But you will also see two little variables here:

  • _counter : A variable created for you that tells you in which record we are currently looping on

  • _items : A variable created for you that tells you how many records exist in the collection

This will then render that specific dynamic HTML view as many times as their are records in the rc.comments array and concatenate them all for you. In my case, I separate each iteration with a simple but you can get fancy and creative.

View Events

All rendered views have associated events that are announced whenever the view is rendered via ColdBox Interceptors. These are great ways for you to be able to intercept when views are rendered and transform them, append to them, or even remove content from them in a completely decoupled approach. The way to listen for events in ColdBox is to write Interceptors, which are essential simple CFC's that by convention have a method that is the same name as the event they would like to listen to. Each event has the capability to receive a structure of information wich you can alter, append or remove from. Once you write these Interceptors you can either register them in your Configuration File or programmatically.

Caution You can disable the view events on a per-rendering basis by passing the prePostExempt argument as true when calling renderView() methods.

Sample Interceptor

Here is a sample interceptor that trims any content before it is renderer:

Of course, I am pretty sure you will be more creative than that!

Rendering Views

We have now seen how to set views to be rendered from our handlers. However, we can use some cool methods to render views and layouts on-demand. These methods exist in the Renderer and several facade methods exist in the super type so you can call it from any handler, interceptor, view or layout.

  1. renderView()

  2. renderExternalView()

  3. renderLayout()

Check out the latest for the latest arguments:

Inline renderings are a great asset for reusing views and doing layout compositions

Model Rendering

If you need rendering capabilities in your model layer, we suggest using the following injection DSL:

This will inject a of a Renderer into your model objects. Remember that renderers are transient objects so you cannot treat them as singletons. The provider is a proxy to the transient object, but you can use it just like the normal object:

// Example PathInfoProvider for detecting a mobile request
function PathInfoProvider( event ){
  var rc = event.getCollection();
  var prc = event.getCollection(private=true);

  local.URI = CGI.PATH_INFO;

  if (reFindNoCase('^/m',local.URI) == 0)
  {
    // Does not look like this could be a mobile request...
    return local.URI;
  }

  // Mobile Request? Let's find out.

  // If the URI is "/m" it is easy to determine that this is a
  // request for the Mobile Homepage.
  if (len(local.URI) == 2)
  {
    prc.mobile = true;
    // Simply return "/" since they want the mobile homepage
    return "/";
  }

  // Only continue with our mobile evaluation if we have a slash after
  // our "/m". Without a slash following the /m the route is something
  // else like coldbox.org/makes/cool/stuff
  if (REFindNoCase('^/m/',local.URI) == 1)
  {
    // Looks like we are mobile!
    prc.mobile = true;

    // Remove our "/m/" determination and continue
    // processing for languages...
    local.URI = REReplaceNoCase(local.URI,'^/m/','/');
  }

  // The URI starts with an "m" but does not look like
  // a mobile request. So, simply return the URI for normal
  // route detection...
  return local.URI;
}
http://localhost/users
http://localhost/users.json
http://localhost/users.xml
http://localhost/users.pdf
http://localhost/users.html    
http://localhost/users => rc.format is null, does not exist
http://localhost/users.json => rc.format = json
http://localhost/users.xml => rc.format = xml
http://localhost/users.pdf => rc.format = pdf
http://localhost/users.html => rc.format = html

Method

Description

setExtensionDetection( boolean )

By default ColdBox detects URL extensions like json, xml, html, pdf which can allow you to build awesome RESTful web services. Default is true.

setValidExtensions( list )

Tell the interceptor what valid extensions your application can listen to. By default it listens to: json, jsont, xml, cfm, cfml, html, htm, rss, pdf

setThrowOnInvalidExtensions( boolean )

By default ColdBox does not throw an exception when an invalid extension is detected. If true, then the interceptor will throw a 406 Invalid Requested Format Extension: {extension} exception. Default is false.

route( ":handler/:action?").end();
// Basic Routing
http://localhost/general -> event=general.index
http://localhost/general/index -> event=general.index

// If 'admin' is a package/folder in the handlers directory
http://localhost/admin/general/index -> event=admin.general.index 

// If 'admin' is a module
http://localhost/admin/general/index -> event=admin:general.index
http://localhost/general/show/page/2/name/luis
# translate to
event=general.show, rc.page=2, rc.name=luis

http://localhost/users/show/export/pdf/name
# translate to
event=users.show, rc.export=pdf, rc.name={empty value}
function onMissingAction( event, rc, prc, missingAction, eventArguments ){

}
// On Error
function onError( event, rc, prc, faultAction, exception, eventArguments ){
    // prepare a data packet
    var data = {
        error = true,
        messages = exception.message & exception.detail,
        data = ""
    }

    // log via the log variable already prepared by ColdBox
    log.error("Exception when executing #arguments.faultAction# #data.messages#", exception);    

    // render out a json packet according to specs status codes and messages
    event.renderData(data=data,type="json",statusCode=500,statusMessage="Error ocurred");

}
function onInvalidHTTPMethod( faultAction, event, rc, prc ){
    return "Go away!";
}
/**
 * Executes internal named routes with or without parameters. If the named route is not found or the route has no event to execute then this method will throw an `InvalidArgumentException`.
 * If you need a route from a module then append the module address: `@moduleName` or prefix it like in run event calls `moduleName:routeName` in order to find the right route.
 * The route params will be passed to events as action arguments much how eventArguments work.
 *
 * @name The name of the route
 * @params The parameters of the route to replace
 * @cache Cached the output of the runnable execution, defaults to false. A unique key will be created according to event string + arguments.
 * @cacheTimeout The time in minutes to cache the results
 * @cacheLastAccessTimeout The time in minutes the results will be removed from cache if idle or requested
 * @cacheSuffix The suffix to add into the cache entry for this event rendering
 * @cacheProvider The provider to cache this event rendering in, defaults to 'template'
 * @prePostExempt If true, pre/post handlers will not be fired. Defaults to false
 *
 * @throws InvalidArgumentException
 */
any function runRoute(
	required name,
	struct params={},
	boolean cache=false,
	cacheTimeout="",
	cacheLastAccessTimeout="",
	cacheSuffix="",
	cacheProvider="template",
	boolean prePostExempt=false
)
// Execute
runRoute( "userData", { id=4 } )
user.cfc
component{

    property name="userService" inject;

    function data( event, rc, prc, id=0 ){
        if( id == 0 )
            return {};
            
        return userService.getData( id );
    }
}
# Using @ destination
runRoute( "userData@user", { id=4 } )

# Using : prefix
runRoute( "user:userData", { id=4 } )
#renderView(view='tags/comment',collection=rc.comments)#
<h1>Title: #comment.Title# (#_counter# of #_items#</h1>
<p>Author: #comment.Author#</p>
#comment.Comment#
<hr/>
#renderView(view='tags/comment',collection=rc.comments,collectionAs='MyComment')#
<h1>Title: #MyComment.Title# (#_counter# of #_items#</h1>
<p>Author: #MyComment.Author#</p>
#MyComment.Comment#
<hr/>
#renderView(view="home/news", collection=prc.news, collectionStartRow=11, collectionMaxRows=20)#

Event

Data

Description

preViewRender

view - The name of the view to render

Executed before a view is about to be rendered

postViewRender

All of the data above plus:

Executed after a view was rendered

component{

    function postViewRender(event,interceptData){
        interceptData.renderedView = trim( interceptData.renderedView );
    }
}
coldbox create handler name="hello" actions="index"
# With rewrites enabled
http://localhost:{port}/hello/index

# With no rewrites enabled
http://localhost:{port}/index.cfm/hello/index
component{

    /**
     * Default Action
     */
     function index( event, rc, prc ){
        event.setView( "hello/index" );
     }


}
http://localhost:{port}/folder/handler/action
http://localhost:{port}/handler/action
http://localhost:{port}/handler
coldbox create view name="virtual/hello"
<h1>Hello from ColdBox Land!</h1>
http://localhost:{port}/virtual/hello
event handlers
layouts and views

Name

Arguments

Description

getSystemSetting

( key, defaultValue )

Looks for key in properties first, env second. Returns the defaultValue if neither exist.

getSystemProperty

( key, defaultValue )

Returns the Java System property for key. Returns the defaultValue if it does not exist.

getEnv

( key, defaultValue )

Returns the server environment variable for key. Returns the defaultValue if it does not exist.

Application.cfc

component {

    variables.util = new coldbox.system.core.util.Util();

    this.datasources[ "my_datasource" ] = {
        driver = util.getSystemSetting( "DB_DRIVER" ),
        host = util.getSystemSetting( "DB_HOST" ),
        port = util.getSystemSetting( "DB_PORT" ),
        database = util.getSystemSetting( "DB_DATABASE" ),
        username = util.getSystemSetting( "DB_USERNAME" ),
        password = util.getSystemSetting( "DB_PASSWORD" )
    };

}
ColdBox settings
via dependecy injection.
/**
* Relocate user browser requests to other events, URLs, or URIs.
*
* @event The name of the event to run, if not passed, then it will use the default event found in your configuration file
* @URL The full URL you would like to relocate to instead of an event: ex: URL='http://www.google.com'
* @URI The relative URI you would like to relocate to instead of an event: ex: URI='/mypath/awesome/here'
* @queryString The query string to append, if needed. If in SES mode it will be translated to convention name value pairs
* @persist What request collection keys to persist in flash ram
* @persistStruct A structure key-value pairs to persist in flash ram
* @addToken Wether to add the tokens or not. Default is false
* @ssl Whether to relocate in SSL or not
* @baseURL Use this baseURL instead of the index.cfm that is used by default. You can use this for ssl or any full base url you would like to use. Ex: https://mysite.com/index.cfm
* @postProcessExempt Do not fire the postProcess interceptors
* @statusCode The status code to use in the relocation
*/
void function relocate(
	event,
	URL,
	URI,
	queryString,
	persist,
	struct persistStruct,
	boolean addToken,
	boolean ssl,
	baseURL,
	boolean postProcessExempt,
	numeric statusCode
)
relocate( "home" );
relocate( event="shop", ssl=true );
relocate( event="user.view", queryString="id=#rc.id#" );
relocate( url="http://www.google.com" );
relocate( uri="/docs/index.html" )
Super Type CFC Docs
COLDBOX-741
COLDBOX-750
COLDBOX-752
COLDBOX-756
COLDBOX-759
COLDBOX-753
COLDBOX-742
COLDBOX-744
COLDBOX-754
COLDBOX-760
LOGBOX-32
WIREBOX-79
WIREBOX-80
WIREBOX-81
<cfoutput>
// render inline
#renderView( 'tags/metadata' )#

// render from a module
#renderView( view="security/user", module="security" )#

// render and cache
#renderView(
    view="tags/longRendering", 
    cache=true, 
    cacheTimeout=5, 
    cacheProvider="couchbase"
)#

// render a view from the handler action
function showData(event,rc,prc){
    // data here
    return renderView( "general/showData" );    
}

// render an email body content in an email template layout
body = renderlayout( layout='email',view='templates/email_generic' );
</cfoutput>
property name="renderer" inject="provider:coldbox:renderer";
function sendEmail(){
    
    // code here.
    var body = renderer.renderView( "templates/email" );

}
API Docs
provider

What's New With 5.5.0

ColdBox 5.5.0 is a minor version update with lots of fixes, improvements, performance enhancements and some nice new features. Below are the major areas of improvement and the full release notes. To update your ColdBox installation just leverage CommandBox:

  • update coldbox

  • update logbox

  • update wirebox

  • update cachebox

The major libraries updated in this release are ColdBox MVC and WireBox.

Compatibility Notes

If you are using annotations for component aliases you will have to tell WireBox explicitly to process those mappings. As by default, we no longer process mappings on startup.

// Process a single mapping immediately
map( name ).process();

// Process all mappings in the mapDiretory() call
mapDirectory( packagePath="my.path", process=true )
mapDirectory( "my.path" ).process();

// Map all models in a module via the this.autoProcessModels in the ModuleConfig.cfc
this.autoProcessModels = true

Major Updates

Performance

This release is a big performance boost for two areas of operation: modules, and models. The Module Service has been completely revised to make it as fast as possible when registering and activating modules. If you have an extensive usage of modules, you will feel the difference when booting up or re-initing the framework.

The other area is the inspection of models that has been lazy evaluated. This allows for faster bootups and reinits as models are only inspected on demand or when explicitly marked.

More Environment Detection

Our environment functions have now been added to the Framework SuperType and thus all objects in the ColdBox eco-system receive it:

  • getEnv(), getSystemSetting() and getSystemProperty()

Custom Resource Patterns

As resources have become more mainstream in ColdBox, we now give you the ability to choose the URL pattern you want to attach to the resource. This allows you to create a-la-carte resource patterns and also allow you to nest resources via patterns.

resources(
  pattern="/site/:siteId/agents",
  resource="agents"
);

ColdBox Release Notes

Bugs

  • [COLDBOX-786] - HTMLHelper typo on getSetting call via elixirpath()

  • [COLDBOX-788] - Private method in handler is accessible from public ( direct link )

New Features

  • [COLDBOX-783] - New module directive: autoProcessModels which defaults to false to defer to lazy processing of models

  • [COLDBOX-789] - Added getEnv(), getSystemSetting() and getSystemProperty() to super type for easier environment setting retrievals

  • [COLDBOX-790] - Added much more logging and info when booting up the Module Service

  • [COLDBOX-791] - buildLink(), relocate() now will translate raw : to / in URL with appropriate module entry points thanks to richard herbert

  • [COLDBOX-792] - Allow nested resources and the ability for custom URL patterns for resources

Improvements

  • [COLDBOX-782] - Add TestBox 3 and code coverage to the core

  • [COLDBOX-785] - Module service performance optimizations for module activations.

  • [COLDBOX-787] - Update RequestContext.cfc getFullUrl() to include port number

WireBox Release Notes

New Features

  • [WIREBOX-84] - Remove auto processing of all mappings defer to lazy loading

  • [WIREBOX-85] - MapDirectory new boolean argument process which defers to false, if true, the mappings will be auto processed

  • [WIREBOX-86] - New binder method: process() if chained from a mapping, it will process the mapping's metadata automatically.

Improvement

  • [WIREBOX-87] - AOP debug logging as serialized CFCs which clogs log files

What's New With 5.2.0

ColdBox 5.2.0 is a minor version update with lots of fixes, improvements and some new features not only to the ColdBox core but to LogBox and WireBox alike. Below are the major areas of improvement and the full release notes. To update your ColdBox installation just leverage CommandBox: update coldbox

ColdBox

The major areas of improvement for the ColdBox core where on tons of fixes, however there are some nice new features discussed below.

Bugs

  • [COLDBOX-597] - Function addAsset generate wrong link if asset path contains ".js"

  • [COLDBOX-668] - Make implicit views case sensitive by default for linux systems

  • [COLDBOX-677] - HTML HELPER - Fix a requirement for columnName if column is defined

  • [COLDBOX-696] - Passing headers to `request` breaks RoutingService when in test mode

  • [COLDBOX-724] - Add ENV overload in detectEnvironment() via ENVIRONMENT system setting/property

  • [COLDBOX-726] - setNextEvent does not exist in coldbox.system.web.Controller

  • [COLDBOX-727] - fail fast strong typed to boolean, update to allow closures

  • [COLDBOX-729] - getRenderData() in base test case was not looking at the right request collection

  • [COLDBOX-732] - Fail fast can't be turned off for original behavior

  • [COLDBOX-736] - Module mappings disappear when not unloading ColdBox in base test case

  • [COLDBOX-738] - Clear not working on string builders: Use setLength(0) since clear is not a method on StringBuilder

  • [COLDBOX-740] - When using group() operations with a handler and no explicit handler routing call added, route never registered the handler

New Features

  • [COLDBOX-722] - New global directive: autoMapModels which if true, it will map all root models just like modules do.

This will allow root models to behave like modules where all models are registered automatically for you but with no namespace.

  • [COLDBOX-737] - toAction() terminator is missing from the new router DSL

This terminator was missing from the new Routing DSL. This will allow you to build up routes that terminate at an action.

  • [COLDBOX-720] - Register config/Router.cfc as an interceptor

The main application router and ALL module routers are now also interceptors.

Improvements

  • [COLDBOX-730] - Implicitly pass args from renderLayout() into the rendered views

  • [COLDBOX-739] - List modules which have already been processed when a module cannot be activated to help with debugging

LogBox

Bugs

  • [LOGBOX-29] - when using async option on FileAppender, nothing logs, well now it does!

New Features

  • [LOGBOX-31] - Add defaultValue arguments to getProperty() methods on abstract appenders

Improvements

  • [LOGBOX-30] - Leave off text "ExtraInfo: " from console appender if empty string

WireBox

Bugs

  • [WIREBOX-76] - Virtual inheritance not injecting generic getters and setters correctly on target objects

  • [WIREBOX-77] - Virtual inheritance not inheriting init from super class

Improvements

  • [WIREBOX-51] - Add method to binder to override alias of current mapping, by passing the current mapping to the influencer closure

  • [WIREBOX-75] - Don't exclude path in parent mapping destinations

  • [WIREBOX-78] - Simplify error message for missing dependency to be human readable

Linking Events Together

ColdBox provides you with a nice method for generating links between events by leveraging an object called event that is accessible in all of your layouts/views and event handlers. This event object is called behind the scenes the request context object, which models the incoming request and even contains all of your incoming FORM and URL variables in a structure called rc.

Tip: You will use the event object to set views, set layouts, set HTTP headers, read HTTP headers, convert data to other types (json,xml,pdf), and much more.

Building Links

The method in the request context that builds links is called: buildLink(). Here are some of the arguments you can use:

/**
* Builds links to events or URL Routes
*
* @to The event or route path you want to create the link to
* @translate Translate between . to / depending on the SES mode on to and queryString arguments. Defaults to true.
* @ssl Turn SSl on/off on URL creation, by default is SSL is enabled, we will use it.
* @baseURL If not using SES, you can use this argument to create your own base url apart from the default of index.cfm. Example: https://mysample.com/index.cfm
* @queryString The query string to append
*/
string function buildLink(
    to,
    boolean translate=true,
    boolean ssl,
    baseURL="",
    queryString="");

Edit Your View

Edit the views/virtual/hello.cfm page and wrap the content in a cfoutput and create a link to the main ColdBox event, which by convention is main.index.

<cfoutput>
<h1>Hello from ColdBox Land!</h1>
<p><a href="#event.buildLink( "main.index" )#">Go home</a></p>
</cfoutput>

This code will generate a link to the main.index event in a search engine safe manner and in SSL detection mode. Go execute the event: http://localhost:{port}/virtual/hello and click on the generated URL, you will now be navigating to the default event /main/index. This technique will also apply to FORM submissions:

<form action="#event.buildLink( 'user.save' )#" method="post">
...
</form>

Tip You can visit our API Docs for further information about the event object and the buildLink method: http://apidocs.ortussolutions.com/coldbox/current/index.html?coldbox/system/web/context/RequestContext.html.

For extra credit try to use more of the buildLink arguments.

URL Structure & Mappings

ColdBox allows you to manipulate the incoming URL so you can create robust URL strategies especially for RESTFul services. This is all done by convention and you can configure it via the application router: config/Router.cfc for more granular control.

Out of the box we provide you with convention based routing that maps the URL to modules/folders/handlers and actions.

route( "/:handler/:action" ).end()

We have now seen how to execute events via nice URLs. Behind the scenes, ColdBox translates the URL into an executable event string just like if you were using a normal URL string:

  • /main/index -> ?event=main.index

  • /virtual/hello -> ?event=virtual.hello

  • /admin/users/list -> ?event=admin.users.list

  • /handler/action/name/value -> ?event=handler.action&name=value

  • /handler/action/name -> ?event=handler.action&name=

By convention, any name-value pairs detected after an event variable will be treated as an incoming URL variables. If there is no pair, then the value will be an empty string.

Tip: By default the ColdBox application templates are using full URL rewrites. If your web server does not support them, then open the config/Router.cfc and change the full rewrites method to false: setFullRewrites( false ).

Routing DSL

The ColdBox Routing DSL will be used to register routes for your application, which exists in your application or module router object. Routing takes place using several methods inside the router, which are divided into the following 3 categories:

  1. Initiators - Starts a URL pattern registration, but does not fully register the route until a terminator is called (target).

  2. Modifiers - Modifies the pattern with extra metdata to listen to from the incoming request.

  3. Terminators - Finalizes the registration process usually by telling the router what happens when the route pattern is detected. This is refered to as the target.

Please note that order of declaration of the routes is imperative. Order matters.

Please remember to check out the latest API Docs for the latest methods and argument signatures.

Initiators

The following methods are used to initiate a route registration process.

Please note that a route will not register unless a terminator is called or the inline target terminator is passed.

  • route( pattern, [target], [name] ) - Register a new route with optional target terminators and a name

  • get( pattern, [target], [name] ) - Register a new route with optional target terminators, a name and a GET http verb restriction

  • post( pattern, [target], [name] ) - Register a new route with optional target terminators, a name and a POST http verb restriction

  • put( pattern, [target], [name] ) - Register a new route with optional target terminators, a name and a PUT http verb restriction

  • patch( pattern, [target], [name] ) - Register a new route with optional target terminators, a name and a PATCH http verb restriction

  • options( pattern, [target], [name] ) - Register a new route with optional target terminators, a name and a OPTIONS http verb restriction

  • group( struct options, body ) - Group routes together with options that will be applied to all routes declared in the body closure/lambda.

Modifiers

Modifiers will tell the routing service about certain restrictions, conditions or locations for the routing process. It will not register the route just yet.

  • header( name, value, overwrite=true ) - attach a response header if the route matches

  • headers( map, overwrite=true ) - attach multiple response headers if the route matches

  • as( name ) - Register the route as a named route

  • rc( name, value, overwrite=true ) - Add an RC value if the route matched

  • rcAppend map, overwrite=true ) - Add multiple values to the RC collection if the route matched

  • prc( name, value, overwrite=true ) - Add an PRC value if the route matched

  • prcAppend map, overwrite=true ) - Add multiple values to the PRC collection if the route matched

  • withHandler( handler ) - Map the route to execute a handler

  • withAction( action ) - Map the route to execute a single action or a struct that represents verbs and actions

  • withModule( module ) - Map the route to a module

  • withNamespace( namespace ) - Map the route to a namespace

  • withSSL() - Force SSL

  • withCondition( condition ) - Apply a runtime closure/lambda enclosure

  • withDomain( domain ) - Map the route to a domain or subdomain

  • withVerbs( verbs ) - Restrict the route to listen to only these HTTP Verbs

  • packageResolver( toggle ) - Turn on/off convention for packages

  • valuePairTranslator( toggle ) - Turn on/off automatic name value pair translations

Terminators

Terminators finalize the routing process by registering the route in the Router.

  • end() - Register the route as it exists

  • toAction( action ) - Send the route to a specific action or RESTFul action struct

  • toView( view, layout, noLayout=false, viewModule, layoutModule ) - Send the route to a view/layout

  • toRedirect( target, statusCode=301 ) - Relocate the route to another event

  • to( event ) - Execute the event if the route matches

  • toHandler( handler ) - Execute the handler if the route matches

  • toResponse( body, statusCode=200, statusText="ok" ) - Inline response action

  • toModuleRouting( module ) - Send to the module router for evaluation

  • toNamespaceRouting( namespace ) - Send to the namespace router for evaluation

Pattern Placeholders

Alphanumeric Placeholders

In your URL pattern you can also use the : syntax to denote a variable placeholder. These position holders are alphanumeric by default:

route( "blog/:year/:month?/:day?", "blog.index" );

Once a URL is matched to the route above, the placeholders (:year/:month?/:day?) will become request collection (RC) variables:

http://localhost/blog/2012/12/22 -> rc.year=2012, rc.month=12, rc.day=22
http://localhost/blog/2012/12-> rc.year=2012, rc.month=12
http://localhost/blog/2012-> rc.year=2012

Optional Placeholders

Sometimes we will want to declare routes that are very similar in nature and since order matters, they need to be delcared in the right order. Like this one:

route( "/blog/:year-numeric/:month-numeric/:day-numeric" );
route( "/blog/:year-numeric/:month-numeric" );
route( "/blog/:year-numeric/" );
route( "/blog/" );

However, we just wrote 4 routes for this approach when we can just use optional variables by using the ? symbol at the end of the placeholder. This tells the processor to create the routes for you in the most detailed manner first instead of you doing it manually.

route( "/blog/:year-numeric?/:month-numeric?/:day-numeric?" );

Caution Just remember that an optional placeholder cannot be followed by a non-optional one. It doesn't make sense.

Numeric Placeholders

ColdBox gives you also the ability to declare numerical only routes by appending -numeric to the variable placeholder so the route will only match if the placeholder is numeric. Let's modify the route from above.

route( "blog/:year-numeric/:month-numeric?/:day-numeric?", "blog.index" );

This route will only accept years, months and days as numbers.

Alpha Placeholders

ColdBox gives you also the ability to declare alpha only routes by appending -alpha to the variable placeholder so the route will only match if the placeholder is alpha only.

route( "wiki/:page-alpha", "wiki.show" );

This route will only accept page names that are alpha only.

There are two ways to place a regex constraint on a placeholder, using the -regex: placeholder or adding a constraints structure to the route declaration.

Regular Expression Placeholders

You can also have the ability to declare a placeholder that must match a regular expression by using the -regex( {regex_here} ) placeholder.

// route with regex placeholders
route(
    pattern="/api/:format-regex:(xml|json)/",
    target="api.execute"
);

The rc variable format must match the regex supplied: (xml|json)

Regular Expression Constraints

You can also apply a structure of regular expressions to a route instead of inlining the regular expressions in the placeholder location. You will do this using the constraints() method of the router.

The key in the structure must match the name of the placeholder and the value is a regex expression that must be enclosed by parenthesis ().

// route with custom constraints
route(
    pattern = "/api/:format/:entryID",
    target  = "api.execute"
).constraints( {
    format  = "(xml|json)",
    entryID = "([0-9]{4})" 
} );

Named Routes

You can register routes in ColdBox with a human friendly name so you can reference them later for link generation and more.

Registering Named Routes

You will do this in two forms:

  1. Using the route() method and the name argument

  2. Using the as() method

// Using the name argument
route( 
    pattern = "/users/list", 
    target = "users.index", 
    name = "usermanager" 
);

route( 
    pattern = "/user/:id/profile", 
    target = "users.show", 
    name = "userprofile"
);

// Using the as() method
route( "/users/:id/profile" )
  .as( "usersprofile" )
  .to( "users.show" )

Generating URLs to Named Routes

You will generate URLs to named routes by leveraging the route() method in the request context object (event).

route(
    // The name of the route
    required name,
    // The params to pass, can be a struct or array
    struct params={},
    // Force or un-force SSL, by default we keep the same protocol of the request
    boolean ssl
);

Let's say you register the following named routes:

route( 
    pattern = "/users/list", 
    target = "users.index", 
    name = "usermanager" 
);

route( 
    pattern = "/user/:id/profile", 
    target = "users.show", 
    name = "userprofile"
);

Then we can create routing URLs to them easily with the event.route() method:

<!-- Named Route with no params -->
<a href="#event.route( 'usermanager' )#">Manage Users</a>

<!-- Named Route with struct params -->
<a href="#event.route( 'userprofile', { id = 3 } )#">View User</a>

<!-- Named Route with array params -->
<a href="#event.route( 'userprofile', [ 3 ] )#">View User</a>

Inspecting The Current Route

The request context object (event) also has some handy methods to tell you the name or even the current route that was selected for execution:

  • getCurrentRouteName() - Gives you the name of the current route, if any

  • getCurrentRoute() - Gives you the currently executed route

  • getCurrentRoutedURL() - Gives you the complete routed URL pattern that matched the route

  • getCurrentRoutedNamespace() - Gives you the current routed namespace, if any

HTTP Method Security

More often you will find that certain web operations need to be restricted in terms of what HTTP verb is used to access a resource. For example, you do not want form submissions to be done via GET but via POST or PUT operations. HTTP Verb recognition is also essential when building strong RESTFul APIs when security is needed as well.

Manual Solution

You can do this manually, but why do the extra coding :)

function delete(event,rc,prc){
    // determine incoming http method
    if( event.getHTTPMethod() == "GET" ){
        flash.put("notice","invalid action");
        setNextEvent("users.list");
    }
    else{
        // do delete here.
    }
}

This solution is great and works, but it is not THAT great. We can do better.

Allowed Methods Property

Another feature property on an event handler is called this.allowedMethods. It is a declarative structure that you can use to determine what the allowed HTTP methods are for any action on the event handler.

this.allowedMethods = {
    actionName : "List of approved HTTP Verbs"
};

If the request action HTTP method is not found in the approved list, it will look for a onInvalidHTTPMethod() on the handler and call it if found. Otherwise ColdBox throws a 405 exception that is uniform across requests.

You can listen for global invalid HTTP methods using the coldbox.onInvalidHTTPMethodHandler located in your config/ColdBox.cfc.

component{

    this.allowedMethods = {
        delete : "POST,DELETE",
        list   : "GET"
    };

    function list(event,rc,prc){
        // list only
    }

    function delete(event,rc,prc){
        // do delete here.
    }
}

If the action is not listed in the structure, then it means that we allow all HTTP methods. Just remember to either use the onError() or onInvalidHTTPMethod() method conventions or an exception handler to deal with the security exceptions.

Allowed Methods Annotation

You can tag your event actions with a allowedMethods annotation and add a list of the allowed HTTP verbs as well. This gives you a nice directed ability right at the function level instead of a property. It is also useful when leveraging DocBox documentation as it will show up in the API Docs that are generated.

function index( event, rc, prc) allowedMethods="GET,POST"{
    // my code here
}

Model Data Binding

The framework also offers you the capability to bind incoming FORM/URL/REMOTE/XML/JSON/Structure data into your model objects by convention. This is done via WireBox's object population capabilities. The easiest approach is to use our populateModel() function which will populate the object from many incoming sources:

  • request collection RC

  • Structure

  • json

  • xml

  • query

This will try to match incoming variable names to setters or properties in your domain objects and then populate them for you. It can even do ORM entities with ALL of their respective relationships. Here is a snapshot of the method:

/**
* Populate a model object from the request Collection or a passed in memento structure
* @model The name of the model to get and populate or the acutal model object. If you already have an instance of a model, then use the populateBean() method
* @scope Use scope injection instead of setters population. Ex: scope=variables.instance.
* @trustedSetter If set to true, the setter method will be called even if it does not exist in the object
* @include A list of keys to include in the population
* @exclude A list of keys to exclude in the population
* @ignoreEmpty Ignore empty values on populations, great for ORM population
* @nullEmptyInclude A list of keys to NULL when empty
* @nullEmptyExclude A list of keys to NOT NULL when empty
* @composeRelationships Automatically attempt to compose relationships from memento
* @memento A structure to populate the model, if not passed it defaults to the request collection
* @jsonstring If you pass a json string, we will populate your model with it
* @xml If you pass an xml string, we will populate your model with it
* @qry If you pass a query, we will populate your model with it
* @rowNumber The row of the qry parameter to populate your model with
*/
function populateModel(
	required model,
	scope="",
	boolean trustedSetter=false,
	include="",
	exclude="",
	boolean ignoreEmpty=false,
	nullEmptyInclude="",
	nullEmptyExclude="",
	boolean composeRelationships=false,
	struct memento=getRequestCollection(),
	string jsonstring,
	string xml,
	query qry
){

Let's do a quick example:

Person.cfc

component accessors="true"{

    property name="name";
    property name="email";

    function init(){
        setName('');
        setEmail('');
    }
}

editor.cfm

<cfoutput>
<h1>Funky Person Form</h1>
#html.startForm(action='person.save')#

    #html.textfield(label="Your Name:",name="name",wrapper="div")#
    #html.textfield(label="Your Email:",name="email",wrapper="div")#

    #html.submitButton(value="Save")#

#html.endForm()#
</cfoutput>

Event Handler -> person.cfc

component{

    function editor(event,rc,prc){
        event.setView("person/editor");        
    }

    function save(event,rc,prc){

        var person = populateModel( "Person" );

        writeDump( person );abort;
    }

}

In the dump you will see that the name and email properties have been bound.

Setting Views

Views (Default Layout)

The event object is the object that will let you set the views that you want to render, so please explore its API in the CFC Docs. To quickly set a view to render, do the following:

event.setView( 'view' );

The view name is the name of the template in the views directory without appending the .cfm. If the view is inside another directory, you would do this:

event.setView( 'mydirectory/myView' );

The views you set will use the default layout defined in your configuration file which by default is the layouts/Main.cfm

We recommend that you set your views following the naming convention of your event. If your event is users.index, your view should be users/index. This will go a long way with maintainability and consistency and also will activate implicit views where you don't even have to use the set view method call.

View With Custom Layouts

You can also use the setView(), setLayout() methods to tell the framework which view and layout combination to use:

function index( event, rc, prc ){
    // Inline
    event.setView( view="main/index", layout="2columns" );
    
    // Concatenated
    event.setView( "main/index" )
        .setLayout( "2columns" );
}

Views With No Layout

You can also tell the framework to set a view for rendering by itself with no layout using the noLayout argument

function index( event, rc, prc ){
    // Inline
    event.setView( view="widgets/users", nolayout=true );   
}

setView() Arguments

Here are the arguments for the setView() method:

* @view The name of the view to set. If a layout has been defined it will assign it, else if will assign the default layout. No extension please
* @args An optional set of arguments that will be available when the view is rendered
* @layout You can override the rendering layout of this setView() call if you want to. Else it defaults to implicit resolution or another override.
* @module The explicit module view
* @noLayout Boolean flag, wether the view sent in will be using a layout or not. Default is false. Uses a pre set layout or the default layout.
* @cache True if you want to cache the rendered view.
* @cacheTimeout The cache timeout in minutes
* @cacheLastAccessTimeout The last access timeout in minutes
* @cacheSuffix Add a cache suffix to the view cache entry. Great for multi-domain caching or i18n caching.
* @cacheProvider The cache provider you want to use for storing the rendered view. By default we use the 'template' cache provider
* @name This triggers a rendering region.  This will be the unique name in the request for specifying a rendering region, you can then render it by passing the unique name to renderView();

Cached Views

You can leverage the caching arguments in the setView() method in order to render and cache the output of the views once the framework renders it. These cached views will be stored in the template cache region, which you can retrieve or purge by talking to it: getCache( 'template' ).

// Cache in the template cache for the default amount of time
event.setView( view='myView', cache=true );
// Cache in the template cache for up to 60 minutes, or 20 minutes after the last time it's been used
event.setView( view='myView', cache=true, cacheTimeout=60, cacheLastAccessTimeout=20 );
// Cache a different version of the view for each language the site has
event.setView( view='myView', cache=true, cacheSuffix=prc.language );

View Arguments

Data can be passed from your handler to the view via rc or prc. If you want to pass data to a view without polluting rc and prc, you can pass it directly via the args parameter, much like a method call.

var viewData = {
  data1 = service.getData1(),
  data2 = service.getData2()
};

event.setView( view='myView', args=viewData );

Access the data in the view like so:

<cfoutput>
  Data 1: #args.data1#<br>
  Data 2: #args.data2#
</cfoutput>

No Rendering

If you don't want to, you don't have to. The framework gives you a method in the event object that you can use if this specific request should just terminate gracefully and not render anything at all. All you need to do is use the event object to call on the noRender() method and it will present to the user a lovely white page of death.

event.noRender();

Sending Files

We all need to deliver files to users at one point in time. ColdBox makes it easy to deliver any type of file even binary files via the Request Context's (event) sendFile() method.

function report( event, rc, prc ){
    var prc.reportFile = reportService.createReport();
    
    event
        .sendFile(
            file = prc.reportFile,
            name = "UserReport.xls",
            deleteFile = true
        )
        .noRender();
}

Method Signature

The API Docs can help you see the entire format of the method: https://apidocs.ortussolutions.com/coldbox/5.1.4/coldbox/system/web/context/RequestContext.html#sendFile()

The method signature is as follows:

/**
 * This method will send a file to the browser or requested HTTP protocol according to arguments.
 * CF11+ Compatibility
 *
 * @file The absolute path to the file or a binary file to send
 * @name The name to send to the browser via content disposition header.  If not provided then the name of the file or a UUID for a binary file will be used
 * @mimeType A valid mime type to use.  If not passed, then we will try to use one according to file type
 * @disposition The browser content disposition (attachment/inline) header
 * @abortAtEnd If true, then this method will do a hard abort, we do not recommend this, prefer the event.noRender() for a graceful abort.
 * @extension Only used for binary files which types are not determined.
 * @deleteFile Delete the file after it has been streamed to the user. Only used if file is not binary.
 */
function sendFile(
    file="",
    name="",
    mimeType="",
    disposition="attachment",
    boolean abortAtEnd="false",
    extension="",
    boolean deleteFile=false
)

Please note that the file argument can be an absolute path or an actual binary file to stream out.

// Default Action
function index( event, rc, prc ){
    prc.welcomeMessage = "Welcome to ColdBox!";
    event.setView( "main/index" );
}
http://localhost:{port}/folder/handler/action
http://localhost:{port}/handler/action
http://localhost:{port}/handler
function index( event, rc, prc ){
    // param an incoming variable.
    event.paramValue( "name", "nobody" );
    // set a private variable
    prc.when = dateFormat( now(), "full" );
    // set the view to render
    event.setView( "hello/index" );
}
<cfoutput>
<p>Hello #encodeForHTML( rc.name )#, today is #prc.when#</p>
</cfoutput>
https://www.cfdocs.org/encodeforhtml
layouts and views
//set a value for views to use
event.setValue( "name", "Luis" );
event.setPrivateValue( "name", "Luis" );

// retrieve a value the user sent
event.getValue( "name" );
// retrieve a value the user sent or give me a default value
event.getValue( "isChecked", false );

// retrieve a private value
event.getPrivateValue( "name" );
// retrieve a private value or give me a default value
event.getPrivateValue( "isChecked", false );

// param a value
event.paramValue( "user_id", "" );
// param a private value
event.paramPrivateValue( "user_id", "" );

// remove a value
event.removeValue( "name" );
// remove a private value
event.removePrivateValue( "name" );

//check if value exists
if( event.valueExists( "name" ) ){

}
//check if private value exists
if( event.privateValueExists( "name" ) ){

}

// set a view for rendering
event.setView( 'blog/index' );

// set a layout for rendering
event.setLayout( 'main' );

// set a view and layout
event.setView( view="blog/userinfo", layout="ajax" );
request context
RC/PRC Data Super Highway
San Salvador, El Salvador
Florida International University
Ortus Solutions
Inland Empire
www.luismajano.com
ITESM
Industrias Bendek S.A.
MidAmerica Nazarene University
http://www.codersrevolution.com

Installation

Welcome to the world of ColdBox!

We are excited you are taking this development journey with us. Before we get started with ColdBox let's install CommandBox CLI, which will allow you to install/uninstall dependencies, start servers, have a REPL tool and much more.

IDE Tools

ColdBox has the following supported IDE Tools:

  • Sublime - https://packagecontrol.io/packages/ColdBox Platform

  • VSCode - https://marketplace.visualstudio.com/items?itemName=ortus-solutions.vscode-coldbox

  • CFBuilder - https://www.forgebox.io/view/ColdBox-Platform-Utilities

CommandBox CLI

The first step in our journey is to install CommandBox. CommandBox is a ColdFusion (CFML) Command Line Interface (CLI), REPL, Package Manager and Embedded Server. We will be using CommandBox for almost every excercise in this book and it will also allow you to get up and running with ColdFusion and ColdBox in a much speedier manner.

Note : However, you can use your own ColdFusion server setup as you see fit. We use CommandBox as everything is scriptable and fast!

Download CommandBox

You can download CommandBox from the official site: https://www.ortussolutions.com/products/commandbox#download and install in your preferred Operating System (Windows, Mac, *unix). CommandBox comes in two flavors:

  1. No Java Runtime (30mb)

  2. Embedded Runtime (80mb)

So make sure you choose your desired installation path and follow the instructions here: https://commandbox.ortusbooks.com/content/setup/installation.html

Starting CommandBox

Once you download and expand CommandBox you will have the box.exe or box binary, which you can place in your Windows Path or *Unix /usr/bin folder to have it available system wide. Then just open the binary and CommandBox will unpack itself your user's directory: {User}/.CommandBox. This happens only once and the next thing you know, you are in the CommandBox interactive shell!

CommandBox Shell

We will be able to execute a-la-carte commands from our command line or go into the interactive shell for multiple commands. We recommend the interactive shell as it is faster and can remain open in your project root.

All examples in this book are based on the fact of having an interactive shell open.

Installing ColdBox

To get started open the CommandBox binary or enter the shell by typing box in your terminal or console. Then let's create a new folder and install ColdBox into a directory.

mkdir myapp --cd
install coldbox

CommandBox will resolve coldbox from ForgeBox (www.forgebox.io), use the latest version available, download and install it in this folder alongside a box.json file which represents your application package.

Dir 0 Apr 25,2018 11:04:05 coldbox
File 112 Apr 25,2018 11:04:05 box.json

You can also install the latest bleeding edge version by using the coldbox@be slug instead, or any previous version.

That's it! CommandBox can now track this version of ColdBox for you in this directory.

Scaffolding ColdBox Applications

CommandBox comes with a coldbox create app command that can enable you to create application skeletons using one of our official skeletons or by creating your own application template:

  • Advanced : A tag based advanced template

  • AdvancedScript (default): A script based advanced template

  • elixir : A ColdBox Elixir based template

  • ElixirBower : A ColdBox Elixir + Bower based template

  • ElixirVueJS : A ColdBox Elixir + Vue.js based template

  • rest: A RESTFul services template

  • rest-hmvc: A RESTFul service built with modules

  • Simple : A traditional simple template

  • SuperSimple : The bare-bones template

You can find many scaffolding templates for ColdBox in our Github organization: github.com/coldbox-templates

Type coldbox create app help in CommandBox to get tons of help for scaffolding apps.

Uninstalling ColdBox

To uninstall ColdBox from this application folder just type uninstall coldbox.

Updating ColdBox

To update ColdBox from a previous version, just type update coldbox.

Installing ColdBox

Welcome to the world of ColdBox!

We are excited you are taking this development journey with us. Before we get started with ColdBox let's install CommandBox CLI, which will allow you to install/uninstall dependencies, start servers, have a REPL tool and much more.

IDE Tools

ColdBox has the following supported IDE Tools:

  • Sublime - https://packagecontrol.io/packages/ColdBox Platform

  • VSCode - https://marketplace.visualstudio.com/items?itemName=ortus-solutions.vscode-coldbox

  • CFBuilder - https://www.forgebox.io/view/ColdBox-Platform-Utilities

CommandBox CLI

The first step in our journey is to install CommandBox. CommandBox is a ColdFusion (CFML) Command Line Interface (CLI), REPL, Package Manager and Embedded Server. We will be using CommandBox for almost every excercise in this book and it will also allow you to get up and running with ColdFusion and ColdBox in a much speedier manner.

Note : However, you can use your own ColdFusion server setup as you see fit. We use CommandBox as everything is scriptable and fast!

Download CommandBox

You can download CommandBox from the official site: https://www.ortussolutions.com/products/commandbox#download and install in your preferred Operating System (Windows, Mac, *unix). CommandBox comes in two flavors:

  1. No Java Runtime (30mb)

  2. Embedded Runtime (80mb)

So make sure you choose your desired installation path and follow the instructions here: https://commandbox.ortusbooks.com/content/setup/installation.html

Starting CommandBox

Once you download and expand CommandBox you will have the box.exe or box binary, which you can place in your Windows Path or *Unix /usr/bin folder to have it available system wide. Then just open the binary and CommandBox will unpack itself your user's directory: {User}/.CommandBox. This happens only once and the next thing you know, you are in the CommandBox interactive shell!

CommandBox Shell

We will be able to execute a-la-carte commands from our command line or go into the interactive shell for multiple commands. We recommend the interactive shell as it is faster and can remain open in your project root.

All examples in this book are based on the fact of having an interactive shell open.

Installing ColdBox

To get started open the CommandBox binary or enter the shell by typing box in your terminal or console. Then let's create a new folder and install ColdBox into a directory.

mkdir myapp --cd
install coldbox

CommandBox will resolve coldbox from ForgeBox (www.forgebox.io), use the latest version available, download and install it in this folder alongside a box.json file which represents your application package.

Dir 0 Apr 25,2018 11:04:05 coldbox
File 112 Apr 25,2018 11:04:05 box.json

You can also install the latest bleeding edge version by using the coldbox@be slug instead, or any previous version.

That's it. CommandBox can now track this version of ColdBox for you in this directory. In the next section we will scaffold a ColdBox application using an application template.

You can find many scaffolding templates for ColdBox in our Github organization: github.com/coldbox-templates

Uninstalling ColdBox

To uninstall ColdBox from this application folder just type uninstall coldbox.

Updating ColdBox

To update ColdBox from a previous version, just type update coldbox.

ColdBox.cfc

The ColdBox configuration CFC is the heart of your ColdBox application. It contains the initialization variables for your application and extra information used by third-party modules and ultimately how your application boots up. In itself, it is also an event listener or ColdBox Interceptor, so it can listen to life-cycle events of your application.

This CFC is instantiated by ColdBox and decorated at runtime so you can take advantage of some dependencies.

  • Controller : Reference to the main ColdBox Controller object (coldbox.system.web.Controller)

  • AppMapping : The location of the application on the web server

  • LogBoxConfig : A reference to a LogBox configuration object

Configuration Storage

Once the application starts up, a reference to the instantiated configuration CFC will be stored in the configuration settings with the key coldboxConfig. You can then retrieve it later in your handlers, interceptors, modules, etc.

// retrieve it
config = getSetting( 'coldboxConfig' );

// inject it
property name="config" inject="coldbox:setting:coldboxConfig";

Configuration Interceptor

Another cool concept for the Configuration CFC is that it is also registered as a ColdBox Interceptor once the application starts up automatically for you. Create functions that will listen to application events:

function preProcess( event, interceptData, buffer, rc, prc ){
    writeDump( 'I just hijacked your app!' );abort;
}

Note that the config CFC does not have the same variables mixed into it that a "normal" interceptor has. You can still access everything you need, but will need to get it from the controller in the variables scope.

function preRender( event, interceptData, buffer, rc, prc ){
    controller.getWirebox().getInstance( 'loggerService' ).doSomething();
}

What's New With 5.6.0

ColdBox 5.6.0 is a minor version update with lots of fixes, improvements, performance enhancements and some nice new features. Below are the major areas of improvement and the full release notes. To update ColdBox or any of the standalone libraries just leverage CommandBox:

  • update coldbox

  • update logbox

  • update wirebox

  • update cachebox

Major Updates

Performance

We had two specific tickets that have resulted in extreme performance improvements for ALL ColdBox requests. You will feel and see the difference:

  • [] - Event Handler Bean: Single instance per handler action for major performance improvements

This ticket was contributed by Dom Watson () one of the lead engineers of the amazing PresideCMS project built on top of ColdBox. We worked together to avoid the creation of handler beans on each runnable event. We now cache each event handler bean representation which results in an extreme boost in performance. Thanks Dom!

  • [] - Remove afterInstanceAutowire interceptor in handlerService as afterHandlerCreation is now officially removed.

Thanks to our local mad scientist Brad Wood, he reported that the handler services still listened to ALL CFC creations in an application in order to relay an afterHandlerCreation interception point from the good 'ol 2.6 days. This has been finally removed and boom, another big boost in performance!

Better Bug Reports

We have enhanced the bug reporting templates to include much more information when dealing with exceptions:

  • Show SQL error details on Adobe CF

  • Current route, params and debug info

  • Contributing module for the current routed URL

Merging of HTTP Verbs

Thanks to our very own Eric Peterson, you can now merge HTTP verbs on the same route pattern, which you could not do before:

ColdBox Core Release Notes

Bugs

  • [] - ModuleService to add default route doesn't work correctly

  • [] - Fix default bug report to show SQL error detail for adobe SQL exceptions

  • [] - When doing package resolving if you are in a module it still tries to resolve a module

  • [] - Error in HTML helper WRAPPERATTRS doesn't exist in argument scope

  • [] - Include the colon for non 80 or 443 port numbers #419 in github

New Features

  • [] - Allow merging of HTTP verbs when doing separate verbs for the same route

  • [] - Update cfconfig to use env variables instead of inline mixins, modernizeOrDie

Improvements

  • [] - Add more current route information to the BugReport.cfm template

  • [] - Ability for bug reports and app to know which module contributed the incoming URL route.

  • [] - Use of .keyExists() can needlessly use memory in requests, suggest StructKeyExists() instead

  • [] - Event Handler Bean: Single instance per handler action for major performance improvements

  • [] - HandlerService.cfc$newHandler(): declares variables that are never used

  • [] - Remove afterInstanceAutowire interceptor in handlerService as afterHandlerCreation is now officially removed.

CacheBox Release Notes

Bugs

  • [] - AbstractCacheProvider.getOrSet(): local var unscoped when checking if null

Upgrading to ColdBox 5

The major compatibility issues will be covered as well as how to smoothly upgrade to this release from previous ColdBox versions. You can also check out the guide to give you a full overview of the changes.

event.getCurrentHandler() returns different results

In ColdBox 4, calling getCurrentHandler() would return the module if it existed:

In ColdBox 5, we correctly remove the module name so it can be retrieved via getCurrentModule() instead.

setNextEvent() deprecated in favor of relocate()

The setNextEvent() method has been renamed to relocate() to better adjust to modern times. This deprecation will be removed in future versions of ColdBox.

Modules AutoReload deprecated

The modules autoReload flag has been deprecated. This causes more headaches than anything. If you want changes reflected, reinit the framework.

onInvalidEvent renamed to invalidEventHandler

The ColdBox construct setting onInvalidEvent has been renamed now to invalidEventHandler. This will be removed in future versions of ColdBox.

setAutoReload() Removed

The setAutoReload() flag in the SES interceptor has been removed. It is evil I tell you. If you want your routing to be re-read, then reinit the framework.

BuildLink LinkTo Argument Deprecated

The buildLink() method had the argument linkTo to denote the event or route to link to. This was verbose, so we shortened it to to:

Railo Support Dropped

Railo support dropped. Any classes that started with the word Railo need to be changed to Lucee especially on the CacheBox providers.

ColdFusion 9-10 Support Dropped

ColdFusion 9-10 support has been dropped. Adobe doesn't support them anymore, so neither do we.

Datasources Configuration Dropped

The datasources configuration setting directive has been dropped in favor of just leveraging the settings directive. Just move your datasource metadata into the settings struct and reference it using the settings DSL.

Previous

New

DSL Builder Interface Update

The WireBox interface: coldbox.system.ioc.dsl.IDSLBuilder has changed in ColdBox 5, so if you are implementing your own DSLs, then you must update it like so:

In ColdBox 5 we removed the output attribute:

Ticket Reference:

Interceptor Update

appendToBuffer() Dropped

The string buffer has been updated in order to become thread safe and there are several methods that have been deprecated. Instead of using appendToBuffer() you need to use the buffer argument.

Previous

New

Ticket Reference:

Adding A Model

Let's complete our saga into MVC by developing the M, which stands for . This layer is all your business logic, queries, external dependencies, etc. of your application, which represents the problem to solve or the domain to solve.

WireBox

This layer is controlled by , the dependency injection framework within ColdBox, which will give you the flexibility of wiring your objects and persisting them for you.

Creating A Service Model

Let's create a simple contact listing, so open up CommandBox and issue the following command:

This will create a models/ContactService.cfc with a getAll() method and a companion unit test at tests/specs/unit/ContactServiceTest.cfc. Let's open the model object:

Notice the singleton annotation on the component tag. This tells WireBox that this service should be cached for the entire application life-span. If you remove the annotation, then the service will become a transient object, which means that it will be re-created every time it is requested.

Add Some Data

Let's mock an array of contacts so we can display them later. We can move this to a SQL call later.

We also have created a project to mock any type of data: . Just use CommandBox to install it: install mockdatacfc

You can then leverage it to mock your contacts or any simple/complex data requirement.

Wiring Up The Model To a Handler

We have now created our model so let's tell our event handler about it. Let's create a new handler using CommandBox:

This will create the handler/contacts.cfc handler with an index() action, the views/contacts/index.cfm view and the accompanying integration test tests/specs/integration/contactsTest.cfc.

Let's open the handler and add a new ColdFusion property that will have a reference to our model object.

Please note that inject annotation on the property definition. This tells WireBox what model to inject into the handler's variablesscope.

By convention it looks in the models folder for the value, which in our case is ContactService. Now let's call it and place some data in the private request collection prc so our views can use it.

Presenting The Data

Now that we have put the array of contacts into the prc struct as aContacts, let's display it to the screen using ColdBox's HTML Helper.

The ColdBox HTML Helper is a companion class that exists in all layouts and views that allows you to generate semantic HTML5 without the needed verbosity of nesting, or binding to ORM/Business objects.

Please check out the API Docs to discover the HTML Helper:

Open the contacts/index.cfm and add the following to the view:

Note: If your models are singletons, they will persist for the life-span of your ColdFusion application. To see code changes for singletons, you have to reinit the framework by using the ?fwreinit={password} Url action or via CommandBox using coldbox reinit. Please check out the API Docs to discover CommandBox: []

That's it! Execute the event: http://localhost:{port}/contacts/index and view the nice table of contacts being presented to you.

Congratulations, you have made a complete MVC circle!

Tip You can find much more information about models and dependency injection in our

Environments

The configuration CFC has embedded environment control and detection built-in. Environments kan be detected by:

  • regex matching against cgi.http_host

  • detection of an environmental variable called ENVIRONMENT ( Coldbox 5.2 and higher )

  • usage of a detectEnvironment() function

The first option (regex matching) is the easiest to use, but not very reliable if you are using multiple hostnames or commandbox for re-initialization.

If you are using commandbox please read ALL options below

Default: Regex matching against cgi.http_host

To detect your environments you will setup a structure called environments in your coldbox configuration with the named environments and their associated regular expressions for its cgi host names to match for you automatically. If the framework matches the regex with the associated cgi.http_host, it will set a setting called Environment in your configuration settings and look for that environment setting name in your CFC as a method by convention. That's right, it will check if your CFC has a method with the same name as the environment and if it exists, it will call it for you. Here is where you basically override, remove, or add any settings according to your environment.

Warning : The environment detection occurs AFTER the configure() method is called. Therefore, whatever settings or configurations you have on the configure() method will be stored first, treat those as Production settings.

The regex match will also create a global setting called "environment" which you can access and use like this:

In the above example, I declare a development key with a value list of regular expressions. If I am in a host that starts with cf2016, this will match and set the environment setting equal to development. It will then look for a development method in this CFC and execute it.

Detection of an environmental variable called ENVIRONMENT

If you are using environmental variables for your different environments, you can specify an environmental variable called ENVIRONMENT and name it staging, development, testing etcetera, depending on the required environment. As in the regex example, a function named after your environment (e.g. staging() or development() ) will be called after your configure method.

This method is more reliable than relying on cgi.http_host, since it will never change once configured correctly.

Custom Environment Detection

If you are NOT using environmental variables you can use your own detection algorithm instead of looking at the cgi.http_host variable. You will NOT fill out an environments structure but actually create a method with the following signature:

This method will be executed for you at startup and it must return the name of the environment the application is on. You can check for any condition which distinguishes your environment from your other environments. Als long as you return a environment name based on your own logic it will then store it and execute the method if it exists.

Around Advices

Around advices are the most powerful of all as you completely hijack the requested action with your own action that looks, smells and feels exactly as the requested action. This is usually referred to as a

This will allow you to run both before and after advices but also surround the method call with whatever logic you want like transactions, try/catch blocks, locks or even decide to NOT execute the action at all.

You can do it globally by using the aroundHandler() method or targeted to a specific action around{actionName}().

Examples

The arguments received by these interceptors are:

  • event : The request context reference

  • targetAction : The function pointer to the action that got the around interception. It will be your job to execute it (Look at samples)

  • eventArguments : The struct of extra arguments sent to an action if any

  • rc : The RC reference

  • prc : The PRC Reference

Exceptions & Only Lists

You can fine tune these interception methods by leveraging two public properties in the handler:

  • this.aroundhandler_only : A list of actions that the aroundHandler() action will fire ONLY!

  • this.aroundhandler_except : A list of actions that the aroundHandler() action will NOT fire on

environments = {
    // The key is the name of the environment
    // The value is a list of regex to match against cgi.http_host
    development = "^cf2016.,^lucee.,localhost",
    staging = "^stg"
};
if ( getSetting('environment') == 'development' ){
    doSomeMajik();
}
/**
* Executed whenever the development environment is detected
*/
function development(){
    // Override coldbox directives
    coldbox.handlerCaching = false;
    coldbox.eventCaching = false;
    coldbox.debugPassword = "";
    coldbox.reinitPassword = "";

    // Add dev only interceptors
    arrayAppend( interceptors, {class="#appMapping#.interceptors.CustomLogger} );
}
string public function detectEnvironment(){
}
router
    .post( "photos/", "photos.create" )
    .get( "photos/", "photos.index" )
    .delete( "photos/", "photos.remove" );
COLDBOX-799
https://twitter.com/dom_watson
COLDBOX-810
COLDBOX-778
COLDBOX-794
COLDBOX-796
COLDBOX-806
COLDBOX-811
COLDBOX-812
COLDBOX-813
COLDBOX-795
COLDBOX-797
COLDBOX-798
COLDBOX-799
COLDBOX-800
COLDBOX-810
CACHEBOX-56
module:handler
handler
<!-- Before -->
<a href="#event.buildLink( linkTo="/about/us" )#">About Us</a>

<!-- After -->
<a href="#event.buildLink( to="/about/us" )#">About Us</a>
// Datasource definitions
datasources = {
    mydsn = {
        type = "mysql",
        username = "luis",
        name = "mydsn"
    }
}
// Injections
property name="dsn" inject="coldbox:datasource:mydsn"
// Settings
settings = {
    mydsn = {
        type = "mysql",
        username = "luis",
        name = "mydsn"
    }
}
// Injections

property name="dsn" inject="coldbox:setting:mydsn"
Old DSL Builder, notice the output attribute
public any function init( required any injector ) output="false"{ 
public any function process( required definition, targetObject ) output="false"{
New DSL Builder
public any function init( required any injector ) { 
public any function process( required definition, targetObject ) {
appendToBuffer( generatedHTML );
buffer.append( generatedHTML );
What's New
https://ortussolutions.atlassian.net/browse/COLDBOX-697
https://ortussolutions.atlassian.net/browse/COLDBOX-654

What's New With 5.1.0

ColdBox 5.1.0 is a minor version update with lots of fixes, improvements and some new features. Below are the major areas of improvement and the full release notes. To update your ColdBox installation just leverage CommandBox: update coldbox

Event Caching Improvements

The event caching cleanup and clearing methods did not work when using granular query strings. This has now been resolved and optimized.

New Auto-Deserialization of JSON Payloads

If you are working with any modern JavaScript framework, this feature is for you. ColdBox on any incoming request will inspect the HTTP Body content and if the payload is JSON, it will deserialize it for you and if it is a structure/JS object, it will append itself to the request collection for you. So if we have the following incoming payload:

{
    "name" : "Jon Clausen",
    "type" : "awesomeness",
    "data" : [ 1,2,3 ]
}

The request collection will have 3 keys for name, type and data according to their native CFML type.

Flash Scope getAll()

The flash scope needed a way to get all of its name-value pair elements in one shot, you can now with the getAll() method.

Complete Rewrite of the HTML Helper

The HTML helper has been completely rewritten in 5.1 into script notation, optimized for performance and security. All HTML output is now XSS encoded for attributes and tag content.

View and Directory Helper Combo

You can now declare a view and directory helper and ColdBox will use them both instead of always picking the view helper only. The order of inclusion is:

  • directory helper

  • view helper

  • view

ColdBox Fail Fast

This is a nice feature that will give your applications stability when doing deployments or production reinits. We have added a new application variable flag: application.fwReinit which is set to true when the framework is reinitializing and false when it completes. We have also added a new directive called COLDBOX_FAIL_FAST which defaults to true.

If fail fast is activated, the framework will present a nice message to users that the application is not yet available instead of holding them in a queue waiting for the reinit or application load to finish. This fail fast will release your traffic queue and produce less timeouts and failures.

The fail fast directive can also be a closure. Then we will execute your closure and you can do whatever you like within it to advice your users' about the reinit. Below you can see what happens with the fail fast code.

// Global flag to denote if we are in mid reinit or not.
cfparam( name="application.fwReinit", default =false );

// Fail fast so users coming in during a reinit just get a please try again message.
if( application.fwReinit ){

    // Closure or UDF
    if( isClosure( variables.COLDBOX_FAIL_FAST ) || isCustomFunction( variables.COLDBOX_FAIL_FAST ) ){
        variables.COLDBOX_FAIL_FAST();
    } 
    // Core Fail Fast Option
    else if( isBoolean( variables.COLDBOX_FAIL_FAST ) && variables.COLDBOX_FAIL_FAST ){
        writeOutput( 'Oops! Seems ColdBox is still not ready to serve requests, please try again.' );
        // You don't have to return a 500, I just did this so JMeter would report it differently than a 200 
        cfheader( statusCode="503", statustext="ColdBox Not Available Yet!" );
    }

    return false;
}

Release Notes

Bugs

  • [COLDBOX-679] - viewmodule parameter not used in system.web.renderer.renderLayout

  • [COLDBOX-680] - When using Resources the POST incorrectly sets action to UPDATE instead of CREATE

  • [COLDBOX-681] - AbstractFlashScope fails on autoPurge property check

  • [COLDBOX-683] - Event Caching Should Include Response Headers

  • [COLDBOX-686] - coldbox create app template doesn't work with a servlet context other than /

  • [COLDBOX-687] - Event caching broken due to not evaluating renderdata as a valid struct thanks to the EC OIL Team (Christian,Sebastian,Didier)

New Features

  • [COLDBOX-682] - Add auto-deserialization of inbound JSON payloads into the RC on request capture

  • [COLDBOX-689] - New flash method: getAll() which retrieves a struct of all flash keys and their content

  • [COLDBOX-693] - Complete rewrite of HTML Helper to Script

  • [COLDBOX-694] - HTML Helper XSS Encodes all output from content to attributes by default

Improvements

  • [COLDBOX-343] - Allow view helper AND directory helper at the same time.

  • [COLDBOX-592] - Have ColdBox bootstrap advertize when Coldbox is reinitting, and have a fail fast routine

  • [COLDBOX-678] - Default Flash Ram to client if session scope is disabled

  • [COLDBOX-685] - Event Cache Key and Storage Enhancements to allow for granular querystring evictions

  • [COLDBOX-690] - Add support for cgi.https to isSSL()

  • [COLDBOX-691] - Ignore AllowedMethods when using runEvent on non-default method calls

RESTFul Data

Out of the box, ColdBox gives you all the RESTFul capabilities you will need to create robust and scalable RESTFul services. Let's add some RESTFul capabilities to our contact listing we created in the previous section.

Tip: You can find much more information about building ColdBox RESTFul services in our full docs.

Producing JSON

If you know beforehand what type of format you will be responding with, you can leverage ColdBox 5's auto-marshalling in your handlers. By default, ColdBox detects any return value from handlers and if they are complex it will convert them to JSON automatically for you:

contacts.cfc
any function index( event, rc, prc ){
    return contactService.getAll();    
}

That's it! ColdBox detects the array and automatically serializes it to JSON. Easy Peasy!

If you want your incoming JSON requestbody to be handled the same as FORM and URL variables you can enable coldbox.jsonPayloadToRC = true in your coldbox config. See releasenotes 5.1.0 and 5.1.2 for details.

renderData()

The request context object has a special function called renderData() that can take any type of data and marshall it for you to other formats like xml, json, wddx, pdf, text, html or your own type.

Tip: You can find more information at the API Docs for renderData() here http://apidocs.ortussolutions.com/coldbox/current/index.html?coldbox/system/web/context/RequestContext.html#renderData()

So let's open the handlers/contacts.cfc and add to our current code:

contacts.cfc
any function index( event, rc, prc ){
    prc.aContacts = contactService.getAll();    
    event.renderData( data=prc.aContacts, formats="xml,json,pdf,html" );
}

We have added the following line:

event.renderData( data=prc.aContacts, formats="xml,json,pdf,html" );

This tells ColdBox to render the contacts data in 4 formats: xml, json, pdf and html. WOW! So how would you trigger each format? Via the URL of course.

Format Detection

ColdBox has the ability to detect formats via URL extensions or an incoming Accepts header. If no extension is sent, then ColdBox attempts to determine the format by inspecting the Accepts header. If we still can't figure out what format to choose, the default of html is selected for you.

# Default: The view is presented using no extension or html,cfm
http://localhost:{port}/contacts/index
http://localhost:{port}/contacts/index.html
http://localhost:{port}/contacts/index.cfm

# JSON output
http://localhost:{port}/contacts/index.json
# OR Accepts: application/json

# XML output 
http://localhost:{port}/contacts/index.xml
# OR Accepts: application/xml

# PDF output
http://localhost:{port}/contacts/index.pdf
# OR Accepts: application/pdf

Tip: You can also avoid the extension and pass a URL argument called format with the correct format type: ?format=json.

Routing

Let's add a new route to our system that is more RESTFul than /contacts/index.json. You will do so by leveraging the application's router found at config/Router.cfc. Find the configure() method and let's add a new route:


// Restuful Route
route( 
    pattern="/api/contacts",
    target="contacts.index",
    name="api.contacts"
);

// Default Route
route( ":handler/:action?" ).end();

The route() method allows you to register new URL patterns in your application and immediately route them to a target event. You can even give it a human readable name that can be later referenced in the buildLink() method.

Make sure you add routes above the default ColdBox route. If not, your route will never fire.

We have now created a new URL route called /api/contacts that if detected will execute the contacts.index event. Now reinit the application, why, well we changed the application router and we need the changes to take effect.

Tip: Every time you add new routes make sure you reinit the application: http://localhost:{port}/?fwreinit.

You can now visit the new URL pattern and you have successfully built a RESTFul API for your contacts.

http://localhost:{port}/api/contacts.json

You can find much more about routing in our full docs

Rewrite Rules

Here are just a few of those rewrite rules for you for major rewrite engines. You can spice them up as needed.

.htaccess

RewriteEngine on
#if this call related to adminstrators or non rewrite folders, you can add more here.
RewriteCond %{REQUEST_URI} ^/(.*(CFIDE|cfide|CFFormGateway|jrunscripts|railo-context|lucee|mapping-tag|fckeditor)).*$
RewriteRule ^(.*)$ - [NC,L]

#Images, css, javascript and docs, add your own extensions if needed.
RewriteCond %{REQUEST_URI} \.(bmp|gif|jpe?g|png|css|js|txt|xls|ico|swf)$
RewriteRule ^(.*)$ - [NC,L]

#The ColdBox index.cfm/{path_info} rules.
RewriteRule ^$ index.cfm [QSA,NS]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.cfm?%{REQUEST_URI} [QSA,L,NS]

nginx

################### LOCATION: ROOT #####################
location / {
     # First attempt to serve real files or directory, else it sends it to the @rewrite location for processing
     try_files $uri $uri/ @rewrite;
}

################### @REWRITE: COLDBOX SES RULES #####################
# Rewrite for ColdBox (only needed if you want SES urls with this framework)
# If you don't use SES urls you could do something like this
# location ~ \.(cfm|cfml|cfc)(.*)$ {
location @rewrite {
  rewrite ^/(.*)? /index.cfm/$request_uri last;
  rewrite ^ /index.cfm last;
}

################### CFM/CFC LUCEE HANDLER #####################
# The above locations will just redirect or try to serve cfml files
# We need this to tell NGinx that if we receive the following requests to pass them to Lucee
location ~ \.(cfm|cfml|cfc|jsp)(.*)$ {
# Include our connector
include lucee.conf;
}

IIS7 web.config

Please note that URL rewriting is handled by an optional module in IIS. More info here: https://www.iis.net/downloads/microsoft/url-rewrite

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <rewrite>
            <rules>
               <rule name="Application Administration" stopProcessing="true">
                    <match url="^(.*)$" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{SCRIPT_NAME}" pattern="^/(.*(CFIDE|cfide|CFFormGateway|jrunscripts|lucee|railo-context|fckeditor)).*$" ignoreCase="false" />
                    </conditions>
                    <action type="None" />
                </rule>
                <rule name="Flash and Flex Communication" stopProcessing="true">
                    <match url="^(.*)$" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{SCRIPT_NAME}" pattern="^/(.*(flashservices|flex2gateway|flex-remoting)).*$" ignoreCase="false" />
                    </conditions>
                    <action type="Rewrite" url="index.cfm/{PATH_INFO}" appendQueryString="true" />
                </rule>
                <rule name="Static Files" stopProcessing="true">
                    <match url="^(.*)$" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{SCRIPT_NAME}" pattern="\.(bmp|gif|jpe?g|png|css|js|txt|pdf|doc|xls)$" ignoreCase="false" />
                    </conditions>
                    <action type="None" />
                </rule>
                <rule name="Insert index.cfm" stopProcessing="true">
                    <match url="^(.*)$" ignoreCase="false" />
                    <conditions logicalGrouping="MatchAll">
                        <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
                    </conditions>
                    <action type="Rewrite" url="index.cfm/{PATH_INFO}" appendQueryString="true" />
                </rule>
            </rules>
        </rewrite>
    </system.webServer>
</configuration>

Tuckey Rewrite

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.2//EN" "http://tuckey.org/res/dtds/urlrewrite3.2.dtd">
<urlrewrite>
    <rule>
        <note>ContentBox Media URLs</note>
        <condition type="request-uri" operator="equal">^/__media/.*$</condition>
        <from>^/(.*)$</from>
        <to type="passthrough">/index.cfm/$1</to>
    </rule>
    <rule>
        <note>Generic Front-Controller URLs</note>
        <condition type="request-uri" operator="notequal">/(index.cfm|robots.txt|osd.xml|flex2gateway|cfide|cfformgateway|railo-context|lucee|admin-context|modules/contentbox-dsncreator|modules/contentbox-installer|modules/contentbox|files|images|js|javascripts|css|styles|config).*</condition>
        <condition type="request-uri" operator="notequal">\.(bmp|gif|jpe?g|png|css|js|txt|xls|ico|swf|woff|ttf|otf)$</condition>
        <!-- For some reason this is not working now.
        <condition type="request-filename" operator="notdir"/>
        <condition type="request-filename" operator="notfile"/>
        -->
        <from>^/(.+)$</from>
        <to type="passthrough">/index.cfm/$1</to>
    </rule>
</urlrewrite>

Viewlets - Reusable Events

A viewlet is a self sufficient view or a widget that can live on its own, its data is pre-fetched and can just be renderer anywhere in your system.

What in the world is this? Well, imagine a portal, in which each section of the portal is self-sufficient, with controls and data. You don't want to call all the handlers for this data for every single piece of content. It's not efficient, you need to create a separation. Well, a viewlet is such a separation that provides you with the ability to create reusable events. So how do we achieve this?

  1. You will use the method runEvent() anywhere you want a viewlet to be displayed or the content rendered. This calls an internal event that will be in charge to prepare and render the viewlet.

  2. Create the portable event but make sure it returns the produced content.

Simple Example

<div id="leftbar">
#runEvent( event='viewlets.userinfo', eventArguments={ userID=4 } )#
</div>

This code just renders out the results of a runEvent() method call. Please note that you can pass in arguments to the event using the eventArguments argument. This makes the event act like a method call with arguments to ti. Remember that all events you call via runEvent() will share the same RC/PRC.

I would suggest you look at the API docs to discover all arguments to the runEvent() method call.

Event Code

viewlets.cfc
function userinfo( event, rc, prc, userID=0 ){

    // place data in prc and prefix it to avoid collisions
    prc.userinfo_qData = userService.getUserInfo( arguments.userID );

    // render out content 
    return renderView( "viewlets/userinfo" );
}

As you can see from the code above, the handler signature can accept arguments which are passed via the eventArguments structure. It talks to a service layer and place some data on the private request collection the viewlet will use. It then returns the results of a renderView() call that will render out the exact viewlet I want. You can be more creative and do things like:

  • render a layout + view combo

  • render data

  • return your own custom strings

  • etc

Caution We would suggest you namespace or prefix your private request collection variables for viewlets in order to avoid collisions from multiple viewlet events in the same execution thread or instead pass the necessary arguments into a view via the args argument.

View Code

viewlets/userinfo.cfm
<cfoutput>
    <div>User Info Panel</div>
    <div>Username: #prc.userinfo_qData.username#</div>
    <div>Last Login: #prc.userinfo_qData.lastLogin#</div>
</cfoutput>

The view is a normal standard view, it doesn't even know it is a viewlet, remember, views are DUMB!

Content Variables

A content variable is a variable that contains HTML/XML or any kind of visual content that can easily be rendered anywhere. So instead of running the viewlet event in the view, you can abstract it to the controller layer and assign the output to a content variable:

function home(event,rc,prc){

    // render some content variables with funky arguments
    prc.sideColumn = renderView(view='tags/sideColumn',cache=true,cacheTimeout=10);

    // set view
    event.setView('general/home');
}

So how do I render it?

<div id="content">
  <div id="leftColumn">
  <cfoutput>#prc.sideColumn#</cfoutput>
  </div>

  <div id="mainView">
  <cfoutput>#renderView()#</cfoutput>
  </div>
</div>

Another example, is what if we do not know if the content variable will actually exist? How can we do this? Well, we use the event object for this and its magic getValue() method.

<div id="content">
  <div id="leftColumn">
  <cfoutput>#prc.sideColumn ?: ''#</cfoutput>
  </div>

  <div id="mainView">
  <cfoutput>#renderView()#</cfoutput>
  </div>
</div>

So now, if no content variable exists, an empty string will be rendered.

Important String manipulation in Java relies on immutable structures, so performance penalities might ensue. If you will be doing a lot of string manipulation, concatenation or rendering, try to leverage native java objects: StringBuilder or StringBuffer

View Caching

You can also pass in the caching arguments below and your view will be rendered once and then cached for further renderings. Every ColdBox application has two active cache regions: default and template. All view and event caching renderings go into the template cache.

Argument

Type

Required

Default

Description

cache

boolean

false

false

Cache the view to be rendered

cacheTimeout

numeric

false

(provider default)

The timeout in minutes or whatever the cache provider defines

cacheLastAccessTimeout

numeric

false

(provider default)

The idle timeout in minutes or whatever the cache provider defines

cacheSuffix

string

false

---

Adds a suffix key to the cached key. Used for providing uniqueness to the cacheable entry

component name="general"{

    function index(event,rc,prc){

        // call some model for data and put into the request collection
        prc.myQuery = getInstance('MyService').getData();    
        // view with caching parameters
        event.setView(
            view="general/index",
            cache=true,
            cacheTimeout=60,
            cacheLastAccessTimeout=15,
            cacheSuffix=getfwLocale()
        );
    }

}

Purging Views

So now that our views are cached, how do I purge them programmatically? Well, you need to talk to the template cache provider and use the clearing methods:

// get a reference to the template cache
cache = cachebox.getCache( 'template' );
// or via shortcut notation
cache = getCache( "template" );
// or injection
property name="cache" inject="cachebox:template";

Then we can perform several operations on views:

  • clearView(string viewSnippet): Used to clear a view from the cache by using a snippet matched according to name + cache suffix.

  • clearMultiView(any viewSnippets): Clear using a list or array of view snippets.

  • clearAllViews([boolean async=true]) : Can clear ALL cached views in one shot and can be run asynchronously.

cachebox.getCache( 'template' ).clearView('general/index');
cachebox.getCache( 'template' ).clearAllViews(async=true);
cachebox.getCache( 'template' ).clearMultiView('general/index','index','home');

Disable View Caching

To turn off view caching for your entire application, set the viewCaching setting to false in your config/Coldbox.cfc config file.

coldbox = {
// Activate view caching
viewCaching = false
}

Executing Events

Apart from executing events from the URL/FORM or Remote interfaces, you can also execute events internally, either public or private from within your event handlers or from interceptors, other handlers, layouts or views.

RunEvent()

You do this by using the runEvent() method which is inherited from our FrameworkSuperType class. Here is the method signature:

/**
* Executes events with full life-cycle methods and returns the event results if any were returned.
* @event The event string to execute, if nothing is passed we will execute the application's default event.
* @prePostExempt If true, pre/post handlers will not be fired. Defaults to false
* @private Execute a private event if set, else defaults to public events
* @defaultEvent The flag that let's this service now if it is the default event running or not. USED BY THE FRAMEWORK ONLY
* @eventArguments A collection of arguments to passthrough to the calling event handler method
* @cache Cached the output of the runnable execution, defaults to false. A unique key will be created according to event string + arguments.
* @cacheTimeout The time in minutes to cache the results
* @cacheLastAccessTimeout The time in minutes the results will be removed from cache if idle or requested
* @cacheSuffix The suffix to add into the cache entry for this event rendering
* @cacheProvider The provider to cache this event rendering in, defaults to 'template'
*/
function runEvent(
	event="",
	boolean prePostExempt=false,
	boolean private=false,
	boolean defaultEvent=false,
	struct eventArguments={},
	boolean cache=false,
	cacheTimeout="",
	cacheLastAccessTimeout="",
	cacheSuffix="",
	cacheProvider="template"
)

The interesting aspect of internal executions is that all the same rules apply, so your handlers can return content like widgets, views, or even data. Also, the eventArguments enables you to pass arguments to the method just like method calls:

Executions

//public event
runEvent( 'users.save' );

//post exempt
runEvent( event='users.save', prePostExempt=true );

//Private event
runEvent( event='users.persist', private=true );

// Run event as a widget
<cfoutput>#runEvent(
    event         = 'widgets.userInfo',
    prePostExempt = true,
    eventArguments= { widget=true }
)#</cfoutput>

// Run with Caching
runEvent( event="users.list", cache=true, cacheTimeout=30 );

Declaration

// handler responding to widget call
function userInfo( event, rc, prc, widget=false ){

    prc.userInfo = userService.get( rc.id );

    // set or widget render
    if( arguments.widget ){
        return renderView( "widgets/userInfo" );
    }

    // else set view
    event.setView( "widgets/userInfo" );
}

Caching

As you can see from the function signature you can tell ColdBox to cache the result of the event call. All of the cached content will go into the template cache by default unless you use the cacheProvider argument. The cache keys are also based on the name of the event and the signature of the eventArguments structure. Meaning, the framework can cache multiple permutations of the same event call as long as the eventArguments are different.

runEvent( event="users.widget", eventArguments={ max=10, page=1 }, cache=true );

// Cached as a new key
runEvent( event="users.widget", eventArguments={ max=10, page=2 }, cache=true );

Tip: You can disable event caching by using the coldbox.eventCaching directive in your config/ColdBox.cfc

coldbox create model name="ContactService" methods="getAll" persistence="singleton"
component singleton accessors="true"{      

    ContactService function init(){         
        return this;     
    }

    function getAll(){      

    }

}
component singleton accessors="true"{

    ContactService function init(){
        variables.data = [
            { id=1, name="coldbox" },
            { id=2, name="superman" },
            { id=3, name="batman" }
        ];    
        return this;

    }

    function getAll(){
        return variables.data;
    }

}
coldbox create handler name="contacts" actions="index"
component{ 

    property name="contactService" inject="ContactService";

    any function index( event, rc, prc ){ 
        event.setView( "contacts/index" ); 
    }
}
any function index( event, rc, prc ){
    prc.aContacts = contactService.getAll();
    event.setView( "contacts/index" );
}
<cfoutput>
<h1>My Contacts</h1>

#html.table( data=prc.aContacts, class="table table-striped" )#
</cfoutput>
model
WireBox
MockDataCFC
http://apidocs.ortussolutions.com/coldbox/current/index.html?coldbox/system/modules/HTMLHelper/models/HTMLHelper.html
https://apidocs.ortussolutions.com/commandbox/4.5.0/index.html
full docs
users.list => users.aroundHandler() <=> list()
// executes around any action
function aroundHandler(event,targetAction,eventArguments,rc,prc){
}

// executes around the list() action ONLY
function aroundList(event,targetAction,eventArguments,rc,prc){
}

// Around handler advice for transactions
function aroundHandler(event,targetAction,eventArguments,rc,prc){

    // log the call
    log.debug("Starting to execute #targetAction.toString()#" );

    // start a transaction
    transaction{

        // prepare arguments for action call
        var args = {
            event = arguments.event,
            rc    = arguments.rc,
            prc   = arguments.prc

        };
        structAppend( args, eventArguments );
        // execute the action now
        var results = arguments.targetAction( argumentCollection=args );
    }

    // log the call
    log.debug( "Ended executing #targetAction.toString()#" );

    // return if it exists
    if( !isNull( results ) ){ return results; }
}

// Around handler advice for try/catches
function aroundHandler(event,targetAction,eventArguments,rc,prc){

    // log the call
    if( log.canDebug() ){
        log.debug( "Starting to execute #targetAction.toString()#" );
    }

    // try block
    try{

        // prepare arguments for action call
        var args = {
            event = arguments.event,
            rc    = arguments.rc,
            prc   = arguments.prc

        };
        structAppend( args, eventArguments );
        // execute the action now
        return arguments.targetAction( argumentCollection=args );
    }
    catch(Any e){
        // log it
        log.error("Error executing #targetAction.toString()#: #e.message# #e.detail#", e);
        // set exception in request collection and set view to render
        event.setValue( "exception", e)
            .setView( "errors/generic" );

    }

}
// only fire for the actions: save(), delete()
this.aroundhandler_only = "save,delete";
// DO NOT fire for the actions: login(), doLogin(), logout()
this.aroundhandler_except = "login,doLogin,logout"
proxy design pattern.

Configuration Directives

The basic configuration object has 1 method for application configuration called configure() where you will place all your configuration directives and settings:

ColdBox.cfc
/**
* A simple CFC that configures a ColdBox application.  You can even extend, compose, strategize and do your OO goodness.
*/
component{

    // Mandatory configuration method
    function configure(){
        coldbox = {
          
        };
    }
    
}

Directives

Inside of this configuration method you will place several core and third-party configuration structures that can alter your application settings and behavior. Below are the core directives you can define:

Directive

Type

Description

struct

An optional structure used to configure CacheBox. If not setup the framework will use its default configuration found in /coldbox/system/web/config/CacheBox.cfc

struct

The main coldbox directives structure that holds all the coldbox settings.

struct

A structure where you will configure the application convention names

struct

A structure where you will configure environment detection patterns

struct

A structure where you will configure the

struct

An optional structure to configure application wide interceptor behavior

array

An optional array of interceptor declarations for your application

struct

A structure where you define how the layout manager behaves in your application

array

An array of layout declarations for implicit layout-view-folder pairings in your application

struct

An optional structure to configure the logging and messaging in your application via LogBox

struct

An optional structure to configure application wide module behavior

struct

An optional structure to configure individual modules installed in your application.

struct

A structure where you can put your own application settings

struct

An optional structure used to define how WireBox is loaded

Bootstrapper - Application.cfc

The Application.cfc is one of the most important files in your application as it is where you define all the implicit ColdFusion engine events, session, client scopes, ORM, etc. It is also how you tell ColdFusion to bootstrap the ColdBox Platform for your application. There are two ways to bootstrap your application:

  1. Leverage composition and bootstrap ColdBox (Default)

  2. Leverage inheritance and bootstrap ColdBox

The composition approach allows you to have a more flexible configuration as it will allow you to use per-application mappings for the location of the ColdBox Platform.

Tip: To see the difference, just open the appropriate Application.cfc in the application templates.

Composition

Application.cfc
component{
    // Application properties
    this.name = hash( getCurrentTemplatePath() );
    this.sessionManagement = true;
    this.sessionTimeout = createTimeSpan(0,0,30,0);
    this.setClientCookies = true;

    // COLDBOX STATIC PROPERTY, DO NOT CHANGE UNLESS THIS IS NOT THE ROOT OF YOUR COLDBOX APP
    COLDBOX_APP_ROOT_PATH = getDirectoryFromPath( getCurrentTemplatePath() );
    // The web server mapping to this application. Used for remote purposes or static purposes
    COLDBOX_APP_MAPPING   = "";
    // COLDBOX PROPERTIES
    COLDBOX_CONFIG_FILE      = "";
    // COLDBOX APPLICATION KEY OVERRIDE
    COLDBOX_APP_KEY          = "";

    // application start
    public boolean function onApplicationStart(){
        application.cbBootstrap = new coldbox.system.Bootstrap( COLDBOX_CONFIG_FILE, COLDBOX_APP_ROOT_PATH, COLDBOX_APP_KEY, COLDBOX_APP_MAPPING );
        application.cbBootstrap.loadColdbox();
        return true;
    }

    // request start
    public boolean function onRequestStart(String targetPage){
        // Process ColdBox Request
        application.cbBootstrap.onRequestStart( arguments.targetPage );

        return true;
    }

    public void function onSessionStart(){
        application.cbBootStrap.onSessionStart();
    }

    public void function onSessionEnd( struct sessionScope, struct appScope ){
        arguments.appScope.cbBootStrap.onSessionEnd( argumentCollection=arguments );
    }

    public boolean function onMissingTemplate( template ){
        return application.cbBootstrap.onMissingTemplate( argumentCollection=arguments );
    }

}

Inheritance

Application.cfc
component extends="coldbox.system.Bootstrap"{

    // Application properties
    this.name = hash( getCurrentTemplatePath() );
    this.sessionManagement = true;
    this.sessionTimeout = createTimeSpan(0,0,30,0);
    this.setClientCookies = true;

    // COLDBOX STATIC PROPERTY, DO NOT CHANGE UNLESS THIS IS NOT THE ROOT OF YOUR COLDBOX APP
    COLDBOX_APP_ROOT_PATH = getDirectoryFromPath( getCurrentTemplatePath() );
    // The web server mapping to this application. Used for remote purposes or static purposes
    COLDBOX_APP_MAPPING   = "";
    // COLDBOX PROPERTIES
    COLDBOX_CONFIG_FILE      = "";
    // COLDBOX APPLICATION KEY OVERRIDE
    COLDBOX_APP_KEY          = "";
}

Directives

You can set some variables in the Application.cfc that can alter Bootstrapping conditions:

Variable

Default

Description

COLDBOX_APP_ROOT_PATH

App Directory

Automatically set for you. This path tells the framework what is the base root location of your application and where it should start looking for all the agreed upon conventions. You usualy will never change this, but you can.

COLDBOX_APP_MAPPING

/

The application mapping is ESSENTIAL when dealing with Flex or Remote (SOAP) applications. This is the location of the application from the root of the web root. So if your app is at the root, leave this setting blank. If your application is embedded in a sub-folder like MyApp, then this setting will be auto-calculated to /MyApp.

COLDBOX_CONFIG_FILE

config/ColdBox.cfc

The absolute or relative path to the configuration CFC file to load. This bypasses the conventions and uses the configuration file of your choice.

COLDBOX_APP_KEY

cbController

The name of the key the framework will store the application controller under in the application scope.

Lock Timeout

The Boostrapper also leverages a default locking timeout of 30 seconds when doing loading operations. You can modify this timeout by calling the setLockTimeout() method on the Bootsrapper object.

application.bootstrapper.setLockTimeout( 10 );

Layouts & Views

ColdBox provides you with a very simple but flexible and powerful layout manager and content renderer. You no longer need to create module tags or convoluted broken up HTML anymore. You can concentrate on the big picture and create as many layouts as your application needs. Then you can programmatically change rendering schemas (or skinning) and also create composite or component based views.

In this section we will explore the different rendering mechanisms that ColdBox offers and also how to utilize them. As you know, event handlers are our controller layer in ColdBox and we will explore how these objects can interact with the user in order to render content, whether HTML, JSON, XML or any type of rendering data.

Please note that you can use ColdBox as a pure API solution with modern JavaScript frameworks for the front end like VueJS, Reactor, Angular, etc.

Conventions

Let's do a recap of our conventions for layouts and view locations:

+ application
  + layouts
  + views

ColdBox Renderer

It is imperative to know who does the rendering in ColdBox and that is the Renderer class that you can see from our diagram above. As you can tell from the diagram, it includes your layouts and/or views into itself in order to render out content. So by this association and inheritance all layouts and views have some variables and methods at their disposal since they get absorbed into the object. You can visit the API docs to learn about all the Renderer methods.

The Renderer is a transient object in order to avoid variable collisions, meaning it is recreated on each request. All of the following property members exist in all layouts and views rendered by the Renderer:

Property

Description

event

A reference to the Request Context object

rc

A reference to the request collection inside of the request context (For convenience)

prc

A reference to the private request collection inside of the request context (For convenience)

html

A reference to the that can help you build interactive and safe HTML

cacheBox

A reference to the framework factory (coldbox.system.cache.CacheFactory)

controller

A reference to the application's ColdBox Controller (coldbox.system.web.Controller)

flash

A reference to the current configured Flash Object Implementation that inherits from the AbstractFlashScope (derived coldbox.system.web.flash.AbstractFlashScope)

logbox

The reference to the library (coldbox.system.logging.LogBox)

log

A pre-configured LogBox object for this specific class object (coldbox.system.logging.Logger)

wirebox

A reference to the object factory (coldbox.system.ioc.Injector)

As you can see, all views and layouts have direct reference to the request collections so it makes it incredibly easy to get and put data into it. Also, remember that the Renderer inherits from the Framework SuperType so all methods are at your disposal if needed.

Injecting In Your Models

You can also use the ColdBox Renderer in your models so you can render email templates, views, etc. We won't inject the renderer directly because remember that the renderer is a transient object. So we will use a WireBox feature called provider, which will inject a proxy placeholder that looks like the renderer and behaves like the renderer. But behinds the scene it takes care of the persistence. So you can just use it!

component{
    
    property name="renderer" inject="provider:coldbox:renderer";
    
    function renderSomething(){
        return renderer.renderView( view="mail/mymail", args={} );
    }
}

Application Router

Every ColdBox application has a URL router and can be located by convention at config/Router.cfc. This is called the application router and it is based on the router core class: coldbox.system.web.routing.Router. Here is where you will configure router settings and define routes using our routing DSL.

Please see the latest API Docs for investigating all the methods and properties of the Router.

Tip: Unlike previous versions of ColdBox, the new routing services in ColdBox 5 are automatically configured to detect the base URLs and support multi-domain hosting. There is no more need to tell the Router about your base URL.

Application Router - Router.cfc

config/Router.cfc
// Inherits from Router and FrameworkSuperType
component{


    function configure(){
        // Turn on Full URL Rewrites, no index.cfm in the URL
        setFullRewrites( true );
        
        // Routing by Convention
        route( "/:handler/:action?" ).end();
        
    }
    
}

The application router is a simple CFC that virtually inherits from the core ColdBox Router class and is configured via the configure() method. It will be decorated with all the capabilities to work with any request much like any event handler or interceptor. In this router you will be doing 1 of 2 things:

  1. Configuring the Router

  2. Adding Routes via the Routing DSL

Router as an Interceptor

The router and indeed all module routers are also registered as full fledged ColdBox interceptors. So they can listen to any event within your application.

Generated Settings

Once the routing service loads your Router it will create two application settings for you:

  • SESBaseURL : The multi-domain URL base URL of your application: http://localhost

  • HTMLBaseURL : The same path as SESBaseURLbut without any index.cfm in it (Just in case you are using index.cfm rewrite). This is a setting used most likely by the HTML <base> tag.

Configuration Methods

You can use the following methods to fine tune the configuration and operation of the routing services:

Method

Description

setEnabled( boolean )

Enable/Disable routing, enabled by default

setFullRewrites( boolean )

If true, then no index.cfm will be used in the URLs. If false, then /index.cfm/ will be added to all generated URLs. Default is false.

setUniqueURLS( boolean )

Enables SES only URL's with permanent redirects for non-ses urls. Default is true. If true and a URL is detected with ? or & then the application will do a 301 Permanent Redirect and try to translate the URL to a valid SES URL.

setBaseURL( string )

The base URL to use for URL writing and relocations. This is automatically detected by ColdBox 5 e.g. , ''

setLooseMatching( boolean )

By default URL pattern matching starts at the beginning of the URL, however, you can choose loose matching so it searches anywhere in the URL. Default is false.

setExtensionDetection( boolean )

By default ColdBox detects URL extensions like json, xml, html, pdf which can allow you to build awesome RESTful web services. Default is true.

setValidExtensions( list )

Tell the interceptor what valid extensions your application can listen to. By default it listens to: json, jsont, xml, cfm, cfml, html, htm, rss, pdf

setThrowOnInvalidExtensions( boolean )

By default ColdBox does not throw an exception when an invalid extension is detected. If true, then the interceptor will throw a 406 Invalid Requested Format Extension: {extension} exception. Default is false.

function configure(){

    setFullRewrites( true );
    setExtensionDetection( true );
    setValidExtensions( "json,cfm,pdf" );

}

The next sections will discus how to register routes for your application.

cachebox
coldbox
conventions
environments
flash
FlashRAM
interceptorSettings
interceptors
layoutSettings
layouts
logbox
modules
moduleSettings
settings
wirebox
http://www.coldbox.org/
http://mysite.com/index.cfm

What's New With 5.4.0

ColdBox 5.4.0 is a minor version update with lots of fixes, improvements, performance enhancements and some nice new features. Below are the major areas of improvement and the full release notes. To update your ColdBox installation just leverage CommandBox:

  • update coldbox

  • update logbox

  • update wirebox

  • update cachebox

Box Namespace

In our initiative to make all Modules sharable between ColdBox, CommandBox and whatever other boxes we make in the future. We created the box alias for injections. In our case any injection with the prefix coldbox can be used as box

RunRoute()

We have created a new internal runner called runRoute() which is similar to runEvent() but it allows you to abstract your events by leveraging named routes. So just like you can create links based on named routes and params, you can execute named routes and params as well internally via runRoute()

/**
 * Executes internal named routes with or without parameters. If the named route is not found or the route has no event to execute then this method will throw an `InvalidArgumentException`.
 * If you need a route from a module then append the module address: `@moduleName` or prefix it like in run event calls `moduleName:routeName` in order to find the right route.
 * The route params will be passed to events as action arguments much how eventArguments work.
 *
 * @name The name of the route
 * @params The parameters of the route to replace
 * @cache Cached the output of the runnable execution, defaults to false. A unique key will be created according to event string + arguments.
 * @cacheTimeout The time in minutes to cache the results
 * @cacheLastAccessTimeout The time in minutes the results will be removed from cache if idle or requested
 * @cacheSuffix The suffix to add into the cache entry for this event rendering
 * @cacheProvider The provider to cache this event rendering in, defaults to 'template'
 * @prePostExempt If true, pre/post handlers will not be fired. Defaults to false
 *
 * @throws InvalidArgumentException
 */
any function runRoute(
	required name,
	struct params={},
	boolean cache=false,
	cacheTimeout="",
	cacheLastAccessTimeout="",
	cacheSuffix="",
	cacheProvider="template",
	boolean prePostExempt=false
)

CacheBox Rewritten

This should have been a major release on its own. The entire CacheBox framework was re-written in script and modernized from top to bottom. We removed all implicit variable access which gave us huge performance boosts and we streamlined all operations with modern techniques. The results are great and our maintenance will be much less in the future. A part from those optimizations we managed to add a few nice items:

  • Adobe 2018 certified

  • New setting resetTimeoutOnAccess which allows you to simulate session scopes on any CacheBox cache. Every time a get() operation is done, that item's timeout will be reset.

  • All cache providers get some multi function goodness: setMulti(), getMulti(), lookupMulti(), clearMulti(),getCachedObjectMetadataMulti()

LogBox Improvements

There are two major improvements we did with LogBox in this release:

1) The file locking operations on file appenders have been streamlined to avoid high i/o operations.

2) The console appender uses an asynchronous streaming technique which makes it extremely efficient and fast.

ColdBox Release Notes

Bugs

  • [COLDBOX-556] - prePostExempt doesn't skip around advices

  • [COLDBOX-755] - Core interceptors in coldbox.cfc do not listen or register custom interception points that are contributed by modules

  • [COLDBOX-761] - invalidEventHandler gets in to an infinite loop when the invalidEventHandler isn't a full event

  • [COLDBOX-766] - ColdBox shutdown errors onApplicationEnd due to lack of application scope

  • [COLDBOX-770] - Use of event.sendFile delivers a file with single quotes in the name

  • [COLDBOX-773] - Remove default defaultValue as it never will throw an exception if missing on requestcontext on getHTTPHeader()

New Features

  • [COLDBOX-765] - Update all elixir methods to match new version

  • [COLDBOX-771] - Add Elixir version 3 path methods

  • [COLDBOX-775] - Added `:` as a delimiter for the route() method when using modules to be consistent with run event

  • [COLDBOX-776] - new runner: runRoute() that allows you to run routes internally with param passing

Improvements

  • [COLDBOX-767] - Change "module already registered" from warn to debug

  • [COLDBOX-774] - Introduce generic "box" namespace for Wirebox injections

CacheBox Release Notes

Bugs

  • [CACHEBOX-46] - CacheboxProvider metadata and stores: use CFML functions on java hash maps breaks concurrency

  • [CACHEBOX-50] - getOrSet() provider method doesn't work with full null support

  • [CACHEBOX-52] - getQuiet(), clearQuiet(), getSize(), clearAll(), expireAll() broken in acf providers

New Features

  • [CACHEBOX-48] - New setting: `resetTimeoutOnAccess` to allow the ability to reset timeout access for entries

  • [CACHEBOX-49] - Global script conversion and code optimizations

  • [CACHEBOX-53] - lookup operations on ACF providers updated to leverage cacheIdExists() improves operation by over 50x

  • [CACHEBOX-54] - setMulti(), getMulti(), lookupMulti(), clearMulti(),getCachedObjectMetadataMulti() is now available on all cache providers

Improvements

  • [CACHEBOX-47] - Increased timeout on `getOrSet` lock

  • [CACHEBOX-51] - Consolidated usages of the abstract cache provider to all providers to avoid redundancy

WireBox Release Notes

Bugs

  • [WIREBOX-82] - builder.toVirtualInheritance(): scoping issues

  • [WIREBOX-83] - When using sandbox security, and using a provider DSL the file existence checks blow up

LogBox Release Notes

New Features

  • [LOGBOX-34] - Console appender completely rewritten to support asynchronous streaming

Improvements

  • [LOGBOX-33] - Improve file exists usage on file appenders to avoid i/o operations

ColdBox

The ColdBox directive is where you configure the framework for operation.

Application Setup

coldbox = {
    // The name of the application
    appName   = "My App",
    // The name of the incoming URL/FORM/REMOTE variable that tells the framework what event to execute. Ex: index.cfm?event=users.list
    eventName = "event"
};

Info : Please note that there are no mandatory settings as of ColdBox 4.2.0. If fact, you can remove the config file completely and your app will run. It will be impossible to reinit the app however without a reinit password set.

Development Settings

coldbox = {
    reinitPassword = "h1cker",
    handlersIndexAutoReload = true
};

reinitPassword

Protect the reinitialization of the framework URL actions. For security, if this setting is omitted, we will create a random password. Setting it to an empty string will allow you to reinitialize without a password. Always have a password set for public-facing sites.

// reinit with no password
http://localhost/?fwreinit=1
// reinit with password
http://localhost/?fwreinit=mypass

handlersIndexAutoReload

Will scan the conventions directory for new handler CFCs on each request if activated. Use false for production, this is only a development true setting.

Implicit Event Settings

coldbox={
    //Implicit Events
    defaultEvent  = "Main.index",
    requestStartHandler     = "Main.onRequestStart",
    requestEndHandler   = "Main.onRequestEnd",
    applicationStartHandler = "Main.onAppInit",
    applicationEndHandler = "Main.onAppEnd",
    sessionStartHandler = "Main.onSessionStart",
    sessionEndHandler = "Main.onSessionEnd",
    missingTemplateHandler = "Main.onMissingTemplate"
}

These settings map 1-1 from ColdBox events to the Application.cfc life-cycle methods. The only one that is not is the defaultEvent, which selects what event the framework will execute when no incoming event is detected via URL/FORM or REMOTE executions.

Extension Points

The ColdBox extension points are a great way to create federated applications that can reuse a centralized core instead of the local conventions. It is also a great way to extend some core classes with your own.

coldbox={
    //Extension Points
    applicationHelper             = "includes/helpers/ApplicationHelper.cfm",
    viewsHelper                    = "",
    modulesExternalLocation        = [],
    viewsExternalLocation        = "",
    layoutsExternalLocation     = "",
    handlersExternalLocation      = "",
    requestContextDecorator     = "",
    controllerDecorator         = ""
}

applicationHelper

A list or array of absolute or relative paths to a UDF helper file. The framework will load all the methods found in this helper file globally. Meaning it will be injected in ALL handlers, layouts and views.

viewsHelper

A list or array of absolute or relative paths to a UDF helper file. The framework will load all the methods found in this helper in layouts and views only.

modulesExternalLocation

A list or array of locations of where ColdBox should look for modules to load into your application. The path can be a cf mapping or cfinclude compatible location. Modules are searched and loaded in the order of the declared locations. The first location ColdBox will search for modules is the conventions folder modules

viewsExternalLocation

The CF include path of where to look for secondary views for your application. Secondary views look just like normal views except the framework looks in the conventions folder first and if not found then searches this location.

layoutsExternalLocation

The CF include path of where to look for secondary layouts for your application. Secondary layouts look just like normal layouts except the framework looks in the conventions folder first and if not found then searches this location.

handlersExternalLocation

The CF dot notation path of where to look for secondary events for your application. Secondary events look just like normal events except the framework looks in the conventions folder first and if not found then searches this location.

requestContextDecorator

The CF dot notation path of the CFC that will decorate the system Request Context object.

controllerDecorator

The CF dot notation path of the CFC that will decorate the system Controller

Exception Handling

coldbox = {
    // Error/Exception Handling handler
    exceptionHandler        = "",
    // Invalid HTTP method Handler
    invalidHTTPMethodHandler = "",
    // The handler to execute on invalid events
    invalidEventHandler = "",
    // The default error template    
    customErrorTemplate     = "/coldbox/system/includes/BugReport-Public.cfm"
}

exceptionHandler

The event handler to call whenever ANY non-catched exception occurs anywhere in the request lifecycle execution. Before this event is fired, the framework will log the error and place the exception in the prc as prc.exception.

invalidHTTPMethodHandler

The event handler to call whenever a route or event is accessed with an invalid HTTP method.

invalidEventHandler

This is the event handler that will fire masking a non-existent event that gets requested. This is a great place to place 302 or 404 redirects whenever non-existent events are being requested.

customErrorTemplate

The relative path from the application's root level of where the custom error template exists. This template receives a key in the private request collection called exception that contains the exception. By default ColdBox does not show robust exceptions, you can turn on robust exceptions by choosing the following template:

coldbox.customErrorTemplate = "/coldbox/system/includes/BugReport.cfm";

Application Aspects

coldbox = {
    // Persist handlers
    handlerCaching             = false,
    // Activate event caching
    eventCaching            = true,
    // Activate view  caching
    viewCaching              = true,
    // Return RC struct on Flex/Soap Calls
    proxyReturnCollection     = false,
    // Activate implicit views
    implicitViews           = true,
    // Case for implicit views
    caseSensitiveImplicitViews = true,
    // Auto register all model objects in the `models` folder into WireBox
    autoMapModels     = true
}

handlerCaching

This is useful to be set to false in development and true in production. This tells the framework to cache your event handler objects as singletons.

eventCaching

This directive tells ColdBox that when events are executed they will be inspected for caching metadata. This does not mean that ALL events WILL be cached if this setting is turned on. It just activates the inspection mechanisms for whenever you annotate events for caching or using the runEvent() caching methods.

viewCaching

This directive tells ColdBox that when views are rendered, the cache=true parameter will be obeyed. Turning on this setting will not cause any views to be cached unless you are also passing in the caching parameters to your renderView() or event.setView() calls.

proxyReturnCollection

This is a boolean setting used when calling the ColdBox proxy's process() method from a Flex or SOAP/REST call. If this setting is set to true, the proxy will return back to the remote call the entire request collection structure ALWAYS! If set to false, it will return, whatever the event handler returned back. Our best practice is to always have this false and return appropriate data back.

implicitViews

Allows you to use implicit views in your application and view dispatching. You can get a performance boost if you disable this setting.

caseSensitiveImplicitViews

By default implicit views are case sensitive since ColdBox version 5.2.0, before this version the default was false.

autoMapModels

ColdBox by convention can talk to, use and inject models from the models folder by just using their name. However, once you built complex hierarchies you would have to specify the specific hierarchy or folder structure for the model; This is a limitation of conventions. Your choices before v5.2.0 was to map the objects yourself in the WireBox binder. Now, if you turn this setting to true, we will issue a mapDirectory() on the models directory for you.

Views

Views are HTML content that can be rendered inside of a layout or by themselves. They can be either rendered on demand or by being set by an event handler. Views can also produce any type of content apart from HTML like JSON/XML/WDDX via our view renderer that we will discover also. So get ready for some rendering goodness!

Setting Views For Rendering

Usually, event handlers are the objects in charge of setting views for rendering. However, ANY object that has access to the request context object can do this also. This is done by using the setView() method in the request context object.

Setting a view does not mean that it gets rendered immediately. It means that it is deposited in the request context. The framework will later on in the execution process pick those variables up and do the actual rendering. To do immediate rendering you will use the inline rendering methods describe later on.

handlers/main.cfc
component
{

    function index(event,rc,prc){
        // call some model for data and put into the request collection
        prc.myQuery = getInstance('MyService').getData();
        // set the view for rendering
        event.setView( "general/index" );

    }

}

We use the setView() method to set the view views/general/index.cfm to be rendered. Now the cool thing about this, is that we can override the view to be rendered anytime during the flow of the request. So the last process to execute the setView() method is the one that counts. Also notice a few things:

  • No .cfm extension is needed.

  • You can traverse directories by using / like normal cfinclude notation.

  • The view can exist in the conventions directory views or in your configured external locations

  • You did not specify a layout for the view, so the application's default layout (main.cfm) will be used.

It is best practice that view locations should simulate the event. So if the event is general.index, there should be a general folder in the root views folder with a view called index.cfm.

Let's look at the view code:

main/index.cfm
<cfoutput>
<h1>My Cool Data</h1>
#html.table( data=prc.myQuery, class="table table-striped table-hover" )#

</cfoutput>

I am using our cool HTML Helper class that is smart enough to render tables, data, HTML 5 elements etc and even bind to ColdFusion ORM entities.

Views With No Layout

So what happens if I DO NOT want the view to be rendered within a layout? Am I doomed? Of course not, just use the same method with the noLayout argument or event.noLayout() method:

component{

    function index(event,rc,prc){
        // call some model for data and put into the request collection
        prc.myQuery = getInstance('MyService').getData();
        // set the view for rendering
        event.setView( view="general/index", noLayout=true );
    }

    function index(event,rc,prc){
        // call some model for data and put into the request collection
        prc.myQuery = getInstance('MyService').getData();
        // set the view for rendering
        event.setView( "general/index" ).noLayout();
    }
}

Views With Layouts

If you need the view to be rendered in a specific layout, then use the layout argument or the setLayout() method:

component name="general"{

    function index(event,rc,prc){
        // call some model for data and put into the request collection
        prc.myQuery = getInstance('MyService').getData();
        // set the view for rendering
        event.setView( view="general/index", layout="Ajax" );
    }

    function index(event,rc,prc){
        // call some model for data and put into the request collection
        prc.myQuery = getInstance('MyService').getData();
        // set the view for rendering
        event.setView( "general/index" ).setLayout( "Ajax" );
    }

}

Views From Modules

If you need the set a view to be rendered from a specific ColdBox Module then use the module argument alongside any other argument combination:

component name="general"{

    function index(event,rc,prc){

        // call some model for data and put into the request collection
        prc.myQuery = getInstance('MyService').getData();
        // set the view for rendering
        event.setView( view="general/index", module="shared-views" );

    }

}

Render Nothing

You can also tell the renderer to not render back anything to the user by using the event.noRender() method. Maybe you just took some input and need to gracefully shutdown the request into the infamous white screen of death.

component name="general"{

    function saveData(event,rc,prc){
        // do your work here …..

        // set for no render
        event.noRender();
    }

}

Implicit Views

You can also omit the explicit event.setView() if you want, ColdBox will then look for the view according to the executing event's syntax by convention. So if the incoming event is called general.index and no view is explicitly defined in your handler, ColdBox will look for a view in the general folder called index.cfm. That is why we recommend trying to match event resolution to view resolution even if you use or not implicit views.

Tip: This feature is more for conventions purists than anything else. However, we do recommend as best practice to use explicitly declare the view to be rendered when working with team environments as everybody will know what happens.

component name="general"{

    function index(event,rc,prc){
        // call some model for data and put into the request collection
        prc.myQuery = getInstance('MyService').getData();    
    }

}

Caution If using implicit views, please note that the name of the view will ALWAYS be in lower case. So please be aware of this limitation. I would suggest creating URL Mappings with explicit event declarations so case and location can be controlled. When using implicit views you will also loose fine rendering control.

Disabling Implicit Views

You can also disable implicit views by using the coldbox.implicitViews configuration setting in your config/ColdBox.cfc. This is useful as implicit lookups are time-consuming.

coldbox.implicitViews = false;

Case Sensitivity

The ColdBox rendering engine can also be tweaked to use case-insensitive or sensitive implicit views by using the coldbox.caseSensitiveImplicitViews directive in your config/ColdBox.cfc. The default is to turn all implicit views to lower case, so the value is always false.

coldbox.caseSensitiveImplicitViews = true;
HTML Helper
CacheBox
AbstractFlashScope
LogBox
Logger
WireBox

Request Context

On every request to a ColdBox event, the framework creates an object that models the incoming request. This object is called the Request Context Object(coldbox.system.web.context.RequestContext), it contains the incoming FORM/REMOTE/URL variables the client sent in and the object lives in the ColdFusion request scope.

Please visit the latest API Docs for further information about the request context.

This object contains two structures internally:

  1. RC - The Request Collection which contains the FORM/REMOTE/URL data merged into a single structure. This is considered to be unsafe data as it comes from any request.

  2. PRC - The Private Request Collection which is a structure that can be used to safely store sensitive data. This structure cannot be modified from the outside world.

The order of preference of variables when merged is FORM first then REMOTE then URL.

REMOTE variables are from leveraging the ColdBox Proxy.

You can enable coldbox.jsonPayloadToRC = true in your coldbox config if you want to merge variables from a JSON request body in the RC. See release notes 5.1.0 and 5.1.2 for details.

You will use these objects in the controller and view layer of your application to get/set values, get metadata about the request, generate URLs, transform data for RESTful requests, and so much more. It is the glue that binds the controller and view layer together. As we progress in the guides, you will progress in mastering the request context.

RC/PRC Data Super Highway

Note that there is no model layer in the diagram. This is by design; the model will receive data from the handlers/interceptors directly.

Most Commonly Used Methods

Below you can see a listing of the most commonly used methods in the request context object. Please note that when interacting with a collection you usually have an equal private collection method.

  • buildLink() : Build a link in SES or non SES mode for you with tons of nice abstractions.

  • clearCollection() : Clears the entire collection

  • collectionAppend() : Append a collection overwriting or not

  • getCollection() : Get a reference to the collection

  • getEventName() : The event name in use in the application (e.g. do, event, fa)

  • getSelf() : Returns index.cfm?event=

  • getValue() : get a value

  • getTrimValue() : get a value trimmed

  • isProxyRequest() : flag if the request is an incoming proxy request

  • isSES() : flag if ses is turned on

  • isAjax() : Is this request ajax based or not

  • noRender(boolean) : flag that tells the framework to not render any html, just process and silently stop.

  • overrideEvent() : Override the event in the collection

  • paramValue(): param a value in the collection

  • removeValue() : remove a value

  • setValue() : set a value

  • setLayout() : Set the layout to use for this request

  • setView() : Used to set a view to render

  • valueExists() : Checks if a value exists in the collection.

  • renderData() : Marshall data to JSON, JSONP, XML, WDDX, PDF, HTML, etc.

Some Samples:

//Get a reference
<cfset var rc = event.getCollection()>

//test if this is an MVC request or a remote request
<cfif event.isProxyRequest()>
  <cfset event.setValue('message', 'We are in proxy mode right now')>
</cfif>

//param a variable called page
<cfset event.paramValue('page',1)>
//then just use it
<cfset event.setValue('link','index.cfm?page=#rc.page#')>

//get a value with a default value
<cfset event.setvalue('link','index.cfm?page=#event.getValue('page',1)#')>

//Set the view to render
<cfset event.setView('homepage')>

//Set the view to render with no layout
<cfset event.setView('homepage',true)>

//set the view to render with caching stuff
<cfset event.setview(name='homepage',cache='true',cacheTimeout='30')>

//override a layout
<cfset event.setLayout('Layout.Ajax')>

//check if a value does not exists
<cfif not event.valueExists('username')>

</cfif>

//Tell the framework to stop processing gracefully, no renderings
<cfset event.noRender()>

//Build a link
<form action="#event.buildLink('user.save')#" method="post">
</form>

Please see the online API Docs for the latest methods and arguments.

Request Metadata Methods

  • getCurrentAction() : Get the current execution action (method)

  • getCurrentEvent() : Get's the current incoming event, full syntax.

  • getCurrentHandler() : Get the handler or handler/package path.

  • getCurrentLayout() : Get the current set layout for the view to render.

  • getCurrentView() : Get the current set view

  • getCurrentModule() : The name of the current executing module

  • getCurrentRoutedNamespace() : The current routed URL mapping namespace if found.

  • getCurrentRoutedURL() : The current routed URL if matched.

  • getDebugpanelFlag() : Get's the boolean flag if the ColdBox debugger panel will be rendered.

  • getDefaultLayout() : Get the name of the default layout.

  • getDefaultView() : Get the name of the default view.

Event Handlers

Event handlers are ColdBox's version of controllers in the MVC design pattern. So every time you hear "event handler", you are talking about a controller that can listen to external events or internal events in ColdBox. Event handlers are responsible for controlling your application flow, calling business logic, preparing a display to a user and much more.

Locations

All your handlers will be stored in the handlers folder of your application template. If you get to the point where your application needs even more decoupling and separation, please consider building ColdBox Modules instead.

Tip: You can create packages or sub-folders inside of the handlers directory. This is encouraged on large applications so you can organize or package handlers logically to facilitate better maintenance and URL experience.

External Location

You can also declare a HandlersExternalLocation directive in your Configuration CFC. This will be a dot notation path or instantiation path where more external event handlers can be found.

coldbox.handlersExternalLocation  = "shared.myapp.handlers";

If an external event handler has the same name as an internal conventions event, the internal conventions event will take precedence.

Development Settings

By default, ColdBox will only scan for event handlers on startup. For development we highly encourage you leverage the following configuration directives:

// Rescan handlers on each request, turn OFF in production please
coldbox.handlersIndexAutoReload = true;
// Deactivate singleton caching of the handlers, turn ON in production pleaese
coldbox.handlerCaching = false;

Anatomy

Event handlers are CFCs that will respond to FORM posts, HTTP requests and/or remote requests (like Flex,Air, SOAP, REST) via an incoming RC variable called event or by URL mappings (Which we saw in the previous section).

Components

Main.cfc
component extends="coldbox.system.EventHandler"{

    /**
     * Default Action
     */
    function index( event, rc, prc ){
        prc.message = "Hello From ColdBox";
        event.setView( "main/index");
    }

    /**
     * Action returning complex data, converted to JSON automatically by ColdBox
     */
    function data( event, rc, prc ){
        var data = getInstance( "MyModel" ).getArray();
        return data; 
    }

}

You can also remove the inheritance from the CFC and WireBox will extend the coldbox.system.EventHandler for you using Virtual Inheritance.

Event Handlers are treated as singletons by ColdBox, so make sure you make them thread-safe and properly scoped. Persistence is controlled by the coldbox.handlerCaching directive

Actions

They are composed of functions which are called actions that will always have the following signature:

function name( event, rc, prc )

Each action receives three arguments:

  1. event - An object that models and is used to work with the current request

  2. rc - A struct that contains both URL/FORM variables (unsafe data)

  3. prc - A secondary struct that is private. This structure is only accessible from within your application (safe data)

An action will usually do one of the following:

  • Set a view/layout to render

  • Return HTML

  • Return Complex Data which is converted to JSON by default

  • Relocate to another event/URL Route

The default action for all event handlers is called index(). This means that when you execute an event, you can omit the index if you so desire.

component extends="coldbox.system.EventHandler"{

    function index( event, rc, prc ){
        return "<h1> Hi from handler land!</h1>";
    }

    function save( event, rc, prc ){
        getInstance( "MyService" ).save( rc );
        relocate( "users/list" );
    }

    function myData( event, rc, prc ){
        return ['coldbox', 'wirebox', 'cachebox', 'logbox'];
    }
}

Private Actions

So what about private functions? Private functions are not executable from the outside world, but can be executed internally via a function available to all handlers called runEvent(), which we will explore later.

Composed Properties

It is important to note that there is a pre-defined object model behind every event handler controller that will enable you to do your work more efficiently. The following are the automatically generated properties every event handler makes available in their variables scope:)

Event Handler UML
  • cachebox : A reference to the CacheBox library (coldbox.system.cache.CacheFactory)

  • controller : A reference to the Application Controller (coldbox.system.web.Controller)

  • flash: A flash memory object (coldbox.system.web.flash.AbstractFlashScope)

  • logbox: A reference to the application LogBox (coldbox.system.logging.LogBox)

  • log: A pre-configured logging logger object (coldbox.system.logging.Logger)

  • wirebox : A reference to the application WireBox Injector (coldbox.system.ioc.Injector)

  • $super: A reference to the virtual super class if using non-inheritance approach.

Event Caching

Event caching is extremely useful and easy to use. ColdBox will act like a cache proxy between your events and the clients requesting the events, much like squid, nginx or HA Proxy. All you need to do is add several metadata arguments to the action methods and the framework will cache the output of the event in the template cache provider in CacheBox. In other words, the event executes and produces output that the framework then caches. Subsequent calls to the same event with the same incoming RC variables will not do any processing, but just output the content back to the user.

For example, you have an event called blog.showEntry. This event executes, gets an entry from the database and sets a view to be rendered. The framework then renders the view and if event caching is turned on for this event, the framework will cache the HTML produced. So the next incoming show entry event will just spit out the cached HTML. The cache key is created by hashing the incoming request collection.

Important to note also, that any combination of URL/FORM parameters on an event will produce a unique cacheable key. So event=blog.showEntry&id=1 & event=blog.showEntry&id=2 are two different cacheable events.

Enabling Event Caching

To enable event caching, you will need to set a setting in your ColdBox.cfc called coldbox.eventcaching to true.

Important Enabling event caching does not mean that ALL events will be cached. It just means that you enable this feature.

Setting Up Actions For Caching

The way to set up an event for caching is on the function declaration with the following annotations:

Important Please be aware that you should not cache output with 0 timeouts (forever). Always use a timeout.

Alert: DO NOT cache events as unlimited timeouts. Also, all events can have an unlimited amount of permutations, so make sure they expire and you purge them constantly. Every event + URL/FORM variable combination will produce a new cacheable entry.

Storage

All event and view caching are stored in a named cache called template which all ColdBox applications have by default. You can open or create a new configuration object and decide where the storage is, timeouts, providers, etc. You have complete control of how event and view caching is stored.

Purging

We also have a great way to purge these events programmatically via our cache provider interface.

Methods for event purging:

  • clearEvent( string eventSnippet, string querystring="" ): Clears all the event permutations from the cache according to snippet and querystring. Be careful when using incomplete event name with query strings as partial event names are not guaranteed to match with query string permutations

  • clearEventMulti( eventsnippets,string querystring="" ): Clears all the event permutations from the cache according to the list of snippets and querystrings. Be careful when using incomplete event name with query strings as partial event names are not guaranteed to match with query string permutations

  • clearAllEvents( [boolean async=true] ) : Can clear ALL cached events in one shot and can be run asynchronously.

this.event_cache_suffix

You can now leverage the cache suffix property in handlers to be declared as a closure so it can be evaluated at runtime so it can add dynamic suffixes to cache keys. This can allow you to incorporate elements into the cache key at runtime instead of statically. This is a great way to incorporate the user's language locale or session identifier to make unique entries.

OnRequestCapture - Influence Cache Keys

We have provided an interception point in ColdBox that allows you to add variables into the request collection before a snapshot is made so you can influence the cache key of a cacheable event. What this means is that you can use it to mix in variables into the request collection that can make this event cache unique for a user, a specific language, country, etc. This is a great way to leverage event caching on multi-lingual or session based sites.

With the simple example above, the user's locale will be addded to all your event caching permutations and thus create entries for different languages.

Life-Cycle Caveats

Several event interception points are NOT available during event caching.

When using event caching the framework will NOT execute ANY event at all. It will stream the content directly from the selected cache provider. This means that any interceptors or code that executes in the event is also NOT executed. The only interception points that will execute are:

  • preProcess

  • postProcess

So please make sure you take note of this when planning for event security.

Monitoring

has an intuitive and powerful monitor that can be used via the ColdBox Debugger Module. From the monitor you can purge, expire and view cache elements, etc.

My First ColdBox Application

CommandBox comes with a coldbox create app command that can enable you to create application skeletons using one of our official skeletons or :

  • Advanced : A tag based advanced template

  • AdvancedScript (default): A script based advanced template

  • elixir : A ColdBox Elixir based template

  • ElixirBower : A ColdBox Elixir + Bower based template

  • ElixirVueJS : A ColdBox Elixir + Vue.js based template

  • rest: A RESTFul services template

  • rest-hmvc: A RESTFul service built with modules

  • Simple : A traditional simple template

  • SuperSimple : The bare-bones template

You can find all our template skeletons here:

Scaffolding Our Application

So let's create our first app using the default template skeleton AdvancedScript:

This will scaffold the application and also install ColdBox for you. The following folders/files are generated for you:

Now let's start a server so we can see our application running:

This will start up a 5 open source CFML engine (If you are in CommandBox 4). If you would like an Adobe ColdFusion server then just add to the command: cfengine=adobe@{version} where {version} can be: 2016,11,10,9.

If you are using CommandBox 3 and below, you will be using a Lucee 4.5 Server.

This command will start a server with URL rewrites enabled, open a web browser for you and execute the index.cfmwhich in turn executes the default event by convention in a ColdBox application: main.index.

Tip: ColdBox Events map to handlers (cfc) and appropriate actions (functions)

That's it, you have just created your first application. Hooray, onward!

Tip: Type coldbox create app help to get help on all the options for creating ColdBox applications.

File/Folder Conventions

ColdBox is a conventions based framework. The location of files and functions matter. Since we scaffolded our first application, let's write down in a table below with the different conventions that exist in ColdBox.

What is the common denominator in all the conventions? That they are all optional.

Re-initializing The Application

There will be times when you make configuration or code changes that are not reflected immediately in the application due to caching. You can tell the framework to _reinit _or restart the application for you via the URL by leveraging the special URL variable fwreinit.

You can also use CommandBox to reinit the application:

Tip: You can add a password to the reinit procedures for further security, please see the .

 coldbox.eventCaching = true;

Annotation

Type

Description

cache

boolean

A true or false will let the framework know whether to cache this event or not. The default is FALSE. So setting to false makes no sense

cachetimeout

numeric

The timeout of the event's output in minutes. This is an optional attribute and if it is not used, the framework defaults to the default object timeout in the cache settings. You can place a 0 in order to tell the framework to cache the event's output for the entire application timeout controlled by coldfusion, NOT GOOD. Always set a decent timeout for content.

cacheLastAccesstimeout

numeric

The last access timeout of the event's output in minutes. This is an optional attribute and if it is not used, the framework defaults to the default last access object timeout in the cache settings. This tells the framework that if the object has not been accessed in X amount of minutes, then purge it.

cacheProvider

string

The cache provider to store the results in. By default it uses the template cache.

// In Script
function showEntry(event,rc,prc) cache="true" cacheTimeout="30" cacheLastAccessTimeout="15"{
    //get Entry
    prc.entry = getEntryService().getEntry(event.getValue('entryID',0));

    //set view
    event.setView('blog/showEntry');
}
templateCache = cachebox.getCache( "template" );
//Trigger to purge all Events
getCache( "template" ).clearAllEvents();

//Trigger to purge all events synchronously
getCache( "template" ).clearAllEvents(async=false);

//Purge all events from the blog handler
getCache( "template" ).clearEvent('blog');

//Purge all permutations of the blog.dspBlog event
getCache( "template" ).clearEvent('blog.dspBlog');

//Purge the blog.dspBlog event with entry of 12345
getCache( "template" ).clearEvent('blog.dspBlog','id=12345')
this.EVENT_CACHE_SUFFIX = function( eventHandlerBean ){
  return "a localized string, etc";
};
component{

    onRequestCapture(event,interceptData){
        var rc = event.getCollection();

        // Add user's locale to the request collection to influenze event caching
        rc._user_locale = getFWLocale();
    }

}
box install cbdebugger
CacheBox
CacheBox
coldbox create app MyApp
+coldbox // The ColdBox framework library (CommandBox Tracked)
+config // Configuration files
+handlers // Your handlers/controllers
+includes // static assets
+interceptors // global interceptors
+layouts // Your layouts
+models // Your Models
+modules // CommandBox Tracked Modules
+modules_app // Custom modules
+tests // Test harness
+views // Your Views
+Application.cfc // Bootstrap
+box.json // CommandBox package descriptor
+index.cfm // Front controller
server start --rewritesEnable

File/Folder Convention

Mandatory

Description

config/Coldbox.cfc

false

The application configuration file

config/Router.cfc

false

The application URL router

handlers

false

Event Handlers (controllers)

layouts

false

Layouts

models

false

Model objects

modules

false

CommandBox Tracked Modules

modules_app

false

Custom Modules You Write

views

false

Views

http://localhost:{port}/?fwreinit=1
coldbox reinit
your own
github.com/coldbox-templates
Lucee
configuration section

Model Integration

We have a complete section dedicated to the Model Layer, but we wanted to review a little here since event handlers need to talk to the model layer all the time. By default, you can interact with your models from your event handlers in two ways:

  • Dependency Injection (Aggregation)

  • Request, use and discard model objects (Association)

ColdBox offers its own dependency injection framework, WireBox, which allows you, by convention, to talk to your model objects. However, ColdBox also allows you to connect to third-party dependency injection frameworks via the IOC module: http://forgebox.io/view/cbioc

Aggregation differs from ordinary composition in that it does not imply ownership. In composition, when the owning object is destroyed, so are the contained objects. - wikipedia

Dependency Injection

Your event handlers can be autowired with dependencies from WireBox by convention. By autowiring dependencies into event handlers, they will become part of the life span of the event handlers (singletons), since their references will be injected into the handler's variables scope. This is a huge performance benefit since event handlers are wired with all necessary dependencies upon creation instead of requesting dependencies (usage) at runtime. We encourage you to use injection whenever possible.

Warning As a rule of thumb, inject only singletons into singletons. If not you can create unnecessary scope-widening injection issues and memory leaks.

You will achieve this in your handlers via property injection, which is the concept of defining properties in the component with a special annotation called inject, which tells WireBox what reference to retrieve via the WireBox Injection DSL. Let's say we have a users handler that needs to talk to a model called UserService. Here is the directory layout so we can see the conventions

Directory Layout
+ handlers
  + users.cfc
+ models
  + UserService.cfc

Here is the event handler code to leverage the injection:

users.cfc
component name="MyHandler"{
    
    // Dependency injection of the model: UserService -> variables.userService
    property name="userService" inject="UserService";

    function index( event, rc, prc ){
        prc.data = userService.list()
        event.setView( "users/index" );
    }

}

Notice that we define a cfproperty with a name and inject attribute. The name becomes the name of the variable in the variables scope and the inject annotation tells WireBox what to retrieve. By default it retrieves model objects by name and path.

Tip: The injection DSL is vast and elegant. Please refer to it. Also note that you can create object aliases and references in your config binder: config/WireBox.cfc

Requesting Model Objects

The other approach to integrating with model objects is to request and use them as associations via the framework super type method: getInstance(), which in turn delegates to WireBox's getInstance() method. We would recommend requesting objects if they are transient (have state) objects or stored in some other volatile storage scope (session, request, application, cache, etc). Retrieving of objects is okay, but if you will be dealing with mostly singleton objects or objects that are created only once, you will gain much more performance by using injection.

users.cfc
component{

    function index( event, rc, prc ){
        // Request to use the user service, this would be best to inject instead 
        // of requesting it.
        prc.data = getInstance( "UserService" ).list();
        event.setView( "users/index" );
    }
    
    function save( event, rc, prc ){
        // request a user transient object, populate it and save it.
        prc.oUser = populateModel( getInstance( "User" ) );
        userService.save( prc.oUser );
        relocate( "users/index" );
    }

}

Association defines a relationship between classes of objects that allows one object instance to cause another to perform an action on its behalf. - 'wikipedia'

A practical example

In this practical example we will see how to integrate with our model layer via WireBox, injections, and also requesting the objects. Let's say that we have a service object we have built called FunkyService.cfc and by convention we will place it in our applications models folder.

Directory Layout
 + application
  + models
     + FunkyService.cfc

FunkyService.cfc

component singleton{

    function init(){
        return this;
    }

    function add(a,b){ 
        return a+b; 
    }

    function getFunkyData(){
        var data = [
            {name="Luis", age="33"},
            {name="Jim", age="99"},
            {name="Alex", age="1"},
            {name="Joe", age="23"}
        ];
        return data;
    }

}

Our funky service is not that funky after all, but it is simple. How do we interact with it? Let's build a Funky event handler and work with it.

Injection

component{

    // Injection via property
    property name="funkyService" inject="FunkyService";

    function index(event,rc,prc){

        prc.data = funkyService.getFunkyData();

        event.renderData( data=prc.data, type="xml" );
    }    


}

By convention, I can create a property and annotate it with an inject attribute. ColdBox will look for that model object by the given name in the models folder, create it, persist it, wire it, and return it. If you execute it, you will get something like this:

<array>
    <item>
        <struct>
            <name>Luis</name>
            <age>33</age>
        </struct>
    </item>
    <item>
        <struct>
            <name>Jim</name>
            <age>99</age>
        </struct>
    </item>
    <item>
        <struct>
            <name>Alex</name>
            <age>1</age>
        </struct>
    </item>
    <item>
        <struct>
            <name>Joe</name>
            <age>23</age>
        </struct>
    </item>
</array>

Great! Just like that we can interact with our model layer without worrying about creating the objects, persisting them, and even wiring them. Those are all the benefits that dependency injection and model integration bring to the table.

Alternative wiring

You can use the value of the inject annotation in several ways. Below is our recommendation.

// Injection using the DSL by default name/id lookup
property name="funkyService" inject="FunkyService";
// Injection using the DSL id namespace
property name="funkyService" inject="id:FunkyService";
// Injection using the DSL model namespace
property name="funkyService" inject="model:FunkyService";

Requesting

Let's look at the requesting approach. We can either use the following approaches:

Via Facade Method

component{

    function index(event,rc,prc){

        prc.data = getInstance( "FunkyService" ).getFunkyData();

        event.renderData( data=prc.data, type="xml" );
    }    


}

Directly via WireBox:

component{

    function index(event,rc,prc){

        prc.data = wirebox.getInstance( "FunkyService" ).getFunkyData();

        event.renderData( data=prc.data, type="xml" );
    }    


}

Both approaches do exactly the same thing. In reality getInstance() does a wirebox.getInstance() callback (Uncle Bob), but it is a facade method that is easier to remember. If you run this, you will also see that it works and everything is fine and dandy. However, the biggest difference between injection and usage can be seen with some practical math:

1000 Requests made to users.index

- Injection: 1000 handler calls + 1 model creation and wiring call = 1001 calls
- Requesting: 1000 handler calls + 1000 model retrieval + 1 model creation call = 2002 calls

As you can see, the best performance is due to injection as the handler object was wired and ready to roll, while the requested approach needed the dependency to be requested. Again, there are cases where you need to request objects such as transient or volatile stored objects.

Routing Methods

Apart from routing by convention, you can also register your own expressive routes. Let's investigate the routing approaches.

Inline Terminator

The route() method allows you to register a pattern and immediately assign it to execute an event or a response via the target argument.

The first pattern registers and if matched it will execute the wiki.page event. The second pattern if matched it will execute the profile.show event from the users module and register the route with the userprofile name.

Inline Responses

You can also pass in a closure or lambda to the target argument and it will be treated as an inline action:

You can also pass just an HTML string with {rc_var} replacements for the routed variables place in the request collection

Routing to Events

If you will not use the inline terminators you can do a full expressive route definition to events using the to() method, which allows you to concatenate the route pattern with modifiers:

Routing To Handler Action Combinations

You can also route to a handler and an action using the modifiers instead of the to() method. This long-form is usually done for visibility or dynamic writing of routes. You can use the following methods:

  • withHandler()

  • withAction()

  • toHandler()

  • end()

Routing to Views

You can also route to views and view/layout combinations by using the toView() terminator:

Routing to Redirects

You can also use the toRedirect() method to re-route patterns to other patterns.

The default status code for redirects are 301 redirects which are PERMANENT redirects.

Routing to Handlers

You can also redirect a pattern to a handler using the toHandler() method. This is usually done if you have the action coming in via the URL or you are using RESTFul actions.

Routing to RESTFul Actions

You can also route a pattern to HTTP RESTFul actions. This means that you can split the routing pattern according to incoming HTTP Verb. You will use a modifier withAction() and then assign it to a handler via the toHandler() method.

Routing to Responses

The Router allows you to create inline responses via closures/lambdas or enhanced strings to incoming URL patterns. You do not need to create handler/actions, you can put the actions inline as responses.

If you use a response closure/lambda, they each accepts three arguments:

  1. event - An object that models and is used to work with the current request (Request Context)

  2. rc - A struct that contains both URL/FORM variables merged together (unsafe data)

  3. prc - A secondary struct that is private only settable from within your application (safe data)

If the response is an HTML string, then you can do {rc_var} replacements on the strings as well:

Sub-Domain Routing

You can also register routes that will respond to sub-domains and even capture portions of the sub-domain for multi-tenant applications or SaaS applications. You will do this using the withDomain() method.

You can leverage the full routing DSL as long as you add the withDomain() call with the domain you want to bind the route to. Also note that the domain string can contain placeholders which will be translated to RC variables for you if matched.

Tip: Please note that you can leverage as well for domains

Adding Variables to RC/PRC

You can also add variables to the RC and PRC structs on a per-route basis by leveraging the following methods:

  • rc( name, value, overwrite=true ) - Add an RC value if the route matched

  • rcAppend map, overwrite=true ) - Add multiple values to the RC collection if the route matched

  • prc( name, value, overwrite=true ) - Add an PRC value if the route matched

  • prcAppend map, overwrite=true ) - Add multiple values to the PRC collection if the route matched

This is a great way to manually set variables in the incoming structures:

Routing Conditions

You can also apply runtime conditions to a route in order for it to be matched. This means that if the route matches the URL pattern then we will execute a closure/lambda to make sure that it meets the runtime conditions. We will do this with the withCondition() method.

Let's say you only want to fire some routes if they are using Firefox, or a user is logged in, or whatever.

route( "/wiki:pagename", "wiki.page" );
route( 
    pattern="/users/:id/profile", 
    target="users:profile.show", 
    name="userprofile" 
);
route(
    pattern="/echo",
    target=function( event, rc, prc ){
        return "hello ColdBox!";
    }
);

route(
    pattern="/users",
    target=function( event, rc, prc ){
        return getInstance( "UserService" ).list();
    }
);
route(
    "/echo/:name",
    "<h1>Hello {name} how are you today!</h1>"
);
route( "/wiki/:pagename" )
    .to( "wiki.show" );

route( "/users/:id/profile" )
    .as( "userProfile" )
    .to( "users:profile.show" );

route( "/users/:id/profile" )
    .as( "userProfile" )
    .withVerbs( "GET" )
    .withSSL()
    .header( "cache", false )
    .prc( "isActive", true )
    .to( "users:profile.show" );
route( "wiki/:pagename" )
    as( "wikipage" )
    withAction( "show" )
    toHandler( "wiki" );

route( "wiki/:pagename" )
    .withHander( "wiki" )
    .withAction( "show" )
    .end();
route( "/contact-us" )
    .toView( 
        view = "view name",
        layout = "layout",
        nolayout = false,
        viewModule = "moduleName",
        layoutModule = "moduleName"
    );
route( "/my-old/link" )
    .toRedirect( target="/new/pattern", statusCode=301 );
// Action comes via the URL
route( "/users/:action" )
    .toHandler( "users" );
// RESTFul actions
route( "/users/:id?" )
    .withAction( {
        GET : "index",
        POST : "save",
        PUT : "update",
        DELETE : "remove"
    } )
    .toHandler( "users" );
/**
 * Setup a response for a URL pattern
 * @body A closure/lambda or enhanced HTML string
 * @statusCode The status code to send
 * @statusText The status code to send
 */
function toResponse( 
    required body, 
    numeric statusCode = 200, 
    statusText = "Ok" 
)
// Simple response routing
route( "/users/hello", function( event, rc, prc ){
    return "<h1>Hello From RESTLand</h1>";
} );

// Simple response routing with placeholders
route( "/users/:username", function( event, rc, prc ){
    "<h1>Hello #encodeForHTML( rc.username )# From RESTLand</h1>";
} );

// Routing with the toResponse() method
route( "/users/:id" )
    .toResponse( function( event, rc, prc ){
        var oUser = getInstance( "UserService" ).get( rc.id ?: 0 );
        if( oUser.isLoaded() ){
            return oUser.getMemento();
        }
        event.setHTTPHeader( statusCode = 400, statusText = "Invalid User ID provided" );
        return {
            "error" : true,
            "messages" : "Invalid User ID Provided"
        };
    } );
// Routing with enhanced HTML strings
route( "/users/:id" )
    .toResponse(
        "<h1>Welcome back user: {id} how are you today!</h1>"
    );
route( "/" )
  .withDomain( "subdomain-routing.dev" )
  .to( "subdomain.index" );

route( "/" )
  .withDomain( ":username.forgebox.dev" )
  .to( "subdomain.show" );
route( "/api/v1/users/:id" )
    .rcAppend( { secured : true } )
    .prcAppend( { name : "hello" } )
    .to( "api-v1:users.show" );
route( "/go/firefox" )
  withCondition( function( requestString ){
    return ( findnocase( "Firefox", cgi.HTTP_USER_AGENT ) ? true : false );
  });
  .to( "firefox.index" );
Routing Groups

Getting Started Guide

The ColdBox HMVC Platform is the de-facto enterprise-level HMVC framework for CFML developers. It's professionally backed, highly extensible, and productive. Getting started with ColdBox is quick and painless. The only thing you need to begin is CommandBox, a command line tool for CFML developers.

This is a one-page introductory guide to ColdBox. If you are new to MVC or ColdBox, you can also leverage our 60 minute quick start guide as well.

IDE Tools

ColdBox has the following supported IDE Tools:

  • Sublime - https://packagecontrol.io/packages/ColdBox Platform

  • VSCode - https://marketplace.visualstudio.com/items?itemName=ortus-solutions.vscode-coldbox

  • CFBuilder - https://www.forgebox.io/view/ColdBox-Platform-Utilities

Install CommandBox

You can read through our one-page CommandBox Getting Started Guide. Or simply grab the CommandBox executable from the download page and double click it to run.

http://www.ortussolutions.com/products/commandbox

You should now be seeing a prompt that looks like this:

CommandBox Shell

Create A New Site

Now we're cooking with gas! Let's create a new ColdBox application. CommandBox comes with built-in commands for scaffolding out new sites as well as installing ColdBox and other libraries. We'll start by changing into an empty directory were we want our new app to live. If necessary, you can create a new folder.

CommandBox> mkdir playground --cd

Now let's ask CommandBox to create a new ColdBox app for us.

CommandBox> coldbox create app MyPlayground

Tip: You can find many scaffolding templates for ColdBox in our Github organization: github.com/coldbox-templates

You can also issue a coldbox create app help command and get help for the creation command.

File/Folder Conventions

This command will place several new folders and files in your working directory. Let's run the ls command to view them.

CommandBox> ls

Here's a rundown of the important bits.

  • coldbox - This is the ColdBox framework managed by CommandBox

  • config/Coldbox.cfc - Your application configuration object

  • config/Router.cfc - Your application URL Router

  • handlers - Your controller layer, which in ColdBox they are called event handlers

  • layouts - Your HTML layouts

  • models - This holds your model CFCs

  • modules - This holds the CommandBox tracked modules

  • modules_app - This holds your app's modules

  • views - Your HTML views will go here

Start It Up

Now that our shiny new MVC app is ready to go, let's fire it up using the embedded server built into CommandBox. You don't need any other software installed on your PC for this to work. CommandBox has it all!

CommandBox> start --rewritesEnable

In a few seconds, a browser window will appear with your running application. This is a full server with access to the web administrator where you can add data sources, mappings, or adjust the server settings. Notice the handy icon added to your system tray as well. The --rewritesEnable flag will turn on some basic URL rewriting so we have nice, pretty URLs.

Tip: If you are creating an app to run on any other server than the commandbox server, you will need to manually set up URL rewriting. More info here: /the-basics/routing/requirements

Default App Template

Take A Look Around

ColdBox uses easy conventions to define the controllers and views in your app. Let's open up our main app controller in your default editor to have a looksie.

CommandBox> edit handlers/main.cfc

At the top, you'll see a function named "index". This represents the default action that runs for this controller, which in ColdBox land they are referred to as event handlers.

// Default Action
function index(event,rc,prc){
    prc.welcomeMessage = "Welcome to ColdBox!";
    event.setView("main/index");
}

Now let's take a look in the main/index view. It's located int he views folder.

CommandBox> edit views/main/index.cfm

This line of code near the top of the view is what outputs the prc.welcomeMessage variable we set in the controller.

<h1>#prc.welcomeMessage#</h1>

Try changing the value being set in the handler and refresh your browser to see the change.

prc.welcomeMessage = "This is my new welcome message";

Building On

Let's define a new event handler now. Your controllers act as event handlers to respond to requests, REST API, or remote proxies.

Pull up CommandBox again and run this command.

CommandBox> coldbox create handler helloWorld index,add,edit,list

That's it! You don't need to add any special configuration to declare your handler. Now we have a new handler called helloWorld with actions index, add, edit, and list. The command also created a test case for our handler as well as stubbed-out views for each of the actions.

Now, let's re-initialize the framework to pick up our new handler by typing ?fwreinit=1 at the end of the URL.

Let's hit this new controller we created with a URL like so. Your port number will probably be different.

127.0.0.1:43272/helloWorld

Normally the URL would have index.cfm before the /helloWorld bit, but our --rewritesEnable flag when we started the server makes this nicer URL possible.

Install Packages

ColdBox's MVC is simple, but it's true power comes from the wide selection of modules you can install into your app to get additional functionality. You can checkout the full list of modules available on the Forgebox directory: www.forgebox.io.

forgebox.io/type/modules

Here's some useful examples:

  • BCrypt -- Industry-standard password hashing

  • cbdebugger -- For debugging Coldbox apps

  • cbjavaloader - For interacting with Java classes and libraries

  • cbMarkdown - For writing in markdown

  • cbMessagebox -- Display nice error/success messages

  • cborm -- Awesome ORM Services

  • cb18n -- For multilingual sites

  • cbt - ColdBox templating language

  • cbValidation - Back-end validation framework

  • qb - Fluent query builder and schema builder

  • route-visualizer - For visualizing your application routes

Install cbmessagebox from the CommandBox prompt like this:

CommandBox> install cbmessagebox

We can see the full list of packages by using the list command.

CommandBox> list
Dependency Hierarchy for myApp (0.0.0)
+-- cbmessagebox (1.0.0)
+-- coldbox (4.0.0)

Right now we can see that our app depends on coldbox and cbmessagebox to run. We'll use our new cbmessagebox module in a few minutes. But first, we'll create a simple Model CFC to round out our MVC app.

Creating A Model

Models encapsulate the business logic your application. They can be services, beans, or DAOs. We'll use CommandBox to create a GreeterService in our new app with a sayHello method.

CommandBox> coldbox create model GreeterService sayHello --open

Tip: The --open is a nice shortcut that opens our new model in our default editor after creating it.

Let's finish implementing the sayHello() method by adding this return statement and save the file.

We can also add the word singleton to the component declaration. This will tell WireBox to only create one instance of our service.

component singleton {

    function sayHello(){
        return 'Hey, you sexy thing!';
    }

}

What is WireBox?

WireBox is a dependency injection framework that is included with ColdBox. It will manage all object creations, persistence and assembling. You don't have to worry about using new or createobject() for CFCs anymore.

Tie It All Together

Ok, let's open up that helloWorld handler we created a while back. Remember, you can hit tab while typing to auto-complete your file names.

CommandBox> edit handlers/helloWorld.cfc

We'll inject our greeterService and the cbmessagebox service into the handler by adding these properties to the top of /handlers/helloWorld.cfc.

What is this magical injection? Injection is a way to get references of other objects placed in the variables scope of other objects. This makes your life easier as you don't have to be creating objects manually or even knowing where they exist.

This will put the instance of our services in the variables scope where we can access it in our action methods.

component {

    property name='greeterService' inject='greeterService';
    property name='messageBox' inject='@cbmessagebox';

    ...
}

And now in our index method, we'll set the output of our service into an info message.

function index( event, rc, prc ){
    messageBox.info( greeterService.sayHello() );
    event.setView( "helloWorld/index" );
}

One final piece. Open up the default layout located in layouts/Main.cfm and find the #renderView()#. Add this line right before it to render out the message box that we set in our handler.

#getInstance( 'messagebox@cbmessageBox').renderIt()#
<div class="container">#renderView()#</div>

Now hit your helloWorld handler one final time with ?fwreinit=1 in the URL to see it all in action! (Again, your port number will most likely be different.

127.0.0.1:43272/helloWorld?fwreinit=1

What's Next?

Congratulations! In a matter of minutes, you have created a full MVC application. You installed a community module from ForgeBox, created a new handler/view and tied in business logic from a service model.

As easy as that was, you're just scratching the surface of what ColdBox can do for you. Continue reading this book to learn more about:

  • Environment-specific configuration

  • Easy SES URL routing

  • Tons of 3rd party modules

  • Drop-in security system

  • Sweet REST web service support

Getting Help

If you run into issues or just have questions, please jump on our ColdBox Google Group and our Slack team and ask away.

ColdBox is Professional Open Source under the Apache 2.0 license. We'd love to have your help with the product.

Rendering Data

Handler actions can return data back to its callers in different formats

  • Complex Data

  • HTML

  • Rendered Data via event.renderData()

Complex Data

By default, any complex data returned from handler actions will automatically be marshaled to JSON by default:

Simple as that. ColdBox detects the complex object and tries to convert it to JSON for you automatically.

renderdata Action Annotation

If you want ColdBox to marshall the content to another type like XML or PDF. Then you can use the renderdata annotation on the action itself. The renderdata annotation can be any of the following values:

  • json

  • jsonp

  • jsont

  • xml

  • html

  • text

  • pdf

renderdata Component Annotation

You can also add the renderData annotation to the component definition and this will override the default of JSON. So if you want XML as the default, you can do this:

$renderData Convention

If the returned complex data is an object and it contains a function called $renderData(), then ColdBox will call it for you automatically. So instead of marshaling to JSON automatically, your object decides how to marshal itself.

Native HTML

By default if your handlers return simple values, then they will be treated as returning HTML.

event.renderData()

Using the renderdata() method of the event object is the most flexible for RESTFul web services or pure data marshaling. Out of the box ColdBox can marshall data (structs, queries, arrays, complex or even ORM entities) into the following output formats:

  • XML

  • JSON

  • JSONP

  • JSONT

  • HTML

  • TEXT

  • PDF

  • WDDX

  • CUSTOM

Here is the method signature:

Below are a few simple examples:

As you can see, it is very easy to render data back to the browser or caller. You can even choose plain and send HTML back if you wanted to.

Render PDFs

You can also render out PDFs from ColdBox using the render data method. The data argument can be either the full binary of the PDF or simple values to be rendered out as a PDF; like views, layouts, strings, etc.

There is also a pdfArgs argument in the render data method that can take in a structure of name-value pairs that will be used in the cfdocument () tag when generating the PDF. This is a great way to pass in arguments to really control the way PDF's are generated uniformly.

Renderdata With Formats

The renderData() method also has two powerful arguments: formats & formatsView. If you currently have code like this:

Where you need to param the incoming format extension, then do a switch and do some code for marshalling data into several formats. Well, no more, you can use our formats argument and ColdBox will marshall and code all that nasty stuff for you:

That's it! ColdBox will figure out how to deal with all the passed in formats for you that renderdata can use. By convention it will use the name of the incoming event as the view that will be rendered for HTML and PDF; implicit views. If the event was users.list, then the view would be views/users/list.cfm. However, you can tell us which view you like if it is named different:

If you need to redirect for html events, you can pass any arguments you normally would pass to setNextEvent to formatsRedirect.

Custom Data Conversion

You can do custom data conversion by convention when marshalling CFCs. If you pass in a CFC as the data argument and that CFC has a method called $renderdata(), then the marshalling utility will call that function for you instead of using the internal marshalling utilities. You can pass in the custom content type for encoding as well:

The CFC converter:

In this approach your $renderdata() function can be much more customizable than our internal serializers. Just remember to use the right contentType argument so the browser knows what to do with it.

function showData( event, rc, prc ){
    prc.data = service.getUsers();
    return prc.data;
}
function usersAsXML( event, rc, prc ) renderdata=xml{
    prc.data = service.getUsers();
    return prc.data;
}

function usersAsPDF( event, rc, prc ) renderdata=pdf{
    prc.data = service.getUsers();
    return prc.data;
}
component renderdata="xml"{

}
function showUser( event, rc, prc ){
    return service.getUser( 2 );
}
function index(event,rc,prc){
    return "<h1>Hello from my handler today at :#now()#</h1>";
}

function myData( event, rc, prc ){
     prc.mydata = myservice.getData();
     return renderView( "main/myData" );
}
/**
* Use this method to tell the framework to render data for you. The framework will take care of marshalling the data for you
* @type The type of data to render. Valid types are JSON, JSONP, JSONT, XML, WDDX, PLAIN/HTML, TEXT, PDF. The deafult is HTML or PLAIN. If an invalid type is sent in, this method will throw an error
* @data The data you would like to marshall and return by the framework
* @contentType The content type of the data. This will be used in the cfcontent tag: text/html, text/plain, text/xml, text/json, etc. The default value is text/html. However, if you choose JSON this method will choose application/json, if you choose WDDX or XML this method will choose text/xml for you.
* @encoding The default character encoding to use.  The default encoding is utf-8
* @statusCode The HTTP status code to send to the browser. Defaults to 200
* @statusText Explains the HTTP status code sent to the browser.
* @location Optional argument used to set the HTTP Location header
* @jsonCallback Only needed when using JSONP, this is the callback to add to the JSON packet
* @jsonQueryFormat JSON Only: This parameter can be a Boolean value that specifies how to serialize ColdFusion queries or a string with possible values "row", "column", or "struct".
* @jsonAsText If set to false, defaults content mime-type to application/json, else will change encoding to plain/text
* @xmlColumnList XML Only: Choose which columns to inspect, by default it uses all the columns in the query, if using a query
* @xmlUseCDATA XML Only: Use CDATA content for ALL values. The default is false
* @xmlListDelimiter XML Only: The delimiter in the list. Comma by default
* @xmlRootName XML Only: The name of the initial root element of the XML packet
* @pdfArgs All the PDF arguments to pass along to the CFDocument tag.
* @formats The formats list or array that ColdBox should respond to using the passed in data argument. You can pass any of the valid types (JSON,JSONP,JSONT,XML,WDDX,PLAIN,HTML,TEXT,PDF). For PDF and HTML we will try to render the view by convention based on the incoming event
* @formatsView The view that should be used for rendering HTML/PLAIN/PDF. By default ColdBox uses the name of the event as an implicit view
* @formatsRedirect The arguments that should be passed to relcoate as part of a redirect for the HTML action.  If the format is HTML and this struct is not empty, ColdBox will call relcoate with these arguments.
* @isBinary Bit that determines if the data being set for rendering is binary or not.
*/
function renderData(
	type="HTML",
	required data,
	contentType="",
	encoding="utf-8",
	numeric statusCode=200,
	statusText="",
	location="",
	jsonCallback="",
 	jsonQueryFormat="true",
	boolean jsonAsText=false,
	xmlColumnList="",
	boolean xmlUseCDATA=false,
	xmlListDelimiter=",",
	xmlRootName="",
	struct pdfArgs={},
	formats="",
	formatsView="",
	formatsRedirect={},
	boolean isBinary=false
){
// html marshalling
function renderHTML(event,rc,prc){
    event.renderData( data="<h1>My HTML</h1>" );
}
// xml marshalling
function getUsersXML(event,rc,prc){
    var qUsers = getUserService().getUsers();
    event.renderData( type="XML", data=qUsers );
}
//json marshalling
function getUsersJSON(event,rc,prc){
    var qUsers = getUserService().getUsers();
    event.renderData( type="json", data=qUsers, statusCode=403 );
}
// from binary
function pdf(event,rc,prc){
  var binary = fileReadAsBinary( file.path );
  event.renderData( data=binary, type="PDF" );
}

// from content
function pdf(event,rc,prc){
  event.renderData( data=renderView("views/page"), type="PDF" );
}
// from content and with pdfArgs
function pdf(event,rc,prc){
  var pdfArgs = { bookmark = "yes", backgroundVisible = "yes", orientation="landscape" };
  event.renderData(data=renderView("views/page"), type="PDF", pdfArgs=pdfArgs);
}
event.paramValue("format", "html");

switch( rc.format ){
    case "json" : case "jsonp" : case "xml" : {
        event.renderData(data=mydata, type=rc.format);
        break;
    } 
    case "pdf" : {
        event.renderData(data=renderView("even/action"), type="pdf");
        break;
    }
    case "html" : {
        event.setView( "event/action" );
        break;
      }
};
event.renderData( data=MyData, formats="xml,json,html,pdf" );
event.renderData( data=MyData, formats="xml,json,html,pdf", formatsView="data/MyView" );
event.renderData( data=MyData, formats="xml,json,html,pdf", formatsRedirect={event="Main.index"} );
// get an instance of your custom converter
myConverter = getInstance("MyConverter")
// put some data in it
myConverter.setData( data );
// marshall it out according to your conversions and the content type it supports
event.renderData( data= myConverter, contentType=myConverter.getContentType() );
component accessors="true"{

    property name="data" type="mytype";
    property name="contentType";

    function init(){ 
        setContentType("text");
        return this; 
    }

    // The magical rendering
    function $renderdata(){
        var d = {
            n = data.getName(),
            a = data.getAge(),
            c = data.getCoo(),
            today = now()
        };

        return d.toString();
    }

}
See docs

What's New With 5.0.0

ColdBox 5.0.0 is a major release for the ColdBox MVC platform. It has been long standing as we have been learning so much especially around containerization and portability. This release has over 70 key issues addressed from new features, improvements and bug fixes. So let's begin our ColdBox 5 adventure.

Global Versioning

All internal libraries now have a standard version according to the major ColdBox release of 5.0.0. Further releases of WireBox, CacheBox and LogBox will adhere to the unified version.

Engine Deprecation

It is also yet another source code reduction due to the dropping of support for the following CFML Engines:

  • Adobe ColdFusion 9

  • Adobe ColdFusion 10

That's right, you will need Adobe ColdFusion 11+ or Lucee 4.5+ in order to work with ColdBox 5.

Automation

We have fully automated all build processes with ColdBox 5 to include CommandBox and TestBox testing, Travis integration and a fully automated test suite that executes against ALL supported CFML engines. Our code coverage has increased dramatically due to this work. We discovered engine bugs that must have plagued our users for years. YAY for testing!

Performance Improvements & Optimizations

As we update core files we keep optimizing the source code and migrating to full cfscript. This migration has allowed us to optimize very old code into modern times with significant performance gains. We have also moved from internal Java reflection to get file information to native CFML functions since now all engines support it. Just this alone has improved vanilla requests tremendously.

WireBox object creation and manipulation has also increased due to new locking strategies in our Mixer Util. You will especially see the difference when creating many transient objects.

Lucee Full Null Support

Thanks to the community we have now full null support for the Lucee CFML engine and up-coming Adobe 2018.

Core Framework Exception Handling

The core framework has been revised with a fine tooth comb to provide better exception messages, better helpful messages and also the ability to intercept exceptions at the framework level via normal exception handlers. You will also see that ColdBox can detect response headers now and make sure it can avoid caching exceptions when event caching is turned on. The appropriate status code will now be reported.

You will also find in the log files attempts to reinit the framework with invalid or missing passwords.

Containers + Environments Support

ColdBox introduces two new methods that are available for your ColdBox.cfc and your ModuleConfig.cfc objects:

  • getSystemProperty( name, defaultValue ) - Retrieve a Java System property

  • getSystemSetting( name, defaultValue ) - Discover an environment variable either by searching system properties first and then system environment variables second.

Hint These methods are also found in the ColdBox core utility object: coldbox.system.core.util.Util which can be injected anywhere it is needed.

These methods will allow you to interact with docker environment variables and override settings, add new settings, and much more.

Modules, Modules and more Modules

We continue to innovate in the Hierarchical MVC (HMVC) design of ColdBox by fine-tuning the modular services and interactions. Here are the major updates to modules in ColdBox 5.

  • All module interceptors are now namespaced to avoid name conflicts with other modules.

  • New modules injected variable: coldboxVersion to be able to quickly detect what version of ColdBox they are running under. This will allow you to create modules that can respond to multiple ColdBox versions.

Inherited Entry Point

All modules have had a URL entry point since the beginning: this.entryPoint = "/route". This entry point is registered in the URL mappings to allow for a unique URL pattern to exist for specific modules. That is great! However, in modern times and with the amount of API centric applications that we are building, we wanted to introduce an easier way to build resource centric APIs.

What if our resource URLs could match by convention our module inceptions? Well, with the new inherited entry points, you can do just that. By leveraging HMVC and module inception you can now create automatic URL nesting schemas.

You can turn this on just by adding the following flag in your ModuleConfig.cfc:

this.inheritEntryPoint = true

When the module is registered it will recurse its parent tree and discover all parent entry points and assemble the entry point accordingly. Let's see an example. We are working on an API and we want to create the following URL resource: /api/v1/users. We can leverage HVMC and create the following modular structure:

+ modules_app
  + api
    + modules_app
      + v1 
        + modules_app
          + users

This models our URL resource modularly. We can now go into each of the module's ModuleConfig and add only the appropriate URL pattern and turn on inherit entry point.

api   - this.entryPoint = /api
v1    - this.entryPoint = /v1
users - this.entryPoint = /users

This feature will not only help you identify API routing, but help you build with modularity in mind. You will also find a new method in the request context called getModuleEntryPoint() to retrieve a modules inherited entry point, which is great for URL building.

New Module Events

You can now listen to more events of the module life-cycle:

  • preModuleRegistration - before each module is registered

  • postModuleRegistration - after each module is registered

  • afterModuleRegistrations - This will fire when all modules have been registered. This is great if you want modules to dynamically depend on each other.

  • afterModuleActivations - This will fire when all modules have been succesfully activated. Great for caching updates, announcements, etc.

Modules_app convention for inception

We have now added the modules_app convention for all module inceptions.

Default Model Export Convention

If you create a module where there is a model with the same name, then we will automatically map it in Wirebox as @modulename.

cors = getInstance( "@cors" )

This is great for 1 model modules.

Routing Enhancements

We continue to push forward in making ColdBox the best RESTFul framework for ColdFusion (CFML). In ColdBox 5 we have great new additions for both routing and rendering.

Simplified URL Rewrites

The SES interceptor now has a boolean flag to denote if rewrites are turned on/off and you will no longer set a base URL. We will automatically detect the base URLs according to multi-domain hosting. Meaning you can out of the box create multi-tenant applications with ease. We will also be adding subdomain routing in our final release.

setFullRewrites( true ); // defaults to false.

Named Routes

This has been a long-time coming to ColdBox and I can't believe we had never added this before. Well, named routes are here, finally!

If you have used other frameworks in other languages, they allow you to name your routes so you can build links to them in an easier manner. We have done just the same. The addRoute() method accepts the name parameter and we have extended the request context with some new methods:

  • getCurrentRouteName() - Gives you the name of the current route, if any

  • route() - Allows you to build URLs according to named routes

Define the Route

// Create a named route
addRoute(
  pattern  = "/user/:username/:page",
  name     = "user_detail"
);

// Same for modules
routes = [

  { 
    pattern  = "/detail/:username/:page",
    name     = "user_detail"
  }

];

Build Links

<a href="#event.route( name="user_detail", params={ username="luis", page="1" } )#>User Details</a>

The route method signature can be seen below:

/**
* Builds links to named routes with or without parameters. If the named route is not found, this method will throw an `InvalidArgumentException`.
* If you need a route from a module then append the module address: `@moduleName` in order to find the right route.
* 
* @name The name of the route
* @params The parameters of the route to replace
* @ssl Turn SSL on/off or detect it by default
* 
* @throws InvalidArgumentException
*/
string function route( required name, struct params={}, boolean ssl )

Resourceful Routes

In ColdBox 5, you can register resourceful routes to provide automatic mappings between HTTP verbs and URLs to event handlers and actions by convention. By convention, all resources map to a handler with the same name or they can be customized if needed. This allows for a standardized convention when building routed applications.

You will now have a new resources() method in the SES interceptor or a resources struct in your modules. Yes, all modules can have their own resourceful routes as well.

// Creates all resources that point to a photos event handler by convention
resources( "photos" );

// Register multiple resources either as strings or arrays
resources( "photos,users,contacts" )
resources( [ "photos" , "users", "contacts" ] );

// Register multiple fluently
resources( "photos" )
    .resources( "users" )
    .resources( "contacts" );

// Creates all resources to the event handler of choice instead of convention
resources( route="photos", handler="MyPhotoHandler" );

// All resources in a module
resources( route="photos", handler="photos", module="api" );

// Resources in a ModuleConfig
resources = [
  { resource="photos" },
  { resource="users", handler="user" }
];

This single resource declaration will create all the necessary variations of URL patterns and HTTP Verbs to actions to handle the resource. It will even create named routes for you.

For in-depth usage of the resources() method, let's investigate the API Signature:

/**
* Create all RESTful routes for a resource. It will provide automagic mappings between HTTP verbs and URLs to event handlers and actions.
* By convention, the name of the resource maps to the name of the event handler.
* Example: `resource = photos` Then we will create the following routes:
* - `/photos` : `GET` -> `photos.index` Display a list of photos
* - `/photos/new` : `GET` -> `photos.new` Returns an HTML form for creating a new photo
* - `/photos` : `POST` -> `photos.create` Create a new photo
* - `/photos/:id` : `GET` -> `photos.show` Display a specific photo
* - `/photos/:id/edit` : `GET` -> `photos.edit` Return an HTML form for editing a photo
* - `/photos/:id` : `POST/PUT/PATCH` -> `photos.update` Update a specific photo
* - `/photos/:id` : `DELETE` -> `photos.delete` Delete a specific photo
* 
* @resource         The name of a single resource or a list of resources or an array of resources
* @handler         The handler for the route. Defaults to the resource name.
* @parameterName     The name of the id/parameter for the resource. Defaults to `id`.
* @only             Limit routes created with only this list or array of actions, e.g. "index,show"
* @except             Exclude routes with an except list or array of actions, e.g. "show"
* @module             If passed, the module these resources will be attached to.
* @namespace         If passed, the namespace these resources will be attached to.
*/
function resources(
  required resource,
  handler=arguments.resource,
  parameterName="id",
  only=[],
  except=[],
  string module="",
  string namespace=""
)

Event Execution

We have also done several updates for event executions, event caching and overall MVC operations:

  • You can now return the event object from event handlers and the framework will not fail. It will be ignored.

  • setNextEvent() is now deprecated in favor of a relocate() method.

  • Request context has a getOnly( keys, private=false ) method to allow for retrieval of certain keys only from the public or private request contexts. Great for functional programming.

Event Caching Enhancements

Dynamic Cache Suffix

You can now leverage the cache suffix property in handlers to be declared as a closure so it can be evaluated at runtime instead of a static prefix.

this.EVENT_CACHE_SUFFIX = function( eventHandlerBean ){
  return "a localized string, etc";
};

With this ability you can enable dynamic cache suffixes according to runtime environments on a per user basis.

Cache Provider Annotations

You can now add a cacheProvider annotation to your cache enabled functions and decide to which CacheBox provider the output will be cached too instead of the default provider of template:

function index( event, rc, prc ) cache=true cacheProvider=couchbase{

}

Rendering Enhancements

We have also done tremendous updates to the rendering engines in ColdBox 5, especially for RESTFul responses and content negotiation.

  • ColdBox now detects the rc.format not only to incoming URL extensions, but also via the Accept Header as content-negotiation.

  • New interception point afterRenderInit which will allow you to add your own injections to the main ColdBox renderer and modify the renderer at runtime.

  • The request context can now deliver files to users via our sendFile() method.

Native JSON Responses + Handler Auto-Marshalling

JSON is the native response now for event handlers that return complex variables.

function data( event, rc, prc ){
  return [1,2,3];
}

function data( event, rc, prc ){
  return myservice.getQuery();
}

That's it! If you return a complex variable from an event handler, ColdBox will convert it to JSON for you automatically. We will even inspect the return object and if it has a $renderdata() method, we will call it for you and return your very own marshalled data! But there's more. You can add a renderData annotation to your action and add any valid renderdata() type and it will return it accordingly.

function data( event, rc, prc ) renderdata="xml"{
  return [1,2,3];
}

function data( event, rc, prc ) renderdata="pdf"{
  event.setView( "users/index" );
}

You can even add a default response type by adding the renderdata annotation to the component construct:

component renderdata="json"{

}

Named Regions

We have introduced the concept of named rendering regions with ColdBox 5. As we increase the reuse of views and create many self-sufficient views or widgets, we end up with setView() or renderView() method calls that expose too much logic, location, parameters, etc. With named regions, we can actually abstract or describe how a region should be rendered and then render it when we need it via a name.

Definition

event.setView(
  view = "users/detail",
  args = { data = userData, border = false },
  layout = "main",
  name = "userDetail"
);

event.setView(
  view = "users/banner",
  args = { data = userData },
  name = "userBanner"
);

Rendering

<div id="banner">#renderView( name="userBanner" )#</div>

<div id="detail">#renderView( name="userDetail" )#</div>

Testing Enhancements

Here are some of the major updates for integration testing with ColdBox and TestBox:

  • Reset the response when calling setup() in integration testing to avoid duplicate headers within same request executions.

  • Base test case doesn't allow for inherited annotations. It now does since we moved the testing life-cycle methods to be annotation based instead of by name.

  • Added dynamic methods getRenderData() and getStatusCode() helpers to the request context upon execute() execution. This will allow you a shorthand approach to getting response status codes and response rendering data struct.