Scorpio Wishlist Item

One of the proper jewels of ColdFusion is the dynamic nature of the language. There is nothing I love more than replacing tons of boilerplate code with a few lines of dynamic CF!

One language feature that could be improved is the ability to dynamically call a method on a component. I find CFInvoke a bit unwieldy, syntactically annoying, and in my humble opinion, unnecessary.

Take the following example:

<cfset UserPreferences = CreateObject("component",User).init() />
<cfset WhichPreference = "Lunch" />
<cfset WhichStyle = "Heavy">

<cfinvoke component="UserPreferences" method="get#WhichPreference#" returnvariable="LunchForToday">
   <cfinvokeargument name="Style" value="#WhichStyle#">
</cfinvoke>

In the above example, we have a UserPreferences object, a variable for the specific class of preferences and an argument.

The intent of the code is to run a method called 'getLunch' on the 'UserPreferences' component and pass 'Heavy' as an argument. (We don't want a simple salad today, boyos!)

We got the job done. Now let us look at a different approach:

<cfset UserPreferences = CreateObject("component", User).init() />
<cfset WhichPreference = "Lunch" />
<cfset WhichStyle = "Heavy">

<cfset LunchForToday = UserPreferences['get' & WhichPreference](WhichStyle) />

This code does the exact same thing. The final statement sets LunchForToday equal to UserPreferences.getLunch("Heavy"). To me, this is a lot more intuitive than the cfinvoke method shown above. This code relies on the runtime evaluation of the method and the arguments. Since the 'getLunch' method exists and the call is made at runtime, this is a very logical and valid way to get the job done.

The only problem is it doesn't seem to work. Based on my testing and conversations with Important Folks, this is simply an error in the ColdFusion language parser. I've it on good authority that it has been filed as a bug with the ColdFusion team. I've added it to the Wishlist Wiki for Scorpio and now put it here on this blog.

Does this make sense? Would others find this useful?

Comments
Yeah. Right now you have to evaluate("UserPreferences.get#WhichPreference#(WhichStyle)") which is what I do all over the place. Hopefully it'll get fixed.
# Posted By Peter Bell | 5/16/07 11:27 AM
Someone on the CFTalk list suggested this method to me:

<cfset myMethod = "getContacts">
<cfset cfc.salesman = CreateObject("component","includes.salesman")>
<cfset myFunction = cfc.salesman[myMethod]>

It's still more code than there needs to be, but it works great.
# Posted By Andy Matthews | 5/16/07 12:37 PM
Oops. Forgot to pass in the arguments:

myFunction(key,sessionID)
# Posted By Andy Matthews | 5/16/07 12:38 PM
@Andy

That method works if the function does not rely on any state variables from the component.

For example:
<cfset myFunction = cfc.salesman[myMethod]>

MyFunction in this case points to the Method resolved when 'myMethod' is evaluated. However, if the function is of the form:

<cffunction name="DaFunction" >
<cfreturn variables.instance.someValue
</cffunction>

Then the code will product an error such as:

someValue does not exist in variables.instance.


Thus, the scope and state variables DO NOT come over along with the function.
# Posted By Dan Wilson | 5/16/07 1:46 PM
<cfset cfc._myFunction = cfc.salesman[myMethod]>
<cfset cfc._myFunction(key,sessionID)>

(beware of thread safety when do this!)
# Posted By Sean Corfield | 5/18/07 4:29 AM
@Sean,

Thanks for the comment. I want to make sure I understand exactly the thread safety risks. Would this be related solely to a mis-scoped variable, or are there other concerns to watch out for?

DW
# Posted By Dan Wilson | 5/18/07 11:15 AM
If you have the CFC in a shared scope and you inject a method like that in one thread, it will affect other threads using the same instance (which will be a problem if each thread injects and tries to use a different method at the same time).

Personally, I'd stick to cfinvoke for this use case :)
# Posted By Sean Corfield | 5/18/07 11:41 AM
BlogCFC was created by Raymond Camden. This blog is running version 5.9.001. Contact Blog Owner