Crazy Fun with CFCs and Functions: StructToBean()

This totally defeats all purposes of OO encapsulation, and a variety of other principles, but it was a fun time to play around with.

Basically, i wanted to see if I could turn a Struct into a Bean object on the fly. Strangely enough, it was pretty simple considering the flexibility you have with functions and CFCs.

The code works out like follows:

<cfscript>
 //helper functions
 function identityHashCode(object)
 {
  var system = createObject("java", "java.lang.System");
  return system.identityHashCode(arguments.object);
 }

 function setInstance(instance)
 {
  variables.instance = arguments.instance;
 }
</cfscript>

<cffunction name="structToObject" hint="Converts a structure to a object with getters and setters" access="public" returntype="WEB-INF.cftags.component" output="false">
 <cfargument name="inputStruct" hint="Struct to convert" type="struct" required="Yes">

 <cfscript>
  var obj = createObject("component", "WEB-INF.cftags.component");
  var lKeys = StructKeyList(arguments.inputStruct);
  var hash = identityHashCode(arguments.inputStruct);
  var key = "";  var path = GetDirectoryFromPath(CGI.PATH_TRANSLATED);
  var nl = chr(10) & chr(13);
  var buffer = createObject("java", "java.lang.StringBuffer").init("<cfscript>#nl#");

  //move variable scope to inside the CFC
  obj.setInstance = setInstance;
  obj.setInstance(arguments.inputStruct);
  StructDelete(obj, "setInstance");
 </cfscript>

 <cfloop list="#lKeys#" index="key">
  <!--- write out the function to get and set --->
  <cfscript>
   buffer.append("function get#key#() { return instance.#key#; }#nl#");
   buffer.append("function set#key#(#key#) { instance.#key# = arguments.#key#; }#nl#");
  </cfscript>
 </cfloop>
 <cfscript>
  buffer.append("</cfscript>");
 </cfscript>
 
 <!--- make the file - use the hash to ensure uniqueness --->
 <cffile action="write" file="#path##hash#.functions" output="#buffer.toString()#">
 <!--- import the file --->
 <cfinclude template="#hash#.functions"> 
 <!--- delete the file --->  
 <cffile action="delete" file="#path##hash#.functions">
 
 <!---  add it to the object --->
 <cfloop list="#lKeys#" index="key">
  <cfscript>
   obj["get#key#"] = variables["get#key#"];
   obj["set#key#"] = variables["set#key#"];
  </cfscript>
 </cfloop>  
 <cfreturn obj>

</cffunction>

To test this out:

<!--- test code --->

<cfscript>
myStruct = StructNew();
myStruct.fred = "Fred";

obj = structToObject(myStruct);
</cfscript>

<cfoutput>
#obj.getFred()#
<hr>

<cfscript>
obj.setFred("blue");
</cfscript>

#obj.getFred()#
</cfoutput>

Essentially this function:,

  1. Creates a basic CFC (WEB-INF.cftags.component is the base CFC for all CFCs),
  2. Adds the ‘setInstance()’ function to the CFC, and sets the Struct to the instance data of the CFC with it,
  3. Removes ‘setInstance()’ function from the CFC,
  4. The function loops through the Struct Keys,
  5. It writes a series of getter and setter functions to an external file,
  6. Imports in the file, placing these functions into the variables scope,
  7. Deletes the file (cleanup!)
  8. Adds all these functions to the CFC,
  9. Returns the CFC.

And look at that, we have created a CFC at run time! Totally defeats some core OO concepts, but it was interesting to note that you can do it.

Leave a Comment