Debugging an object's state
I'm currently working on a project that uses a series of classes that make heavy use of inheritance. What I wanted to be able to do was see all the public methods available to me without having to dig down through the inheritance tree. In addition, I wanted to know what the values available via accessors at any given moment were.
I'm sure that someone must have already done this, but my 5 mins of searching didn't give me anything so I decided to write my own!
Introspection.cfc
<!---
=================================================================================
Last changed by: $Author: john.whish $
Last changed date: $Date: 2009-01-14 15:34:09 +0000 (Wed, 14 Jan 2009) $
=================================================================================
Example Usage (of a myObject instance of the Foobar class):
---------------------------------------------------------------------------------
<cfset myObject = CreateObject('component', 'Foobar').init( myArgs ) />
<cfset debug = CreateObject('component', 'Introspection').init( myObject ) />
<cfdump var="#debug.getState()#" label="State" />
<cfdump var="#debug.getPublicMethods()#" label="Public Methods" />
=================================================================================
--->
<cfcomponent output="false" hint="I introspect an object">
<!---
-------------------------------------------------------------------------------
Constructor
-------------------------------------------------------------------------------
---->
<cffunction name="init"
access="public"
output="false">
<cfargument name="object" type="any" required="true" hint="A class instance" />
<cfset setObject( arguments.object ) />
<cfreturn this />
</cffunction>
<!---
-------------------------------------------------------------------------------
Public Accessors & Mutators
-------------------------------------------------------------------------------
---->
<cffunction name="getState"
access="public"
output="false"
returntype="struct"
hint="I return a structure of accessor methods that the current value">
<cfset var publicmethods = getPublicMethods( getObject() ) />
<cfset var method = "" />
<cfset var returnedValue = "" />
<cfset var methodvalues = {} />
<cfloop collection="#publicmethods#" item="method">
<cfif StructKeyExists( publicmethods[ method ], "returntype" )
AND publicmethods[ method ].returntype NEQ "void"
AND method neq "init">
<cftry>
<cfinvoke component="#variables.instance.object#"
method="#method#"
returnvariable="returnedValue" />
<cfif StructKeyExists( methodvalues, method )>
<cfabort showerror="#method# exists!" />
</cfif>
<cfif IsObject( returnedValue )>
<cfset methodvalues[ method ] = "OBJECT => " & publicmethods[ method ].returntype />
<cfelse>
<cfset methodvalues[ method ] = returnedValue />
</cfif>
<cfcatch>
<cfset methodvalues[ method ] = "ERROR! => " & cfcatch.Message />
<cfif ArrayLen( publicmethods[ method ].parameters ) NEQ 0>
<cfset methodvalues[ method ] &= " The " & method & "() method expects " & ArrayLen( publicmethods[ method ].parameters ) & " argument(s)" />
</cfif>
</cfcatch>
</cftry>
</cfif>
</cfloop>
<cfreturn methodvalues />
</cffunction>
<cffunction
name="getPublicMethods"
access="public"
output="false"
returntype="struct"
hint="I return a structure of all the public methods in the class and superclasses">
<cfargument name="object" type="any" required="false" default="#variables.instance.object#" />
<cfset var method = "" />
<cfset var publicmethods = {} />
<cfset var classmethods = {} />
<cfset var currentClassMetaData = GetMetaData( arguments.object ) />
<!--- loop through all classes and superclasses --->
<cfloop condition="True">
<cfset StructAppend( classmethods, getClassMethods( currentClassMetaData ), False ) />
<cfif NOT StructKeyExists( currentClassMetaData, 'extends' )>
<cfbreak />
<cfelse>
<cfset currentClassMetaData = currentClassMetaData.extends />
</cfif>
</cfloop>
<cfloop collection="#classmethods#" item="method">
<cfif classmethods[ method ].access EQ "public">
<cfset publicmethods[ method ] = classmethods[ method ] />
</cfif>
</cfloop>
<cfreturn publicmethods />
</cffunction>
<!---
-------------------------------------------------------------------------------
Private Accessors & Mutators
-------------------------------------------------------------------------------
---->
<cffunction name="setObject"
access="private"
output="false"
returntype="void">
<cfargument name="theObject" type="any" required="true" />
<cfif IsObject( arguments.theObject )>
<cfset variables.instance.object = arguments.theObject />
<cfelse>
<cfthrow type="exception" message="You must pass an object to the init method!" />
</cfif>
</cffunction>
<cffunction name="getObject"
access="private"
output="false"
returntype="any">
<cfreturn variables.instance.object />
</cffunction>
<cffunction name="getClassMethods"
access="private"
output="false"
returntype="struct">
<cfargument name="classmetadata" type="any" required="true" hint="metadata for the class" />
<cfset var index = "" />
<cfset var classmethods = {} />
<cfif StructKeyExists( arguments.classmetadata, "functions" )>
<cftry>
<cfloop from="1" to="#ArrayLen( arguments.classmetadata.functions )#" index="index">
<cfset classmethods[ arguments.classmetadata.functions[ index ].name ] = arguments.classmetadata.functions[ index ] />
</cfloop>
<cfcatch>
<cfdump var="#arguments.classmetadata#" />
<cfabort />
</cfcatch>
</cftry>
</cfif>
<cfreturn classmethods />
</cffunction>
</cfcomponent>
It isn't perfect, and obviously can't handle dynamic methods (created with onMissingMethod), but it has helped me, so hopefully it may help you (let me know!).
- Posted in:
- ColdFusion

