Swapping ColdFusion Component Methods At Run Time

I was in the shower this morning, thinking about Peter Bell and his LightWire code. LightWire (to dumb it down a lot) automates the passing in of dependent object during object creation / initialization. This got me thinking: we can store variables at run time but what about methods? Methods are just variables right??? Traditionally I think of methods as compile time objects. I have done things like that in Javascript where you can add functions to an object at any time because it's a really lose, awesome language, but what about ColdFusion? When it comes to methods in Components, it's not just about the method code... it's about references (ex. THIS, VARIABLES, SUPER). How does that translate at run time?

As you can see, both these objects have a way to act inappropriately (oh you guys!).

Let's start off by instantiating each of these objects and then getting them to act inappropriately (just to make sure everything is up and running):

<cfset Girl = CreateObject( "component", "Girl" ).Init(

Name = "Sarah"

) />

<cfset Boy = CreateObject( "component", "Boy" ).Init(

Name = "Jeff"

) />

<!--- Do something crazy. --->

#Girl.ActInappropriately()#<br />

<br />

<!--- Do something crazy. --->

#Boy.ActInappropriately()#<br />

This gives us the output:

SARAH: I am Sarah and I just flashed my boobies :)

JEFF: I am Jeff and I just pooped myself :(

Nice, working well. Let's just take a look at the functions for a second. Both of them refer to the THIS scope as well as the VARIABLES scope. They have the same simple value - Name was stored in both scopes. Let's see what happens when we swap the methods at run time (for the rest of these tests, assume that the Boy and Girl objects have been instantiated already):

<!--- Move the boy function over to the girl. --->

<cfset Girl.ActInappropriately = Boy.ActInappropriately />

<!--- Do something crazy. --->

#Girl.ActInappropriately()#<br />

<br />

<!--- Do something crazy. --->

#Boy.ActInappropriately()#<br />

This gives us the output:

SARAH: I am Sarah and I just pooped myself :(

JEFF: I am Jeff and I just pooped myself :(

As you can see, the functionality of the Boy's method was copied over to the Girl's method, however, both the THIS and the VARIABLES scope pointers translated appropriately to the new component. That's pretty cool!

Ok, that's moving a method from one component to another... but what happens if I want to create an entirely new method and store it in a component. To test this, I created this non-component-contained method, RunNaked:

<cffunction

name="RunNaked"

access="public"

returntype="string"

output="false"

hint="I am about to run around naked.">

<!--- Do something crazy. --->

<cfreturn (

"I am " & THIS.Name &

" and I just ran naked around the office :D"

) />

</cffunction>

This method refers to the THIS scope. Outside of a component, this makes no sense. In fact, if I try to call this method as is:

<!--- Run naked. --->

#RunNaked()#

... it throws the following error:

Element NAME is undefined in THIS.

Ok, but that's not what I want to test. Let's store this method pointer into the girl's ActInappropriately method (if only this were real, right!):

<!--- Store a completely new function pointer. --->

<cfset Girl.ActInappropriately = RunNaked />

<!--- Do something crazy. --->

#Girl.ActInappropriately()#<br />

<br />

<!--- Do something crazy. --->

#Boy.ActInappropriately()#<br />

This gives us the output:

I am Sarah and I just ran naked around the office :D

JEFF: I am Jeff and I just pooped myself :(

Very cool! Even though the new method has no idea about what a component even was (as it was defined outside of any components), when moved over to the Girl instance, the THIS scope hooked up quite nicely.

This has been a cool little test don't you think? I have not fully looked into it, but I think this is the idea behind ColdFusion Mixins. I am not sure what I would ever use this for, but it might come in handy one day to be able to create an Empty ColdFusion Component and then dynamically give it functionality. Who knows!

Reader Comments

Say you have a PoleDancer class that implements a Dance() method and a Stripper class that implements a Strip() method as well as the Dance() method. Say you then have a function that operates one way if you pass it a PoleDancer and another if you pass it a Stripper (duck typing). You can call StructDelete(myStripper,"Strip") to remove that method from the Stripper, and thus the function will think that the object is a PoleDancer, not a Stripper. If you wanted to be extra sneaky:

Well played (and thanks for getting into the spirit of the posting ;)). Now that you mention the deleting, I think I read something in the last ColdFusion Quarterly, by Ray Camden, that dynamically deletes the OnRequest() method from the Application.cfc if there is .CFC or WSDL in the URL. Can't find a link to it though.

But thanks, I totally forgot about deleting stuff. And the storing the old reference is very cool.

Actually, this is also very practical. It looks a whole lot like "mixins" in ruby - a way of implementing multiple inheritance. So you can configure an object's behavior dynamically (at runtime), giving it whatever functions it needs to do the job at hand.

Nice post! The first piece is what Hal/Sean (in the CF world) describe as object based mixins. You can even cfinclude methods into a template on component instantiation for class based mixins. I've never seen just having the function sitting there all alone and adding it to a class, but it is cool to see that works too!

I actually just quite recently updated the function libraries in the onTap framework to load methods on demand using onMissingMethod in CF8. (And am just now reading this blog entry 2yrs late). :) The libraries themselves were originally created on CF5 before there even were CFCs and until just this latest version (3.2) they were always stored in structures instead of CFCs. The dream had always been to have a collection of functions that could understand their own dependencies and be loaded as-needed instead of being required to aggressively load when the application starts. (DLL wouldn't be a terrible analogy.) But making that actually happen proved to be too difficult to manage prior to CF8 and onMissingMethod. Now they finally do, which eliminated a nice big chunk of delay when the application loads.

Glad you got it working. I am just starting to really appreciate the OnMissingMethod() functionality in ColdFusion 8. In OOPhoto, its really allowing me to rock some very dynamic and "smart" behaviors on my objects.