Wednesday, June 18, 2008

Getters/Setters Redux

There has been a lot of news about some of the forthcoming additions to CF9 and they all sound great! All the SysCon media and OpenBlueDragon chatter has been really tiresome. I'm glad that we're getting to hear about "neat" and "cool" stuff from Adobe again.

One of the features that I've heard is planned for cf9 is implicit getters and setters. I imagine those will be along similar lines to the ones in as3, but I don't know any more than you at this point. That sounds like it will save a lot of keystrokes for lots of us and I'm looking forward to it. What I'm not looking forward to is knowing that it's not going to be out until sometime in 2009. So, to help ease that pain a little, here's a little code I have running that sort of does that for basic value object like components. Just extend it to provide get/set/val/do functions for each property you declare with cfproperty. All of the methods can be overridden when needed, and in some cases you should override them.

<cfcomponent hint="Extension point for Value Objects" output="false">

<cffunction name="onMissingMethod" output="false">

<!--- define arguments --->
<cfargument name="missingMethodName" type="string" />
<cfargument name="missingMethodArguments" type="struct" />

<cfset var name = "" />
<cfset var propList = ""/>
<cfset var private = structNew() />

Build a list of properties from the cfproperty tags in the
extending object. We will use this list to determine if
the properties belong to the object
<cfset md = getMetaData( this )>
<cfset foundMethod = structFindValue( md , arguments.missingMethodName , "all")>
<cfset propArray =>

<cfloop from="1" to="#arrayLen(propArray)#" index="item">
<cfset propList = listAppend(propList , propArray[item].name , ",")>

Getters/Accessors - examine the missing method that was called to see
if it began with "get". If it does, then we are going to return the
value of the property that matches the rest of the method name
<cfif left(missingMethodName,3) is "get">
<cfset name = right( missingMethodName , len(missingMethodName) - 3 ) />
<cfif listFindNoCase( propList , name )>
<cfreturn variables.instance[name] />
<cfthrow type="ValueObject" message="Property #name# is not defined in component"/>

<!--- Setters --->
<cfelseif left(missingMethodName,3) is "set">
<cfset name = right( missingMethodName , len(missingMethodName) - 3 ) />
<cfif structIsEmpty(missingMethodArguments)>
<cfthrow type="ValueObject" message="The #uCase(name)# parameter to the get#name#
function is required but was not passed in."/>
<cfelseif NOT listfindNoCase( propList,name )>
<cfthrow type="ValueObject" message="The set#name#() method of the component is not defined."/>
<cfset evaluate( "this.val#name#('" & missingMethodArguments[1] & "')" )>
<cfset evaluate( "'" & missingMethodArguments[1] & "')" )>

<!--- Doers --->
<cfelseif left( missingMethodName , 2) is "do">
<cfset name = right( missingMethodName , len(missingMethodName) - 2 ) />
<cfset variables.instance[name] = missingMethodArguments[1] />
Business Rule place holder
if this method is not overriden in your component for each property,
no business rules or format enforcement is taking place
<cfelseif left(missingMethodName,3) is "val">
<!--- This should be overriden in your component --->
<cfset name = right( missingMethodName , len(missingMethodName) - 3 ) />

<!--- Print --->
<cfelseif missingMethodName is "Print">

<cfreturn variables.instance />

The isEqual method here is not detecting if you have two handles
on the same object. It's actually checking to see if the
properties are the same on the two objects.
<cfelseif missingMethodName is "isEqual">
<!--- serialize the attributes of the first object --->
action = "cfml2wddx"
input = "#this.print()#"
output = "private.obj"

<!--- serialize the attributes of the second object --->
action = "cfml2wddx"
input = "#missingMethodArguments.obj.print()#"
output = "private.obj2"

<!--- Test for equality --->
<cfif private.obj eq private.obj2>
<cfreturn true />
<cfreturn false />
type = "ValueObject"
message = "The method #missingMethodName# was not found in component
#expandPath('/' & replace(,'.','/','all'))#"
detail = "Make sure that you have defined the property using cfproperty."

No comments:

Post a Comment