Programmatic Config Files and Automagic Configuration
Something I was getting really tired of in my framework was that every time I added a new feature (controller) or custom data type, not only did I need to write the CFC, but I also had to add the bean definition to my config file. With the programmatic config file in LightWire, I was able to write a script to automagically instantiate and inject all of the custom data types and features by looking at all of the CFC's in the appropriate directories . . .
I start by getting a list of all of the CFC's in a given directory (so I can implement this as a generalized solution):
<cffunction name="getDistinctObjectList" returntype="string" hint="I return a distinct list of objects in a given directory for both an application and the framework." output="false">
<cfargument name="FileDotPath" required="true" type="string" hint="The file path relative to the application/framework root with dots as directory delimiters.">
<cfset var ObjectList = "">
<cfset var ApplicationObjectList = "">
<cfset var FrameworkDirectoryPath = variables.LightBaseDirectoryPath & variables.DirectoryDelimiter & "framework" & variables.DirectoryDelimiter & Replace(FileDotPath, ".", variables.DirectoryDelimiter, "all")>
<cfset var ApplicationDirectoryPath = variables.ApplicationDirectoryPath & variables.DirectoryDelimiter & Replace(FileDotPath, ".", variables.DirectoryDelimiter, "all")>
<cfdirectory action="list" directory="#FrameworkDirectoryPath#" name="ObjectList">
<cfset ObjectList = ReplaceNoCase(valuelist(ObjectList.name), ".cfc","","all")>
<cfdirectory action="list" directory="#FrameworkDirectoryPath#" name="ApplicationObjectList">
<cfoutput query="ApplicationObjectList">
<cfif NOT ListFindNoCase(ObjectList, Name)>
<cfset ObjectList = ListAppend(ObjectList, Name)>
</cfif>
</cfoutput>
<cfset ObjectList = ReplaceNoCase(ObjectList, ".cfc","","all")>
<cfreturn ObjectList>
</cffunction>
<cfargument name="FileDotPath" required="true" type="string" hint="The file path relative to the application/framework root with dots as directory delimiters.">
<cfset var ObjectList = "">
<cfset var ApplicationObjectList = "">
<cfset var FrameworkDirectoryPath = variables.LightBaseDirectoryPath & variables.DirectoryDelimiter & "framework" & variables.DirectoryDelimiter & Replace(FileDotPath, ".", variables.DirectoryDelimiter, "all")>
<cfset var ApplicationDirectoryPath = variables.ApplicationDirectoryPath & variables.DirectoryDelimiter & Replace(FileDotPath, ".", variables.DirectoryDelimiter, "all")>
<cfdirectory action="list" directory="#FrameworkDirectoryPath#" name="ObjectList">
<cfset ObjectList = ReplaceNoCase(valuelist(ObjectList.name), ".cfc","","all")>
<cfdirectory action="list" directory="#FrameworkDirectoryPath#" name="ApplicationObjectList">
<cfoutput query="ApplicationObjectList">
<cfif NOT ListFindNoCase(ObjectList, Name)>
<cfset ObjectList = ListAppend(ObjectList, Name)>
</cfif>
</cfoutput>
<cfset ObjectList = ReplaceNoCase(ObjectList, ".cfc","","all")>
<cfreturn ObjectList>
</cffunction>
Then to implement this for a data types, I do the following:
<cfscript>
var DataTypeList = "";
var DataTypeName = "";
Project = CreateObject("component","lightbase.applications.#Application.name#.config.ApplicationConfig").init();
// DATA TYPES
DataTypeList = getDistinctObjectList("com.datatype.types");
// Data type (facade) addSingleton("lightbase.framework.com.datatype.DataType");
addMixinProperty("DataType","DataTypeList",DataTypeList);
// All of the data type singletons
For (ConfigCount = 1; ConfigCount lte listlen(DataTypeList); ConfigCount = ConfigCount + 1)
{
// Get current object name DataTypeName = ListGetAt(DataTypeList, ConfigCount);
If (FileExistsinApplication("com.datatype.types.#DataTypeName#"))
{addSingleton("lightbase.applications.#Application.Name#.com.datatype.types.#DataTypeName#","#DataTypeName#DataType");}
Else
{addSingleton("lightbase.framework.com.datatype.types.#DataTypeName#","#DataTypeName#DataType");};
// Inject app config and value list service to all data types addConstructorDependency("#DataTypeName#DataType", "ApplicationConfig");
addMixinDependency("#DataTypeName#DataType", "ValueListService");
// And mix all data types into the data type service addMixinDependency("DataType", "#DataTypeName#DataType");
};
</cfscript>
var DataTypeList = "";
var DataTypeName = "";
Project = CreateObject("component","lightbase.applications.#Application.name#.config.ApplicationConfig").init();
// DATA TYPES
DataTypeList = getDistinctObjectList("com.datatype.types");
// Data type (facade) addSingleton("lightbase.framework.com.datatype.DataType");
addMixinProperty("DataType","DataTypeList",DataTypeList);
// All of the data type singletons
For (ConfigCount = 1; ConfigCount lte listlen(DataTypeList); ConfigCount = ConfigCount + 1)
{
// Get current object name DataTypeName = ListGetAt(DataTypeList, ConfigCount);
If (FileExistsinApplication("com.datatype.types.#DataTypeName#"))
{addSingleton("lightbase.applications.#Application.Name#.com.datatype.types.#DataTypeName#","#DataTypeName#DataType");}
Else
{addSingleton("lightbase.framework.com.datatype.types.#DataTypeName#","#DataTypeName#DataType");};
// Inject app config and value list service to all data types addConstructorDependency("#DataTypeName#DataType", "ApplicationConfig");
addMixinDependency("#DataTypeName#DataType", "ValueListService");
// And mix all data types into the data type service addMixinDependency("DataType", "#DataTypeName#DataType");
};
</cfscript>
I've left in a bunch of additional bits of code to show a real world example rather than something completely stripeped down, but hopefully the sample is still simple enough to show that anyone can start writing their own little automagic scripts - just beware - as with c++, you can "blow your whole darned leg off".
Did I mention that I *like* programmatic config files for Dependency Injection :->
Thoughts?



There are no comments for this entry.
[Add Comment]