ColdFusion Custom Tags Cannot Act As ColdFusion Component Mixins

Posted January 4, 2011 at 10:28 AM by Ben Nadel

Tags: ColdFusion

The other night, I was laying in bed, thinking about ColdFusion code (as I often times find myself doing), and it suddenly occurred to me that I wasn't sure of the expected behavior of ColdFusion custom tags that execute within a ColdFusion component context. Anyone who has created a user defined function (UDF) library or used free-standing functions knows that UDFs get bound to the calling page, not to the defining page. Unless, of course, the UDF is being called as a class method on a ColdFusion component; in that case, the UDF is bound to the component instance, not to the calling context. But what happens when you execute a ColdFusion custom tag within a ColdFusion component? What is the CALLER scope bound to? And, how do method invocations via the CALLER scope work?

To test this, I created a simple ColdFusion component that had a public method, a private method, and private variable:

Greeting.cfc

  • <cfcomponent
  • output="true"
  • hint="I am here to test the binding of CALLER to a ColdFusion component.">
  •  
  •  
  • <!--- Set a local variable to the component. --->
  • <cfset variables.name = "Sarah" />
  •  
  •  
  • <cffunction
  • name="sayHello"
  • access="public"
  • returntype="void"
  • output="true"
  • hint="I say hello.">
  •  
  • <!--- Say hello. Use the loacl NAME variable. --->
  • Hello, my name is #variables.name#!
  •  
  • <!--- Return out. --->
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="privateMethod"
  • access="private"
  • returntype="void"
  • output="false"
  • hint="I am just a private method for CFDump.">
  •  
  • <cfreturn />
  • </cffunction>
  •  
  •  
  • <!---
  • Now, execute a custom tag that will invoke the sayHello()
  • method in the CALLER context.
  • --->
  • <cf_sayhello />
  •  
  • </cfcomponent>

As you can see, this is very simple; the public method, sayHello(), makes use of the private variable, name, in order to output a greeting. As part of the pseudo constructor, we are then executing a ColdFusion custom tag (sayhello.cfm) which will invoke the sayHello() method. The private method is there just to help us figure out which scopes are being bound (a private method will not show up on a public-scope binding).

The sayhello.cfm ColdFusion custom tag then turns around and invokes the sayHello() method on its Caller scope (and various combinations of caller-based paths):

sayhello.cfm (ColdFusion Custom Tag)

  • <!---
  • Define a local variable to the custom tag. This will not collide
  • with the caller's variables scope, but since it is named the same
  • as a variable in the caller context, it will allow us to figure
  • out where the method invocation is being bound.
  • --->
  • <cfset variables.name = "customTag" />
  •  
  • <!---
  • Invoke the sayHello() method on the CALLER scope. If the caller
  • scope were just a standard CFM template, then the function would
  • execute with a local, custom-tag context. However, in this case,
  • the caller context is a CFC - where will the function bind??
  • --->
  • <cfset caller.sayHello() />
  •  
  • <br />
  • <br />
  •  
  • <!---
  • Now, try to invoking the method by explicitly calling the
  • variables scope associated with the caller context.
  • --->
  • <cfset caller.variables.sayHello() />
  •  
  • <br />
  • <br />
  •  
  • <!---
  • Now, try invoking the method by explicitly calling the
  • this scope associated with the caller context.
  • --->
  • <cfset caller.this.sayHello() />
  •  
  •  
  • <br />
  • <br />
  •  
  •  
  • <!---
  • Now, let's CFDump out the caller scope to see what the actual
  • binding is.
  • --->
  • <cfdump
  • var="#caller#"
  • label="CALLER Scope"
  • />
  •  
  • <br />
  •  
  • <!---
  • And, let's output the caller-bound varaibles scope to see what
  • the actual binding is.
  • --->
  • <cfdump
  • var="#caller.variables#"
  • label="CALLER.Variables Scope"
  • />
  •  
  • <br />
  •  
  • <!---
  • And, let's output the caller-bound this scope to see what
  • the actual binding is.
  • --->
  • <cfdump
  • var="#caller.this#"
  • label="CALLER.This Scope"
  • />
  •  
  •  
  • <!--- Exit out of the tag. --->
  • <cfexit method="exitTag" />

As you can see, the first thing we are doing is defining a custom-tag-local variable, name, with the value "customTag". This will help us determine where the sayHello() method is being bound. Then, we try to execute the sayHello() method on various caller-based objects: caller, caller.variables, caller.this. When we instantiate the Greeting.cfc ColdFusion component, thereby running the above ColdFusion custom tag, we get the following page output:

 
 
 
 
 
 
ColdFusion Caller Scope Does Not Bind To A ColdFusion Compoennt When Exexcuting Within A ColdFusion Component. 
 
 
 

Now this is some very interesting stuff! The first thing we can see is that some of our class method invocations actually acted like unbound user-defined functions; that is, their execution was bound to their calling context (the custom tag), and not to the ColdFusion component instance as one might expect:

caller.sayHello() :: NOT bound to CFC.

caller.variables.sayHello() :: NOT bound to CFC.

caller.this.sayHello() :: Bound to CFC.

Furthermore, from our CFDump output, we can see that there is a clear distinction between the CALLER scope the ColdFusion component itself. The Caller scope is mysterious and powerful beast. I have blogged before about the special way in which the Caller scope treats keys for getting and setting values. As such, it's not too surprising that the caller scope doesn't map to a ColdFusion component even when the target context is a ColdFusion component.

