Defining Application mappings using relative paths in Lucee
Ben has an interesting post today on his discovery that Lucee appears to be much better than Adobe ColdFusion (ACF) at resolving relative paths defined in application mappings.
In ACF he'd always had to "parse" the path of the Application.cfc where the mappings were defined to ensure that the mappings would resolve correctly regardless of the location of the calling template.
this.root = getDirectoryFromPath( getCurrentTemplatePath() ); // parse the path of the current Application.cfc
this.mappings = {
"/root": this.root,
"/up-one": this.root & "../",
"/down-one": this.root & "vendor/"
};
Ben points out that you can define the mappings more simply using relative paths:
this.mappings = {
"/root": "./",
"/up-one": "../",
"/down-one": "vendor/"
};
And he has a clever test to show that, while these mappings fail in ACF when called from sub-directories, they work just fine in Lucee.
Although I stopped using ACF in favour of Lucee several years ago, I still use the more complex "parsing" approach so I was looking forward to simplifying my code.
Inheritance problem
Unfortunately things didn't work as I hoped because of the inheritance structure I tend to use with my Application.cfc files.
My main Application.cfc lives above the web root, and I then have another Application.cfc in the web root which extends it. This allows me to have multiple web roots associated with an application without having to duplicate the Application.cfc. It also adds a little bit of extra security by reducing the amount of code below the web root.
This is the structure to give you a clearer idea.
c:/test/aboveTheWebroot/ _Application.cfc _webroot/ __Application.cfc __index.cfm
The Application.cfc in the "aboveTheWebroot" folder looks like this:
component hint="I'm in directory above the public web root"{
this.name = "mappingsTest";
this.applicationTimeout = CreateTimeSpan( 0, 0, 0, 5 );
this.sessionManagement = false;
this.mappings = {
"/aboveTheWebRoot": "./" //simple relative path definition
}
}
The Application.cfc in the webroot only contains this:
component extends="../Application"{}
To check how the mapping is resolved, my webroot/index.cfm file has the following:
<cfscript>
Echo( "<i>/aboveTheWebroot</i> resolves to <b>#ExpandPath( '/aboveTheWebroot' )#</b>" );
</cfscript>
If all was working correctly it should output:
/aboveTheWebroot resolves to C:\test\aboveTheWebroot
But instead I get:
/aboveTheWebroot resolves to C:\test\aboveTheWebroot\webroot
That seems to indicate that the relative mapping definition is being resolved from the child Application.cfc's path in the webroot directory instead of the parent Application.cfc where the definition actually lives.
As far as I can see the only way of making sure the definition is correct is to use the the "parsing" approach, to which I tend to add a third layer in the form of GetCanonicalPath()
to make sure there are no issues with trailing slashes (which I've found can affect ORM):
this.root = GetCanonicalPath( GetDirectoryFromPath( GetCurrentTemplatePath() ) );
this.mappings = {
"/aboveTheWebRoot": this.root
}
Using this for my definition in the parent Application.cfc gives me the correct output:
/aboveTheWebroot resolves to C:\test\aboveTheWebroot
It seems that, for my situation at least, this complexity is still necessary.