Brandon asks:
I have a UDF that gets called a few times each page request. I need this UDF to save the results of each request to an array. Then I need it to output that array to another UDF. Is that possible?
Here is an example
whatever_page.cfm (included on request)
<cfset newButton("first")>
another_page.cfm (also included on request)
<cfset newButton("nextButton")>So you can see that multiple templates are calling this function. I would like this function to then store the results somehow so that they can all be output together in a central location. Is that even possible?
This needs to be done because in my app plugins are installed and try to create buttons. Then once all the templates call the newButton function, another function retrieves that array (or struct, doesn't matter) and loops over it, creating the dynamic buttons.
Is this possible? Of course it. ColdFusion is everything and the kitchen sink! Seriously though - yes, this is possible, and there are a few different ways of doing it. The basic issue is finding a way to store each button so you get it later. As you can probably guess, this involves using one of ColdFusion's built in scopes - but which?
The Request scope is the best choice for this type of functionality. It will only last for the current request and it provides a simple way for multiple files to share the same data. Here is a simple example:
<cfscript>
function newButton(b) {
if(!structKeyExists(request,"buttons")) request.buttons = [];
arrayAppend(request.buttons, b);
}
function getButtons() { return request.buttons; }
</cfscript>
<cfset newButton("Nuke the Zoo!")>
<cfset newButton("Obama 08!")>
<cfset newButton("Bunnies have 2D8 HP")>
<cfdump var="#getButtons()#">
This script has 2 UDFs. One for adding a button to an array, and another for returning the array. The second UDF isn't technically needed. I could have just dumped request.buttons. By the same token the first UDF isn't necessary either. But together they provide a nice, abstract way to store and retrieve the data.
Now you may ask - what would happen if someone else does: request.buttons = "Foo"? That would obviously screw things up pretty bad. In cases where you control the code, as you are here, it isn't such a big deal. If your button display code all of a sudden started breaking because the buttons weren't a proper array, you would have a good idea of what to look for. You mentioned you were building an application with plugins. I'd be willing to bet you will need this functionality in multiple places. Instead of using the root Request scope, it may make sense to work in your own area underneath the request scope. If your app was code named Foo, then perhaps request.foo.buttons would be more sensible. Or request.foo.ui.buttons. You get the idea. You can even abstract this name within another UDF:
function getStore() {
if(!structKeyExists(request,"corestore")) request.corestore = {};
return "corestore";
}
function newButton(b) {
if(!structKeyExists(request[getStore()],"buttons")) request[getStore()].buttons = [];
arrayAppend(request[getStore()].buttons, b);
}
function getButtons() { return request[getStore()].buttons; }
This version is like the first one, except that I've added a getStore() UDF to create a root level key for my data. Notice I create the structure if it doesn't exist, and then I return the name. The other UDFs don't' care about this name. They just store what they need to. You could get even fancier of course but hopefully this gives you an idea on how to solve your problem.