onMissingMethod and Abstract Object - Part 2
I just did a bit more work on the onMissingMethod handler in my abstract bean cfc.
In case you are interested here are the differences between my implementation and John Whish's original which it was based on.
- I have improved the code compared to the earlier post.
- I have made the code work when you have multiple levels of CFC with one extending another. (John's original code only worked at the first level.
- I have implemented the checking of what is gettable and settable.
- I have removed the type checking that John put in, as I prefer to be able to load everything from a form into my bean and call validate to find out what is invalid. (You can always put John's checking back.)
hint="I act as a generic getter and setter for the class properties."
access="public"
description="Usage getXYZ() or setXYZ(value) where XYZ is the property name"
returntype="any">
<cfargument name="missingMethodName" type="string" />
<cfargument name="missingMethodArguments" type="struct" />
<cfset var propertyName = "" />
<cfset getAccessors() />
<cfswitch expression="#Left( arguments.missingMethodName, 3 )#">
<cfcase value="get">
<cfset propertyName = ReplaceNoCase( arguments.missingMethodName, "get", "" ) />
<cfif StructKeyExists( variables.instance._properties, propertyName ) AND variables.instance._properties[propertyName].gettable>
<cfreturn variables.instance[ propertyName ] />
<cfelseif StructKeyExists( variables.instance._properties, propertyName )>
<cfthrow message="Property '#propertyName#' is not marked gettable. Cannot get value." />
<cfelse>
<cfthrow message="Unknown property '#propertyName#'. Cannot get value." />
</cfif>
</cfcase>
<cfcase value="set">
<!--- I do not validate the type as I want to be able to save any string here and validate it later --->
<cfset propertyName = ReplaceNoCase( arguments.missingMethodName, "set", "" ) />
<cfif StructKeyExists( variables.instance._properties, propertyName ) AND variables.instance._properties[propertyName].settable>
<cfset variables.instance[ propertyName ] = arguments.missingMethodArguments[1] />
<cfelseif StructKeyExists( variables.instance._properties, propertyName )>
<cfthrow message="Property '#propertyName#' is not marked settable. Cannot set value." />
<cfelse>
<cfthrow message="Unknown property '#propertyName#'. Cannot set value." />
</cfif>
</cfcase>
<cfdefaultcase>
<cfthrow message="Unknown method." />
</cfdefaultcase>
</cfswitch>
</cffunction>
hint="I build a structure of public properties, data types and if the property is gettable aor settable."
access="public">
<cfset var metadata = "" />
<cfset var properties = arrayNew(1) />
<cfset var index = "" />
<cfif StructKeyExists( variables.instance, "_properties" ) AND IsStruct( variables.instance._properties )>
<cfreturn variables.instance._properties />
<cfelse>
<!--- The first time we get or set a property we need to build a structure of properties --->
<cfset variables.instance._properties = {} />
<cfset metadata = getMetaData(this)>
<!--- Loop through the component hierarchy --->
<cfloop condition="structKeyExists(metadata,'properties') OR structKeyExists(metadata,'extends')">
<cfif structKeyExists(metadata,"properties")>
<cfset properties = metadata.properties />
<cfloop array="#properties#" index="index">
<cfset variables.instance._properties[index.name] = structNew()/>
<cfif IsDefined("index.type")>
<cfset variables.instance._properties[index.name].type = index.type/>
<cfelse>
<cfset variables.instance._properties[index.name].type = "any" />
</cfif>
<cfif IsDefined("index.gettable")>
<cfset variables.instance._properties[index.name].gettable = iif(index.gettable,"true","false")/>
<cfelse>
<cfset variables.instance._properties[index.name].gettable = "false" />
</cfif>
<cfif IsDefined("index.settable")>
<cfset variables.instance._properties[index.name].settable = iif(index.settable,"true","false")/>
<cfelse>
<cfset variables.instance._properties[index.name].settable = "false"/>
</cfif>
</cfloop>
</cfif>
<!--- Check if we need to go to the next level of hierarchy --->
<cfif structKeyExists(metadata,"extends")>
<cfset metadata = metadata.extends>
<cfelse>
<cfset metadata = "">
</cfif>
</cfloop>
<cfreturn variables.instance._properties />
</cfif>
</cffunction>
To use it you will need to add the extra attributes to the properties tags:
<cfproperty name="createdBy" type="numeric" gettable="true" settable="false" />
Please let me know if you find it useful.



This is a great idea and I'll start using this approach until CF9 strikes!
Thanks for sharing.