Using Constants in ColdFusion Components
In code we often need to make use of constant values. It is good practice to use named constants rather than literal values, but what is a good technique for managing constants in an object oriented ColdFusion application?
One technique is to break your constants down into separate files that relate to their particular area. For example you might have a set of files:
/constants/global.cfm
/constants/products.cfm
/constants/users.cfm
Etc.
Inside each file you would define the constants for that area. For example, for the products.cfm file you may write:
// Define the product constant namespace.
structGet("constant.product");
// Product status constants.
constant.product.STATUS_ACTIVE = 1;
constant.product.STATUS_INACTIVE = 2;
constant.product.STATUS_ARCHIVED = 3;
// Other product related constants here, etc.
constant.product.IS_FEATURE = 1;
constant.product.IS_SPECIAL = 2;
</cfscript>
Here you can use the structGet() function to create a "namespace" for your constants. This means that if you have identical constant names in different areas then they will not overlap.
Then, wherever you need to use the constants you can include these directly into your components:
<cfinclude template="/constants/global.cfm">
<cfinclude template="/constants/products.cfm">
<!--- Functions here... --->
</cfcomponent>
This would cause the constants to all be loaded into the component's variables scope. You could them use them in a function as follows:
<cfreturn getStatusId() eq constant.product.STATUS_ACTIVE>
</cffunction>
This does not appear to be a particularly object oriented technique but seems a little nicer that some of the alternatives, such as referencing an external scope (eg constants stored in the application scope) or passing/injecting a "Constants" object into other objects that require it.
What do you think? How do you handle constants in ColdFusion components?

I have ColdSpring inject them, normaly from a Map in the ColdSpring XML though if their more complicated or user-dependant I'll do a fill blown 'EnvironmentService' that can be injected into all the components.
I originally started off with a ColdSpring bean of constants, but when working on a ColdSpring / Transfer application I could not see a simple way to get my constants into Transfer Decorators, so I switched to cfm's.
However, just now found Brian Kotek's Transfer bean injector:
http://www.briankotek.com/blog/index.cfm/2008/1/14...
Thanks Brian!
<bean id="appConstants" class="coldspring.beans.factory.config.MapFactoryBean">
<property name="sourceMap">
<map>
<entry key="product">
<map>
<entry key="STATUS_ACTIVE"><value>1</value></entry>
<entry key="STATUS_INACTIVE"><value>2</value></entry>
<entry key="STATUS_ARCHIVED"><value>3</value></entry>
<entry key="user">
<map>
<entry key="STATUS_ARCHIVED"><value>1</value></entry>
<entry key="ADMIN_USER_ID"><value>1</value></entry>
</map>
</entry>
</map>
</entry>
<entry key="user">
<map>
<entry key="STATUS_ARCHIVED"><value>1</value></entry>
<entry key="ADMIN_USER_ID"><value>1</value></entry>
</map>
</entry>
</map>
</property>
</bean>
Then using code such as:
<cfset constants = factory.getBean("appConstants")>
You get a nested set of structures that may be used as constants.
I also noticed that you cannot use a bean with id="constants". This causes a bean creation exception.
The frameworks often have the problem of performance; a small request will mostly take 500 - 700ms, which is caused by many, many cfc for framework-startup...
the glory way is not always the same... everyone has to check, which kind of app is used in which way.... a cms often has other requirements than a gui of a management interface, used inside a company...
Yes, certainly something like ColdSpring may not be for everybody.
However I now quite like the idea of storing constants in a single component that lives in the application scope. As it is only a single component created at application startup, it should have no practical impact on performance.
So in a non-framework or simplified application, you could have:
On application start, create some service that provides access to constants. When this object is created, the constants would be created at the same time.
<cfset application.appService = createObject("component","com.AppService").init()>
And within the application code, you would make a call such as:
<cfset constants = application.appService.getConstants()>
that would return a struct containing the application constants.
See my comments at:
[URL="http://www.bennadel.com/blog/1776-Creating-Globall...;]Creating Globally Accessible User Defined Functions In ColdFusion (Safer Version)[/URL]
Here was the final solution:
[Put in application.cfc:]
[CODE]
<cffunction name="onApplicationStart">
. . . snip . . .
<cfscript>
application.globalmethods = structnew();
keys = structkeylist(this);
for (i=1; i LTE listlen(keys); i=i+1) {
key = listgetat(keys,i);
if (iscustomfunction(this[key]) and left(lcase(key),2) NEQ "on")
structinsert(application.globalmethods, key, this[key]);
}
application.registerglobalmethods = this.registerglobalmethods;
</cfscript>
. . . snip . . .
</cffunction>
<cfinclude template="GlobalFunctions.cfm">
<cffunction name="registerglobalmethods" output="no">
<cfset StructAppend(variables, application.globalmethods, true)>
</cffunction>
[/CODE]
Finally, in any cfc that uses a global function, call the register function, then you can use any global function (with no scope prefix):
[CODE]<cfset application.registerglobalmethods();>[/CODE]
Now, you could skip the register function and just use:
[CODE]<cfset StructAppend(variables, application.globalmethods, true)>[/CODE]
when ever a cfc needed to use global functions. But I think this is more readable.
for ( key in this ) ...