Injected Methods Show Up in Object Metadata in Railo but not ColdFusion
CFML , ColdFusion , Railo Add commentsI recently had a chance to listen to the latest edition of the This Week in ColdFusion Podcast wherein design patterns are discussed. One of the design patterns that Brian Carr talks about in the podcast is the Decorator Pattern. As an example, Brian describes how you can use the dynamic nature of ColdFusion to assign a function to a variable and then add that function to an object at runtime (this is also known as "method injection"). Bob Silverberg, who was a guest on the podcast then asked Brian whether the injected method would show up in the object's metadata if you called the getMetaData() function on it. Brian didn't know for sure, but he and Bob agreed that it was unlikely. This piqued my curiosity, so I set about to test this myself.
First, I built a very simple component -- person.cfc
<cfcomponent> <cffunction name="init" access="public" returntype="any"> <cfset variables.name = "Tony Garcia" /> <cfreturn this /> </cffunction> </cfcomponent>
This component only has an init() method, which sets variables.name to "Tony Garcia" and returns the instance.
Next, I wrote this template called injectiontest.cfm.
<cffunction name="testMethod" access="public" returntype="string"> <cfreturn variables.name /> </cffunction> <cfset tony = createObject( "component","person" ).init() /> <cfdump var="#getMetaData( tony )#" label="Before Injection" /> <cfset getName = testMethod /> <cfset tony.getName = getName /> <cfdump var="#getMetaData( tony )#" label="After Injection" /> The name is: <cfoutput> #tony.getName()# </cfoutput>
It's pretty clear what's going on here. First I define a function named "testMethod". Then I create an instance of person.cfc and dump out its metadata. I then assign testMethod() to a variable and inject it into my object using the name "getName" for the method (since that's what it does). I dump the metadata again after the injection and then just test the method itself by outputting its return value.
Here is the output of the dump before injection using ColdFusion 9:

As expected, only the init() method appears in the array of functions. This is the dump after the method injection:

The only function that appears is still the init() method. So Brian and Bob were correct in their suspicions. But then, since I work alot with Railo, I decided to run the code in Railo 3.1. Here is the dump before the method injection (now I'm just showing the functions element):

Again, we only see the init() function. But notice that the dump gives us some more info about the function compared to the CF9 dump. Take special note of the "owner" element, which is the path to the cfc.
Now here is the dump after the method injection:

So the Railo dump of the object's metadata does indeed include the injected method! Another interesting observation is that the name given for the method is "testMethod", not "getName", so it uses the original name of the function that was injected, not the name it was assigned to in the object. Also, note that the "owner" element is the path to the template where the injected method was defined. So if you're doing some fancy programming against metadata, the "owner" element may be used to check whether the method in question is built-in to the object or was injected.
Anyway, I thought this was pretty interesting so I thought I'd share.

Apr 1, 2010 at 10:11 PM I'm very glad you decided to do that research Tony. I'm also thrilled to see that Railo properly describes object instance meta-data correctly. Now if it only supported full-script components and interfaces :-)
Apr 1, 2010 at 11:21 PM Interesting! I didn't know that Railo updated the metadata dynamically!
As for full-script - it's coming... we had argument types and defaults a long time ago and we've recently added function access and return types. Component and interface syntax, along with the new operator is planned "soon".
Apr 2, 2010 at 6:16 AM Does Railo also have a getComponentMetaData() method? In CF, this allows the meta data to be gotten from the class definition, rather than from an instance. I ask because I wonder if Railo has any concept of meta data of the class itself?
In CF, all instances share the same meta data object.
Apr 2, 2010 at 6:17 AM Arrr, tabbing from comments goes to the submit button (skips subscribe + remember checkboxes). Just subscribing....
Apr 2, 2010 at 7:03 AM @Ben,
Yep -- I've used getComponentMetadata() in Railo with no problems.
Apr 2, 2010 at 7:04 AM @Tony,
Ah pretty cool then. I guess you can use getComponentMetaData() to get root class meta data and getMetaData() to get run time meta data. Seems like a sweet deal.
Apr 2, 2010 at 7:07 AM Yeah. I guess, then, the title of the blog post is a bit misleading since I was really talking about the metadata of the object, not the component
Apr 2, 2010 at 7:10 AM @Tony,
I didn't mean to imply anything about the title - just curious about the differences in Railo. In CF, getMetaData() and getComponentMetaData() return the same exact thing for a CFC. So, to me, it seems cool that Railo differentiates.
Apr 2, 2010 at 8:28 AM I went ahead and changed the title anyway. Technically, I think "object metadata" is more accurate than "component metadata".
Apr 2, 2010 at 8:41 AM Sounds good to me :)
Apr 2, 2010 at 3:31 PM @Sean
Full CF syntax / API support in Railo would be a dream come true :-). Needless to say (although I'll say it) I'm very happy to hear that it's close.
Apr 2, 2010 at 4:46 PM I've noticed this in Adobe CF and always considered it a bug. It's worth nothing, that structKeyExists(myObject,"injectedMethodName") WILL return true. The fact that it's not reflected in the metadata to me is just wrong.
I can only assume the Adobe engineers chose to cache metadata and reuse it for all instances of a component for performance reasons.
Apr 22, 2010 at 10:48 AM @Brian -- you might be interested in my latest blog post:
http://objectivebias.com/entry/full-cfscript-component-syntax-is-now-in-latest-railo-ber