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:

view plain print about
1<cfset UserPreferences = CreateObject("component",User).init() />
2<cfset WhichPreference = "Lunch" />
3<cfset WhichStyle = "Heavy">
4
5<cfinvoke component="UserPreferences" method="get#WhichPreference#" returnvariable="LunchForToday">
6    <cfinvokeargument name="Style" value="#WhichStyle#">
7</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:

view plain print about
1<cfset UserPreferences = CreateObject("component", User).init() />
2<cfset WhichPreference = "Lunch" />
3<cfset WhichStyle = "Heavy">
4
5<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?

There are no comments for this entry.

Add Comment Subscribe to Comments

5/16/07 11:27 AM # Sez Peter Bell

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.


5/16/07 12:37 PM # Sez Andy Matthews

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.


5/16/07 12:38 PM # Sez Andy Matthews

Oops. Forgot to pass in the arguments:

myFunction(key,sessionID)


5/16/07 1:46 PM # Sez Dan Wilson

@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.


5/18/07 4:29 AM # Sez Sean Corfield

<cfset cfc._myFunction = cfc.salesman[myMethod]>
<cfset cfc._myFunction(key,sessionID)>

(beware of thread safety when do this!)


5/18/07 11:15 AM # Sez Dan Wilson

@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


5/18/07 11:41 AM # Sez Sean Corfield

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 :)


Add Comment Subscribe to Comments