onMissingMethod and Abstract Objects

I have finally got around to working a bit more on the fusebox scaffolder project. One of the things I have meaning to do was improve the generated code by using an abstract bean and letting generated beans inherit some base methods. I intend to do this for most of the generated cfcs, but just started with the bean as a first step.

I have not in the past used onMissingMethod but now enough of what I am doing is on CF8 that I decided it was worth incorporating in the generated code. I began by reading up about it to see what others had done.

There was certainly lots of ideas out there and I decided to take John Whish's dynamicAccessors.cfc and take it a bit further.

John's idea was to make use of the <cfproperty> tag to set up the properties in a cfc and then to make use of the onMissingMethod in a base cfc to create dynamic accessors. I really like this idea but I also like Peter Bell's idea for controlling the access to the getters and setters through a list of property names.

I was wondering about combining the two ideas and remembered somthing I discovered in the early days of CFCs. You can actaully create your own attributes on many of the CF tags just by adding them to the tag.

So I can write:

<cfproperty name="title" type="string" settable="true" gettable="true"/>
and ColdFusion will ignore my two new attributes in most respects. What it will do is add them to the metadata so I can read them. This allows me to extend John's getAccessors() method to update two new structures one for the setters and one for the getters.

<cffunction name="getAccessors"
         hint="I build a structure of public properties and data types."
         access="public">

      <cfset var metadata = "" />
      <cfset var properties = arrayNew(1) />
      <cfset var index = "" />
         
      <cfif StructKeyExists( variables.instance, "_properties" )
         AND StructKeyExists( variables.instance, "_getable" )
         AND StructKeyExists( variables.instance, "_setable" )
         AND IsStruct( variables.instance._properties )
         AND IsStruct( variables.instance._gettable )
         AND IsStruct( variables.instance._settable )>

         
         <cfdump var="#variables.instance._properties#"/>
         <cfdump var="#variables.instance._gettable#"/>
         <cfdump var="#variables.instance._settable#"/>
         <cfabort/>
         
         <cfreturn variables.instance._properties />
      <cfelse>
         <!--- The first time we get or set a property we need to build a structure of properties and types --->
         <cfset variables.instance._properties = {} />
         <cfset variables.instance._gettable = {} />
         <cfset variables.instance._settable = {} />
         
         <cfset metadata = getMetaData(this)>
         
         <cfloop condition="structKeyExists(metadata,'properties') OR structKeyExists(metadata,'extends')">
            <cfif structKeyExists(metadata,"properties")>
               <cfset properties = metadata.properties />
               <cfloop array="#properties#" index="index">
                  <cfif IsDefined("index.type")>
                     <cfset StructInsert( variables.instance._properties, index.name, index.type ) />
                  <cfelse>
                     <cfset StructInsert( variables.instance._properties, index.name, "any" ) />
                  </cfif>
                  <cfif IsDefined("index.getable")>
                     <cfset StructInsert( variables.instance._gettable, index.name, index.getable ) />
                  <cfelse>
                     <cfset StructInsert( variables.instance._gettable, index.name, "false" ) />
                  </cfif>
                  <cfif IsDefined("index.setable")>
                     <cfset StructInsert( variables.instance._settable, index.name, index.setable ) />
                  <cfelse>
                     <cfset StructInsert( variables.instance._settable, index.name, "false" ) />
                  </cfif>
               </cfloop>
            </cfif>
            <cfif structKeyExists(metadata,"extends")>
               <cfset metadata = metadata.extends>
            <cfelse>
               <cfset metadata = "">
            </cfif>
         </cfloop>
         
         <cfreturn variables.instance._properties />   
      </cfif>
         
   </cffunction>
I now have two new structures in my object which I can use to test if the generated code should allow you to set or get each property.

A small change to the onMissingMethod() function and I can now control who has access quite easily.

What I really want to know is: Am I mad to do this? After all I am arbitarily extending the language and I didn't ask anyones permission.

27Jan 16:25 GMT - Modified to fix a couple of typos.

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
John Whish's Gravatar Hi Kevin, glad you liked what I was doing. Controlling access to accessors is a nice touch. I'm a big fan of using metadata (I even use it for basic form validation!)
# Posted By John Whish | 1/27/09 9:57 AM
Peter Bell's Gravatar Hi Kevin,

I think this is a great approach and it's the direction I'm moving towards in CF as it seems to fit with where CF9 is going in terms of making CF Property the definitive source for metadata. The main practical issue is that you may need to rename some of your metadata for CF9 if you end up using the same names as Centaur uses for some of these things, but quite frankly, a lot of this work is going to be moot in CF 9 as I'm pretty sure there has been public talk about adding ideas like implicit getters and setters, so I think this is mainly going to be a patch for CF8 until CF9 comes out.

I definitely think this is the way to go though.
# Posted By Peter Bell | 1/27/09 1:18 PM
John Whish's Gravatar Good point about cfproperty attributes names in CF9. It will be interesting to see if the ideas like implicit getters and setters make it to CF9!

It's good to know that I'm heading down a similar path to you both. What is it they say, "all great minds thing alike, and fools seldom differ!" *grin*
# Posted By John Whish | 1/27/09 1:47 PM
Kevin Roche's Gravatar I have just posted an update on this with improved code http://is.gd/hptE
# Posted By Kevin Roche | 1/27/09 4:47 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.8.001.