cfc that bring in additional functions via include - coldfusion

My application.cfc starts with:
<cfcomponent extends="org.corfield.framework">
later on void function setupApplication() output="false" {
I have
// tools
include "initapp.cfm";
initapp.cfm has nothing but functions in it. Things like:
<!--- Helper functions --->
<cfscript>
string function stripHTML(str) output="false" {
return REReplaceNoCase(arguments.str,"<[^>]*>","","ALL");
}
application.stripHTML = stripHTML;
</cfscript>
The nature of the functions is NOT associated with a session. Is there a better way to make functions available globally?

If you're trying to put helper functions together for use in CFCs, one option may be to use the component.cfc file.
Using the component.cfc file
All CFCs automatically extend the ColdFusion
WEB-INF/cftags/component.cfc component. (The WEB-INF directory is in
the cf_root/wwwroot directory on ColdFusion configured with an
embedded J2EE server. It is in the cf_root directory when you deploy
ColdFusion on a J2EE server.) This CFC is distributed as a zero-length
file. You can use it for any core methods or properties that you want
all CFCs in your ColdFusion application server instance to inherit.
Note: When you install a newer version of ColdFusion, the installation
procedure replaces the existing component.cfc file with a new version.
Therefore, before upgrading, you should save any code that you have
added to the component.cfc file, and then copy the code into the new
component.cfc file.
If that solution is TOO global you can extend your helper cfc, but it has to be done in every cfc and doesn't answer your one-time-set-it-and-forget-it idea. See Using CFCs effectively
If your helper functions are for use in .cfm files, I'd do like Adam suggested. I usually put my helper functions in a "tools" cfc located in a CFC folder and make it an application scoped cfc.
function onApplicationStart(){
application.tools = createObject("component", "cfc.tools");
}
One of my helper functions logs the time it takes to index a solr collection. Using it looks like
<cfset application.tools.logSolrIndex('collectionName',getTickCount()-start,qFileList.recordCount)>
Last resort:
If you had to stick with an include for use outside of the application.cfc, I might simply include initapp.cfm onRequest() before you include your page.

Put the functions in a library CFC, and then put an instance of that CFC in the application scope in onApplicationStart().
That said, you'd be kinda breaking encapsulation if you then referenced this application-scoped CFC within other CFCs, which is a consideration (not necessarily a deal breaker, but a consideration).
You could look at some sort of dependency injection approach to mitigate this (eg: ColdSpring)
Whichever way I ended up doing it, I would not be doing it the way you're doing it, I'm afraid.

Related

Difference between cfc and cfm

I'm trying to create basically a library of UDFs (User Defined Functions) for a web site run in ColdFusion. While doing this I am trying to find out what the differences are between cfc and cfm files. Which would be more helpful in creating this library of functions? I know that I can use
<cfinclude template="mytemplate.cfm>
to include it in a page but that will run the entire contents of that cfm on that page every time. I don't know and easier way to use cfc other than to create an object of the cfc and call the function that way.
<cfobject type="component" action="create" name="test">
Any ideas?
The way that I do it is to create all my UDF in a cfc. I then initialize that cfc on application start:
public function onApplicationStart() {
// Application settings
application.util = createObject("component","cfc.util");
return;
}
use a cfc you can call more easily from more places, if its not too huge put it into your application scope

How to traverse two levels up when referencing a coldfusion component in a cfinvoke?

If I don't want to do any mappings, is there another way to call a method in a component, which is "two levels up". Like so:
service
component_to_call.cfc
mem
pages
handlers
calling.cfc
this is my call from inside calling.cfc:
<cfinvoke
component="service.component_to_call"
method="do_the"
param1="#renderedResults#"
param2="#taskByName#"
returnvariable="tamperedCode">
</cfinvoke>
which throws an error that the service.component_to_call can't be found.
Qustion:
How can I call the component in the parent folder?
Why don't you want to use mappings? That's a bit odd.
Anyway, just browse directly to the CFC you wish to instantiate, and it will output it's autogenerated API docs, the first thing will be its full dotted path. You can use that. However that is absolute, not relative.
What you cannot do is to somehow specify a relative path like one might with a file (eg: "../../fileInGrandparentDir.cfm"... one cannot do that with a CFC path.
An alternative is to make some directory that is an ancestor to both CFCs into a custom tag path, then you can just reference the CFCs by their filename and CF will find them. There's a performance hit with doing this if the directory structure is complex. That said: not much of a performance hit.
Really... you should have a mapping to the top level of your app's directory, and then use the mapping to fully-path the CFCs. That's how it's generally done, and is the most transportable approach, I think.
You could include the CFC from the level up in a Proxy.cfc that is at the same level as the calling CFC and then extend Proxy.cfc by adding extends="Proxy" as an attribute of the calling CFC.
Example Proxy.cfc
<cfcomponent name="Proxy">
<cfinclude template="../../service/component_to_call.cfc">
</cfcomponent>
Example calling cfc
<cfcomponent name="calling" extends="Proxy">
</cfcomponent>

Using onMissingTemplate in lieu of stub cfm files

In our ColdFusion application each request goes through index.cfm
Application.cfc decides form the query and form parameters which componetes the user is actually wanted. Those components are instantiated and the content is dropped through OnRequestStart.
Rather than always hit index.cfm with a query/form parameter, for simple cases, we would like to hit a "missing" cfm (i.e. MyApp.cfm) and allow the OnMissingTemplate function parse out the fact that we really want the content of a component (i.e. MyApp).
Another way to do this would be to actualy put cfm stub files in for "generic" calls to the components but it seems like with OnMissingTemplate we do not need to do that.
Is this a reasonable use for OnMissingTemplate?
That's a great use for onMissingTemplate. Just make sure that if you're using IIS, that you make sure that the files you're linking to are actually .cfm (MyApp.cfm) files, and not directories (/MyApp/). See these links for more information:
http://www.bennadel.com/blog/1625-ColdFusion-8-s-OnMissingTemplate-So-Close-To-Being-Good.htm
http://www.bennadel.com/blog/1694-ColdFusion-s-OnMissingTemplate-Event-Handler-Works-With-CFC-Requests.htm

Fusebox installation without access to webroot

Previously (and locally) I've placed the fusebox5 directory in the web root, and then applications from anywhere in the tree have been able to access it. I'd previously also used Application.cfm rather than .cfc.
In this environment I don't have access to the webroot and the FB files don't really need to be that far down anyway, so I had planned to store them alongside the applications. Am I right in thinking that index.cfm is overlooked if Application.cfc is in use (and therefore there's no point changing the cfinclude value to be eg. ../fusebox5/)? If so, how can I include the framework without having Fusebox in the root or in a mapping? Error is:
Could not find the ColdFusion Component or Interface fusebox5.Application.
No, your app is still going to need index.cfm. What you'll need is a cf application mapping for Fusebox in your Application.cfc. Look at Ray Camden's Application.cfc template for an example of setting application specific mappings.
You can run Fusebox 5+ in a subfolder of your app root. It just may not be the most obvious thing to make it work.
Unfortunately, you cannot create a dynamic mapping for extending Application.cfc because your Application.cfc has not yet been instantiated - you get a chicken vs. egg scenario. You can't create the mapping to Fusebox because your Application.cfc didn't start, you can't start your Application.cfc because it can't find the component it's supposed to extend.
THIS IS A BUG IN COLDFUSION 8. ColdFusion should look for mappings in this order:
Mapped folders from the CF Administrator
Sub directories off the current directory
Sub directories off the web root called
It does this when you use CreateObject(), but not when you use the Extends attribute on cfcomponent.
The easiest solution is to use your Application.cfc like you would for any application, then include fusebox from your index.cfm. Your folder structure would look like this:
/myapp/fusebox5/
/myapp/index.cfm
-- consists of <cfinclude template="fusebox5/fusebox5.cfm" />
Your index.cfm file will not be ignored as long as you don't intercept the request with Application.cfc's OnRequest, or if you use OnRequest, make sure you include the intended target (which will almost always be index.cfm anyway).
If you want to not require index.cfm to do the include, you can have your Application.cfc's OnRequest method do the cfinclude.
<cffunction name="onRequest">
<cfinclude template="fusebox5/fusebox5.cfm">
</cffunction>
You still may need an index.cfm so your web server won't give a directory listing or 404, but it's ok if the file is empty.
In Application.cfc:
<cfscript>
this.mappings = {}; //create a new structure to store app-specific mappings
this.mappings["Fusebox"] = expandPath('./Fusebox'); //add mapping
</cfscript>

Extend a CFC using a relative path

I want to extend a CFC in a different directory and I have a couple of options, but can't figure out how to do this:
A) Use a dynamic mapping (this will have to be dynamic based on the site, e.g. for the live site it would be cfc.myPackage.MyCFC but on a dev site it would be myCfcRoot.myPackage.MyCFC) - I've tried putting expressions into the extends bit but obviously CF doesn't like that, e.g. :
<cfcomponent name="MyComponent" extends="#config.cfcRoot#.BaseComponent">
or
<cfcomponent name="MyComponent" extends="#GetRealPath(../BaseComponent.cfc)#">
B) Provide a relative path (somehow) to the CFC to extend.
I fear that I can't do this, but I'm hoping that there is something I've missed.
Daniel is basically correct, you need a mapping. However, there are 3 workarounds.
CFCs will pick the current path as a relative root mapping, so if your CFCs are all in the same directory, you can just say
<cfcomponent name="MyComponent" extends="Example">
Or if your components are in subdirectories from the current cfc, you can access it:
<cfcomponent name="MyComponent" extends="subdirectory.Example">
Second, if you are running on ColdFusion 8, you can define a mapping in your application.cfc using the mappings struct like this:
<cfset this.mappings["/MyApp"] = expandPath(".") />
There are two good references for Application.cfc, first, Ray Camden's example Application.cfc which just gives a nice view of what goes where, then the CF8 Live Docs application settings page, which has a section on mappings along with some good comments.
Finally, you can use the built-in mapping of your web root, so if your application is in a subdirectory named "MyApp" off the web root, your root mapping will be "MyApp". Let's say you correctly put your components in:
wwwroot\MyApp\com\MyApp\example.cfc
The mapping to this cfc in this case will be:
MyApp.com.MyApp.Example
And using your example, you can extend like this:
<cfcomponent name="MyComponent" extends="MyApp.com.MyApp.Example">
Anything else, such as if your components are outside of the web root, or if you are not sure what the folder structure of your finished application will be, and you will need to set a mapping in the CF Administrator.
Now this is only tested in cf8 so other engine could differ.
if you want to use relative paths to extend applications you can but your have to start them with a "/.". For instance you can do this to extend an application.cfc from your web root into directory below you webroot:
<cfcomponent output="false" extends="/.application">
<!--- whatever code you have --->
</cfcomponent>
now let's say I have the following paths in my application:
[webroot]/1/1a
[webroot]/2
let's say that the application.cfc in [webroot]/1/1a extends the application.cfc in [webroot]. now I want to place an application.cfc in [webroot]/2 and extend the application.cfc in [webroot]/1/1a. all i would have to do in my [webroot]/2/application.cfc is the following:
<cfcomponent output="false" extends="/./1/1a/application">
<!--- whatever code you have --->
</cfcomponent>
hope this makes sense.
Unless the CFC is in the same directory as the calling script the CFC must be located and referenced from a path relative to a "Mapping".
I have found that sometimes you need to make sure that "/" is mapped to your document root of your webserver and that becomes the base for all relative paths. Or you can setup a mapping for a CFC directory that hold all of your common CFCs.
This screen is found in the ColdFusion Admin under "Server Settings" -> "Mappings".
In regards to Method B. It can be done using an additional file in your example. It's one extra file per cfc that you want to extend per directory that you want to extend from.
The 3 files that are required.
/somepathtobasecomponent/basecomponent.cfc (your base component file)
/pathtoworkingcfc/function.cfc (the file with the extends attribute)
/pathtoworkingcfc/basecomponent_extend.cfc (the new file that will do the extending)
/somepathtobasecomponent/basecomponent.cfc
Nothing to change here. This stays the same.
/pathtoworkingcfc/function.cfc
in the cffunction tag set the extends to extends="basecomponent_extend"
/pathtoworkingcfc/basecomponent_extend.cfc
Sole content of the file is a cfinclude
<cfinclude template="/somepathtobasecomponent/basecomponent.cfc">