When you look at the CFDump, you'll notice that the ColdFusion custom tag has access to the private method - privateMethod() - of the ColdFusion component when it outputs the variables scope. However, variables-scope-based invocations do not bind to the CFC. The only way that we could get the method to bind to the CFC is when we invoke it via "caller.this"; unfortunately, when we do that, as you can see in our last CFDump, we no longer have access to the private method.

From this, I can see two important take-aways: 1) a ColdFusion custom tag, when executed from within a ColdFusion component, can gain access to both the public and private methods of the given component, and 2) although the custom tag can access both the public and private methods of the CFC, it cannot act as though it were a ColdFusion component mixin. In short, a custom tag acts like a completely separate, encapsulated object which happens to have the ability to access unbound private methods.

I suppose it would help to starting thinking about the Caller scope more like a "bridge" to another context and less like a reference to an existing object or scope.




Reader Comments

Jan 4, 2011 at 11:37 AM // reply »
49 Comments

Our experience is use Custom Tags for content handlers, view tools and not for logic. With that concept in mind we would not be against your use case but that level of content handling might work better from a component to start with.

Why were you trying to use a custom tag inside a mixin component to start with... that might shed some light on the subject. Was this something from a takeover project. (It is where the frustration hits me more often for things like this.)


Jan 4, 2011 at 2:36 PM // reply »
10,743 Comments

@John,

I can't think of a great use-case for in-CFC custom tags. This was more to explore the behavior of the Caller scope within a CFC context. I just wanted to know how it works!


Jan 4, 2011 at 2:49 PM // reply »
49 Comments

@Ben,

Functional exploration has a value in the Mix. Thanks for the exploration of one of my favorite topics... 'how ColdFusion thinks'.


Jan 4, 2011 at 3:22 PM // reply »
10,743 Comments

@John,

Agreed - understanding the way things works is always quality.


Jan 4, 2011 at 4:13 PM // reply »
27 Comments

@Ben,

When I run your code and dump caller.this, I see both sayHello() and privateMethod() inside the dump. What version of CF are you running? I'm on 9,0,1,274733.


Jan 4, 2011 at 4:38 PM // reply »
10,743 Comments

@Tony,

Uh oh :) I ran this in ColdFusion 8.1 (or whatever the latest CF8 version is. Furthermore, when I try to access the privateMethod():

<cfset caller.this.privateMethod() />

... I get:

The method privateMethod was not found in component /Sites/bennadel.com/testing/caller_cfc_method/Greeting.cfc. Ensure that the method is defined, and that it is spelled correctly.

It works if I call:

<cfset caller.variables.privateMethod() />

... but, of course, outputs nothing.

Can you actually invoke the private method without error?


Jan 4, 2011 at 4:56 PM // reply »
27 Comments

@Ben,

No it still errors out. Must just be an update to cfdump that's causing the difference in results.


Jan 4, 2011 at 5:00 PM // reply »
10,743 Comments

@Tony,

Ha ha, I can't tell which is worse : a change in behavior of the caller scope across versions... or a completely misleading CFDump :)


Post A Comment

Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.

Please review the following issues:

Author Name:


Author Email:

Author Website:

Comment:

Supported HTML tags for formatting: <strong>bold</strong>   <em>italic</em>   <code>code</code>







  • Help Wanted - Find Your Next ColdFusion Job
InVision App - Prototyping Made Beautiful With Prototyping Tools Ben Nadel's Company - Epicenter Consulting Recent Blog Comments
May 16, 2012 at 8:18 PM
Best Of ColdFusion 10 Contest Entry - HTML Email Utility
Just found this, looks good! I'm trying to run it on local, it's the 64bit version and I'm experiencing horrible lag. On average the generate.cfm processes the content change in 60-90 seconds. I've ... read »
May 16, 2012 at 6:40 PM
Maintaining Sessions Across Multiple ColdFusion CFHttp Requests
I am trying to integrate this CFHTTPsession into an application that will log into zeekrewards.com to post ads and I am not having any luck. The code works perfectly for logging into other websites, ... read »
May 16, 2012 at 2:44 PM
Creating A Sometimes-Fixed-Position Element With jQuery
Thank you, very useful technique! Worked like a charm. ... read »
May 16, 2012 at 1:58 PM
Movies As A Religious Experience
Acting can, in a way, ruin the movie-goer's experience. I used to be able to get so caught up in movies and their plots, and totally engaged. But lately, I haven't been able to as much with a lot o ... read »
May 16, 2012 at 1:52 PM
The Science Of Optimal Post-Exercise Nutrition
children of this age eat very less vegetables so u can opt for salads they will like it also carrot ,cucumber,onion and as far as pulses are concerned u can boil them ,give him along with mashed rice ... read »
May 16, 2012 at 1:34 PM
Strange ColdFusion JRUN Stack Overflow Error
Hey, Recently I updated my jrun4 using the latest updater 7 and now i am having memory issues :(:(:( any help is appreciated ... read »
May 16, 2012 at 9:56 AM
ColdFusion 10 Beta, Apache Tomcat, And Symbolic Links On Mac OSX
Hi, Now that ColdFusion 10 is out I have stumbled over this as well and I cannot figure out the proper solution. We're running virtual hosts via Apache2; the ColdFusion-applications store their fil ... read »
May 15, 2012 at 6:03 PM
Movies As A Religious Experience
@Ben, I don't know whether you'd consider this a religious observation, but it seems to me, in a sense, movies multiply how many lives we get to have. Each movie is like a little extra life we get ... read »