Light-Weight DumpModel() Function For CFWheels In ColdFusion
At PAI Industries, we use the CFWheels Framework, which is a port++ of the Ruby-on-Rails web framework for ColdFusion. One of the core features of the framework is that it injects a lot of function definitions into every execution context (ie, within the models, views, and controllers). What this means is that anytime you go to CFDump
a model or a controller, your dump-output is bananas large!
For example, if I look up a single model and output it:
<cfscript>
router = model( "manufacturing.routing.Router" )
.findByKey( 48 )
;
dump( router );
</cfscript>
... the output of this one operation is massive — notice the size of the scrollbar thumb relative to the size of the window:

This CFDump
output is massive because of the 411 Functions that CFWheels is including in the execution context of the Model (ie, as injected instance methods).
In ColdFusion, the CFDump
tag and the corresponding dump()
/ writeDump()
functions have a showUDFs
flag which can be used to hide the included functions:
<cfscript>
router = model( "manufacturing.routing.Router" )
.findByKey( 48 )
;
dump(
var = router,
showUDFs = false // Hide those functions!
);
</cfscript>
In Adobe ColdFusion (ACF), this would be sufficient — ACF will fully omit the User Defined Functions (UDFs) from the writeDump()
output. However, in Lucee CFML, the output still contains the UDFs keys — it merely suppresses the UDF details from being rendered:

This dump()
output is much better than the original output; however, the core instance properties of our model are still getting lost in the sea of injected functions. The UDFs key are now much smaller; but there's still 411 of them that I have to scroll past in order to see my model data.
To get around this Lucee CFML quirk, I created a dumpModel()
UDF. This UDF performs a deep-copy of the given value, but omits function keys during the copy operation. It then passes this lighter-weight copy off to the core dump()
function in CFML:
<cfscript>
/**
* I simplify Wheels Model dumps by omitting all the Functions and flattening the data
* component into structs.
*/
public void function dumpModel(
required any var,
string label = "Model",
string output = "browser"
) {
var normalizedVar = isObject( var )
? dumpModelDataAccessor( var )
: var
;
dump(
label = label,
var = normalizedVar,
output = output
);
}
/**
* I recursively access the properties in the given ColdFusion component.
*/
private struct function dumpModelDataAccessor(
required any target,
numeric depth = 4
) {
var data = {};
// Safe-guard for circular references.
if ( depth <= 0 ) {
return data;
}
loop
collection = target
key = "local.key"
value = "local.value"
{
if ( isCustomFunction( value ) ) {
continue;
}
data[ key ] = isObject( value )
? dumpModelDataAccessor( value, depth - 1 )
: value
;
}
return data;
}
</cfscript>
Notice that the dumpModel()
function is really two functions, the second being the recursive branch of the logic that walks-down chains of nested ColdFusion components. Ultimately, it creates a nested, simplified Struct, which it then hands off to the native dump()
.
Aside: this code only recursively explores CFC instances. It assumes that any other data structures (array and struct) are already in a simplified format. This is specific to how CFWheels works - only CFC instances have the injected Functions.
We can now update our demo code to use this dumpModel()
function:
<cfscript>
router = model( "manufacturing.routing.Router" )
.findByKey( 48 )
;
dumpModel( router );
</cfscript>
Notice that I don't need to CFInclude
any helper functions. This is because CFWheels is automatically making that custom dumpModel()
function available in every execution context — which is exactly the "issue" we're trying to work around within our dump output. And now, when I run this version of the ColdFusion code, we get the following CFDump
output:

As you can see, the dumpModel()
function took the ColdFusion component (CFC) instance, deep-copied it into a vastly simplified Struct, and then output said struct to the browser.
Now, it's all signal and no noise! Noice!
Want to use code from this post? Check out the license.
Reader Comments
This is my standard dump keyboard macro. I am rarely looking at functions.
<cfdump var='#somevar#' showUDFs=false/> <cfabort />
Usually I just dump using our model.getSimpleVariables(), a custom base function that exists at the core of our model inheritance. (We just have our base and baseModel inheritance for 99% of our objects.)
@Will,
Hundo-p, I almost never care about functions. Historically, it's never been a real issue for me since I've never worked with an ORM before, so all of my "things" have just been Structs / Queries. This is the first time I've had a data-access layer return CFCs. The fact that Lucee CFML shows the
<UDF>
tag even when youshowUDFs=false
was surprising to me. Not a bug, per se; but, not what ACF does.@All,
Tom King mentioned to me that models do have a
.properties()
method that will return single-layer of simplified data; but, not a recursive data structure. It's worth noting; and probably a method that I only discovered after I had already went down this road.Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →