In our last installment, we talked about the benefits of converting legacy FuseBox applications over to ColdBox MVC. In this entry we'll actually convert the Fusebox 3 sample app available from fusebox.org to show you the similiarities and differences between the two frameworks.
- The finished ColdBox code is here: https://github.com/bdw429s/Fusebox-3-Sample-App-Conversion/
- The original Fusebox code is in the root of the above repo in a zip called "FB3SimpleApp.zip"
Overview
Here's a screenshot of the files in the Fusebox and ColdBox code side-by-side. Both are pretty self-explanatory, so we'll work our way through the contents of each file as we go.
One of the first things you may notice is many of the Fusebox files have a prefix in their names such as "dsp_". You can do this in ColdBox if you wish, but we prefer to use folders to denote what a file does. For example, any file in the "views" folder is going to be for display purposes. Also, note the fbx_savecontent.cfm file is a custom tag only needed for compatibility on very old versions of ColdFusion so there's no need for an equivalent in ColdBox.
Core Framework
On the left, the "fbx_fusebox30_CF*.cfm" files represent the core Fusebox framework. There are different files for different versions of ColdFusion servers and operating systems. On the right, the "coldbox" folder contains the framework. The easiest place for this folder to live is in your web root so CF will automatically resolve CFC paths that start with "coldbox" there. For the security-minded, you can also move the folder elsewhere and create a server or app-specific mapping that points to it. This is especially handy if you have multiple ColdBox site and want to only maintain one copy of the framework.
Bootstrap
Both Fusebox and ColdBox use the concept of a "front controller". Basically, this means that all requests are directed at index.cfm, and then the framework kicks in and decides what actual code to run based on URL parameters or default settings. The way the Fusebox framework gets loaded is by a cfif statement in the index.cfm file that detects the version of CF and the operating system and includes the appropriate file. The ColdBox app has an index.cfm file, but it is an empty placeholder. ColdBox leverages the application, session, and request lifecycle methods that are part of Application.cfc. If your legacy site uses Application.cfm, you'll need to convert this over.
index.cfm
<cfinclude template="fbx_fusebox30_CF50.cfm">
Application.cfc
component extends="coldbox.system.Coldbox" { this.name = 'FuseBoxSampleConversion'; }
You'll notice that Application.cfc extends the core ColdBox component. This component implements methods such as onApplicationStart() and onRequestStart() to load up the framework and process requests. If you ever need to add methods to Application.cfc, make sure you continue to call the super-scoped method as well. If you want to use an app-specific mapping for "/coldbox", you'll need to use our non-inheritance template. Read more about this here.
Settings
The Fusebox app uses an fbx_Settings.cfm files that contains settings for the app. In ColdBox, we have a "/config" convention folder where we place all our configuration. There are several files that can eventually be put in this folder if you begin using WireBox, LogBox, etc but by default all you need is a CFC called ColdBox.cfc in there. The only requirements of this file is that it contains a method called configure() and defines a variable called coldbox.appName. There are a number of optional settings you can define here as well additional methods for environment control that override settings for you automatically on your development or staging servers. You can read about this here.
fbx_Settings.cfm
<!--- In case no fuseaction was given, I'll set up one to use by default. ---> <cfparam name="attributes.fuseaction" default="home.main"> <!--- useful constants ---> <cfparam name="request.self" default="index.cfm"> <cfparam name="self" default="index.cfm"> <!--- should fusebox silently suppress its own error messages? default is FALSE ---> <cfset fusebox.suppresserrors = false>
config/ColdBox.cfc
component { function configure(){ coldbox = { appName = "SuperSimpleApp", reinitPassword = "", defaultEvent = "home.main" }; } }
The attributes.fuseaction's equivalent is our defaultEvent setting in ColdBox. In FuseBox, home is the circuit and main is the fuseaction. In ColdBox we have the same concept but with a bit different terminology. home is the name of our handler, and main is called the action.
There is no need for the self settings in ColdBox. There is an object called event that you will use to build links which fully encapsulates that information for you.
#event.buildLink( "home.main" )#
There is also no equivalent ColdBox setting for fusebox.suppresserrors as it operates under a bit different paradigm. ColdBox does things by convention, so if a setting or file doesn't exist, it assumes you didn't want to define it and moves on. For files that are absolutely required for the site to run or explicitly referenced by you, we believe there is no value in suppressing a fatal error such as this. What we do offer instead is a nice custom error template and a number of flexible options for dealing with errors that do arise. You can read more about exception handling in ColdBox here. For debugging info,warning, and informational messages, ColdBox uses its LogBox library which allows you to choose how to store those messages (if at all).
You'll notice ColdBox also has a reinitPassword setting that doesn't exist in Fusebox. That is because the Fusebox framework reloads itself on every request but ColdBox avoids that overhead by loading the framework up once when the application inits and caching it for the best performance. In the event you need to make changes that require a reload of ColdBox just add ?fwreinit=1 to the URL. On your production servers you'll want to specify a password to use in the URL so only you can reload your site.
Circuits and Handlers
Like we said above, Fusebox breaks a site up into circuits, and ColdBox breaks it up via handlers. Circuits are divided into fuseactions, and handlers, are divided into actions. Instead of using a switch statement to route the request, ColdBox handlers are defined by convention by a CFC in your handlers directory. The name of the CFC determines the name of the handler. Actions are simply methods in your CFC, and you probably guessed that the names of the methods are the names of the actions! ColdBox also allows for very powerful routes to be defined ad hoc by you to make up nice descriptive URLs that don't have to match your file/method names. Read more about that here.
fbx_Switch.cfm
<cfswitch expression = "#fusebox.fuseaction#"> <cfcase value="main"> <cfinclude template="dsp_main.cfm"> </cfcase> <cfcase value="fusebox.defaultfuseaction"> <!---This will be the value returned if someone types in "circuitname.", omitting the actual fuseaction request---> </cfcase> <cfdefaultcase> <!---This will just display an error message and is useful in catching typos of fuseaction names while developing---> <cfoutput>This is the cfdefaultcase tag. I received a fuseaction called "#attributes.fuseaction#" and I don't know what to do with it.</cfoutput> </cfdefaultcase> </cfswitch>
handlers/home.cfc
component { function main( event, rc, prc ){ // "home/main" view used by convention } // Default action function index( event, rc, prc ){ event.setView( "home/main" ); } function onMissingAction( event, rc, prc ){ throw( "Man you screwed that up" ); } }
The "main" case in Fusebox is the same as our main() method. ColdBox will automatically parse the default event of home.main and execute the main() method in the home handler CFC. Your handler is the place for any logic that is NOT HTML. This would include queries, service calls, defaulting incoming parameters, and data manipulation. In Fusebox you are used to form and url variables being merged into attributes. ColdBox presents these variables in the passed-in struct called rc which stands for "Request Collection". In this sample page there's nothing much to do so our method is empty. Note ColdBox will include the /views/home/main.cfm file for us by convention automatically based on the event name. This can be overridden by calling event.setView().
The fusebox.defaultfuseaction is used if you pass the circuit name via the URL with no fuseaction specified. In ColdBox, the index() method is the equivalent convention for this. Passing just an event of just "home" will run "home.index" by default.
In Fusebox the default case is run if the passed fuseaction is bogus. By default, ColdBox will throw an error that can be handled in several different ways as documented here. However, you can still provide local handling of that error by specifying an optional onMissingAction() method in your handler that will be automatically called for you. ColdBox has a handful of handy "special" method names like that. Read about them here.
Views
Just like Fusebox, ColdBox uses plain CFM files to template out HTML. This is a perfect place for tags to shine! You don't need to manually include your view files though. ColdBox will automatically look for a view file named after your action in a folder named after your handler. Of course, all this happens in our "'/views" convention folder. You are free to name your view files whatever you want though. Just use the following code to tell the framework what view to include:
// Include /views/randomFolder/randomFile.cfm event.setView( "randomFolder/randomFile" );
dsp_main.cfm
This is the main fuseaction page. <img src="minifusebox.gif">
views/home/main.cfm
This is the main view page. <img src="http://www.coldbox.org/includes/images/logos/poweredby.gif">
There's no real difference here other than I updated the text and image. ColdBox views have the same event object, rc, and prc structs available to them that were passed into the handler's method. This is how the handler passes information to the view.
Layouts
fbx_Layouts.cfm
<cfset fusebox.layoutFile = "defaultLayout.cfm"> <cfset fusebox.layoutDir = "">
ColdBox doesn't have a dedicated file for layout settings, instead they are part of the main configuration file located at /config/ColdBox.cfc. To read about all the layout settings available, click here.
The equivalent of fusebox.layoutFile would be layoutSettings.defaultLayout. In my code you'll see I declined to use that setting. By default, ColdBox will look for a layout called main.cfm.
The fusebox.layoutDir settings would be replaced with the conventions.layoutsLocation setting. All the convention folders (views, handlers, models, etc) can be overridden but this is rarely needed so the setting is typically just omitted. The default layout location is the folder "/layouts" which means the default layout will be "/layouts/main.cfm".
defaultLayout.cfm
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Untitled</title> </head> <body> <table> <tr><td>Fuseaction Output</td></tr> <tr><td><cfoutput>#fusebox.layout#</cfoutput></td></tr> </table> </body> </html>
layouts/main.cfm
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Untitled</title> </head> <body> <table> <tr><td>Event Output</td></tr> <tr><td><cfoutput>#renderView()#</cfoutput></td></tr> </table> </body> </html>
These layout files are almost identical. The main difference is Fusebox replaces the #fusebox.layout# variables with the contents of the rendered view and ColdBox uses a method called #renderView()#. ColdBox layouts have access to the same things views have which includes the event object, rc, and prc scopes. Layouts can also call nested layouts with #renderLayout( "layoutName" )# and additional views with #renderView( "viewName" )#. To override the layout for a given action, use event.setLayout( "layoutName" ); in your controller.
Run Spot, Run!
Here is the output of the Fusebox app:
And here's our ColdBox app:
It should also be noted that the "full" URL that doesn't rely on the default settings for the Fusebox app would be:
http://www.example.com/index.cfm?fuseaction=home.main
The ColdBox URL would be:
http://www.example.com/index.cfm?event=home.main
ColdBox also allows for nice SES URLs and web server rewrites which would look like this:
http://www.example.com/home/main
Conclusion
To see all the code, please visit this repo on GitHub: https://github.com/bdw429s/Fusebox-3-Sample-App-Conversion
For what it's worth, the code also runs on Railo, an open source CF engine.
This app is very simple so it just scratches the surface of what ColdBox can do. Hopefully it helps you feel a bit more comfortable with the equivalent conventions that ColdBox offers and you can begin to formulate a plan for converting your legacy site to a newer, more robust platform. If you have questions, please feel free to ask on our mailing list. Our next installment in the series will be converting the sample app for FuseBox 5.1 so stay tuned. And I'll also list these handy resources again from the first post:
- Check out our bite-sized PDF reference cards here: http://wiki.coldbox.org/wiki/Dashboard.cfm#PDF_Ref_Cards
- Play with the framework right inside your browser with these samples: http://runnable.com/ColdBox
- Join our Google Group. We're friendly, active, and love to help with ANY kind of CF question: https://groups.google.com/forum/?hl=en#!forum/coldbox
- Check out our video library of webinars and presentations: http://www.coldbox.org/media
- Look up answers in our comprehensive docs: http://wiki.coldbox.org/
Add Your Comment