Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at cf.Objective() 2009 (Minneapolis, MN) with:

Dynamic And Unexpected ColdFusion GetCurrentTemplatePath() Behavior

By Ben Nadel on
Tags: ColdFusion

ColdFusion has two methods that help you determine which templates are being executed:

  • GetCurrentTemplatePath()
  • GetBaseTemplatePath()

Normally, the GetCurrentTemplatePath() method will return the path of the template in which the method call resides. However, I just came across some unexpected behavior; if the method call is inside of a ColdFusion user defined function, then the GetCurrentTemplatePath() returns the path of the template that binds dynamically at runtime to the parent function. We're not even talking about the template that houses the UDF - we're talking about the template that binds to the UDF.

However, that is not even 100% consistent. If the GetCurrentTemplatePath() is inside of a ColdFusion template that is then included into a function, the GetCurrentTemplatePath() function returns the path of the housing template, not of the page that binds to the ColdFusion UDF at runtime.

Take a look at this example. I have two mixins. One is a mixin that contains a ColdFusion UDF that simply returns the current template path (GetPath.cfm):

  • <cffunction
  • name="GetMixinPath"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="Returns the current template's (this) file path.">
  •  
  • <!---
  • Return the path of the currently executing
  • template (this mixin function).
  • --->
  • <cfreturn GetCurrentTemplatePath() />
  • </cffunction>

The other mixin is a simple ColdFusion template which just outputs the current template path:

  • <cfoutput>
  • #GetCurrentTemplatePath()#
  • </cfoutput>

Then, I have two ColdFusion components. I have my Base.cfc which has a defined method, GetBaseComponentPath(), which returns the current template path. It also has a method, GetBaseComponenMixintPath(), which includes the GetCurrentTemplatePath.cfm mixin and returns the outputted value:

  • <cfcomponent
  • output="false"
  • hint="Base component to be extended">
  •  
  • <cffunction
  • name="GetBaseComponentPath"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="Returns the base component's (this) file path.">
  •  
  • <!---
  • Return the path of the currently executing
  • template (this component).
  • --->
  • <cfreturn GetCurrentTemplatePath() />
  • </cffunction>
  •  
  •  
  • <cffunction
  • name="GetBaseComponenMixintPath"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="Returns the base component's (this) file path.">
  •  
  • <cfset var strPath = "" />
  •  
  • <!---
  • Include the template that outputs the
  • current template path.
  • --->
  • <cfsavecontent variable="strPath">
  • <cfinclude template="GetCurrentTemplatePath.cfm" />
  • </cfsavecontent>
  •  
  • <!--- Return the path of the mixin template. --->
  • <cfreturn Trim( strPath ) />
  • </cffunction>
  •  
  • </cfcomponent>

Then, I have the Extender.cfc ColdFusion component which extends the Base.cfc and includes the mixin GetPath.cfm:

  • <cfcomponent
  • extends="Base"
  • output="false"
  • hint="The extending component.">
  •  
  • <cffunction
  • name="GetExtenderComponentPath"
  • access="public"
  • returntype="string"
  • output="false"
  • hint="Returns the current component's (this) file path.">
  •  
  • <!---
  • Return the path of the currently executing
  • template (this component).
  • --->
  • <cfreturn GetCurrentTemplatePath() />
  • </cffunction>
  •  
  •  
  • <!---
  • Include the mixin file that defines a function
  • for getting the current template path.
  • --->
  • <cfinclude template="./GetPath.cfm" />
  •  
  • </cfcomponent>

Ok, so now, I am gonna instantiate the Extender.cfc ColdFusion component and test all the various method returns for GetCurrentTemplatePath():

  • <!--- Create our extender CFC instance. --->
  • <cfset objCFC = CreateObject( "component", "Extender" ) />
  •  
  • <h4>
  • CFC-Based Methods
  • </h4>
  •  
  • <p>
  • Getting the path via the Base.cfc method:<br />
  • #objCFC.GetBaseComponentPath()#
  • </p>
  •  
  • <p>
  • Getting the path via the Exnteder.cfc method:<br />
  • #objCFC.GetExtenderComponentPath()#
  • </p>
  •  
  • <p>
  • Getting the path via the mixin method:<br />
  • #objCFC.GetMixinPath()#
  • </p>
  •  
  • <p>
  • Getting the path via the Base.cfc mixin method:<br />
  • #objCFC.GetBaseComponenMixintPath()#
  • </p>
  •  
  •  
  • <!---
  • The above have all been called on methods of
  • a CFC. What happens if we include the mixin
  • method directly into the current template
  • (index.cfm) and then execute it that way.
  • --->
  •  
  • <h4>
  • Template-Based Methods
  • </h4>
  •  
  • <!--- Now, let's include the mixin into this template. --->
  • <cfinclude template="./GetPath.cfm" />
  •  
  • <p>
  • Getting the path via TEMPLATE mixin method:<br />
  • #GetMixinPath()#
  • </p>

Running the above, we get the following output:

CFC-Based Methods

Getting the path via the Base.cfc method:
D:\....\testing\app_extend\Extender.cfc

Getting the path via the Exnteder.cfc method:
D:\....\testing\app_extend\Extender.cfc

Getting the path via the mixin method:
D:\....\testing\app_extend\Extender.cfc

Getting the path via the Base.cfc mixin template/method:
D:\....\testing\app_extend\GetCurrentTemplatePath.cfm

Template-Based Methods

Getting the path via TEMPLATE mixin method:
D:\....\testing\app_extend\index.cfm

As you can see, the Base.cfc method, GetBaseComponentPath(), the Extender.cfc method, GetExtenderComponentPath(), and the Extender.cfc mixin method, GetMixinPath(), all dynamically bound themselves to the Extender.cfc at runtime, returning the Extender.cfc as the current template path (regardless of where GetCurrentTemplatPath() was defined). Even the index.cfm-based mixin method, GetPath(), bound dynamically to the index page at runtime.

The only thing that didn't seem to bind dynamically at runtime was the GetCurrentTemplatePath.cfm mixin which was a straight-up ColdFusion template, not a mixin method.

Something about this just rubs me the wrong way. I understand that function binding is highly dynamic, and this is a very powerful thing because it allows us to evaluate the THIS and VARIABLES scope at the point of reference, which, in turn, allows us to add, delete, and share methods across ColdFusion components. However, I don't feel that this is the intent of methods like GetBaseTemplatePath() and GetCurrentTemplatePath(). In fact, if you look at the documentation for GetCurrentTemplatePath(), it reads:

Returns: The absolute path of the page that contains the call to this function, as a string.

It doesn't mention anything here about dynamic runtime binding. Now, you might argue that the documentation then states:

If the function call is made from a page included with a cfinclude tag, this function returns the page path of an included page.

Here, at least it mentions the CFInclude usage. However, if that was the requirement, then using it within a CFModule of CFImport tag would not meet this criteria. Of course, if we invoke the GetCurrentTemplatePath.cfm as a module from WITHIN the index.cfm page:

  • <cfmodule template="./GetCurrentTemplatePath.cfm">

... we get the following output:

D:\....\testing\app_extend\GetCurrentTemplatePath.cfm

Here, the GetCurrentTemplatePath() method is binding to the tag, not to the index page. This proves that CFInclude is NOT a requirement for the GetCurrentTemplatePath() method to work correctly.

So, long story short, I think the dynamic binding of the GetCurrentTemplatePath() to its housing ColdFusion component is not the expected behavior and seems to go against the ColdFusion documentation.




Reader Comments

Another interesting behavior i just notice a month a go with thegetTemplatePath() function.

If you add a call to this function in the application.cfm and on the url your accessing a cfm file the function return the actual path of the cfm page - but if your accessing a remote method of a cfc file the getTemplatePath() return the path to the application.cfm file.

note that this was not the behavior of the Coldfusion mx 6.1. In 6.1 the function returns the path to the cfc file as well.

Reply to this Comment

@Kris,

That is very interesting. I haven't done much yet with direct CFC calls. I am surprised that is has changed from version to version.

Reply to this Comment

I ran into a problem with this behavior while working on a project recently. I needed to get the path to the calling page inside a custom tag, which proved to be pretty complicated.

For anyone who's curious about the internals of the CF engine this sheds a lot of light on how function binding and includes/cfcs/custom tags really work inside the engine.

http://enfinitystudios.thaposse.net/blog/2007/07/17/getting-the-expected-results-for-getcurrenttemplatepath-in-a-custom-tag/

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
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.