Posted in
ColdFusion
| Posted on 08-05-2007
| 3,332 views
Tonight I played around with onMissingMethod, a cool new feature in ColdFusion 8. See Ben Nadel's entry
What I didn't realize and was surprised by - you can't rename these arguments. So consider this:
ColdFISH is developed by Jason Delmore. Source code and license information available at coldfish.riaforge.org
<cffunction name="onMissingMethod" access="public" returnType="any" output="false">
<cfargument name="method" type="string" required="true">
<cfargument name="args" type="struct" required="true">
1<cffunction name="onMissingMethod" access="public" returnType="any" output="false">
2 <cfargument name="method" type="string" required="true">
3 <cfargument name="args" type="struct" required="true">
While the method itself will run, the arguments above will not actually be used. You must name them as Ben describe's in his entry:
ColdFISH is developed by Jason Delmore. Source code and license information available at coldfish.riaforge.org
<cffunction name="onMissingMethod" access="public" returnType="any" output="false">
<cfargument name="missingMethodName" type="string" required="true">
<cfargument name="missingMethodArguments" type="struct" required="true">
1<cffunction name="onMissingMethod" access="public" returnType="any" output="false">
2 <cfargument name="missingMethodName" type="string" required="true">
3 <cfargument name="missingMethodArguments" type="struct" required="true">
As a side note - for the life of me I couldn't find any mention of onMissingMethod in the docs - neither the Developer's Guide nor the Reference. Can anyone else find it?
This is the code I'm using in a generic bean:
ColdFISH is developed by Jason Delmore. Source code and license information available at coldfish.riaforge.org
<cfcomponent name="Core Bean" output="false">
<cffunction name="onMissingMethod" access="public" returnType="any" output="false">
<cfargument name="missingMethodName" type="string" required="true">
<cfargument name="missingMethodArguments" type="struct" required="true">
<cfset var key = "">
<cfif find("get", arguments.missingMethodName) is 1>
<cfset key = replaceNoCase(arguments.missingMethodName,"get","")>
<cfif structKeyExists(variables, key)>
<cfreturn variables[key]>
</cfif>
</cfif>
<cfif find("set", arguments.missingMethodName) is 1>
<cfset key = replaceNoCase(arguments.missingMethodName,"set","")>
<cfif structKeyExists(arguments.missingMethodArguments, key)>
<cfset variables[key] = arguments.missingMethodArguments[key]>
</cfif>
</cfif>
</cffunction>
</cfcomponent>
1<cfcomponent name="Core Bean" output="false">
2
3<cffunction name="onMissingMethod" access="public" returnType="any" output="false">
4 <cfargument name="missingMethodName" type="string" required="true">
5 <cfargument name="missingMethodArguments" type="struct" required="true">
6 <cfset var key = "">
7
8 <cfif find("get", arguments.missingMethodName) is 1>
9 <cfset key = replaceNoCase(arguments.missingMethodName,"get","")>
10 <cfif structKeyExists(variables, key)>
11 <cfreturn variables[key]>
12 </cfif>
13 </cfif>
14
15 <cfif find("set", arguments.missingMethodName) is 1>
16 <cfset key = replaceNoCase(arguments.missingMethodName,"set","")>
17 <cfif structKeyExists(arguments.missingMethodArguments, key)>
18 <cfset variables[key] = arguments.missingMethodArguments[key]>
19 </cfif>
20 </cfif>
21
22</cffunction>
23
24</cfcomponent>
This let's me define a simple bean like so:
ColdFISH is developed by Jason Delmore. Source code and license information available at coldfish.riaforge.org
<cfcomponent output="false" extends="bean">
<cfset variables.id = "">
<cfset variables.username = "">
<cfset variables.password = "">
<cfset variables.name = "">
<cfset variables.email = "">
</cfcomponent>
1<cfcomponent output="false" extends="bean">
2
3<cfset variables.id = "">
4<cfset variables.username = "">
5<cfset variables.password = "">
6<cfset variables.name = "">
7<cfset variables.email = "">
8
9</cfcomponent>
You also need to be careful about the missingMethodArguments data structure. If you don't use named arguments in the call, it will just contain positional arguments - i.e., arguments named "1" and "2" etc so arguments.missingMethodArguments[key] will not work. For the setter, you'll need to reference arguments.missingMethodArguments[1] if arguments.missingMethodArguments[key] is not defined.
CFCs support an onMissingMethod function. By defining an onMissingMethod function in the cfcomponent tag
body in the CFC, you can handle calls to methods that are not implemented in the CFC. If an application calls a
function that is not defined in the CFC, ColdFusion calls the onMissingMethod function and passes it the requested
method’s name and arguments. If you do not define an onMissingMethod function, a call to a method that is not
defined in the CFC causes ColdFusion to throw an error that must be handled in the calling code.
The onMissingMethod function is useful for several purposes:
• To handle errors directly in the component, instead of requiring that each instance of code that calls the
component handles them.
• To create a dynamic proxy, an object that can take arbitrary calls and dynamically determines the correct action.
The onMissingMethod function must have the following format:
<cffunction name="onMissingMethod">
<cfargument name="missingMethodName" type="string">
<cfargument name="missingMethodNameArguments" type="struct">
code to handle call to nonexistent method
</cffunction>
Good to see the get/set example. That's one of the classic examples I like to give - I'll be writing a lot more about onMissingMethod() in due course :)
Without the code, you're more in the dark. Is it getCustomerNumber() or getCustomerID()?Is it getArticleName() or getArticleTitle()?
Or do I need to write a documentation function that checks the field list and generates virtual documentation for virtual functions?
Not to mention that with a code generator there's an existing function template to copy and/or modify as needed.
I'm a bit surprised at your example method names - I think it speaks volumes about the sort of object models you work with.
In my world, I'd see getCustomer().getId() and getArticle().getTitle() and, frankly, those are more obvious names (you wouldn't really have customer.getCustomerNumber() and article.getArticleTitle() surely?).
I mean, I assume it's okay for an order object's method to have a getter for foreign keys? Of course, I could have order.getCustomer().getID(), but loading a new object and the accompanying database hit seems like overkill for the times when all I need is the ID (or is it the number?) (too bad I can't do a cfdump on the object and find out).
At any rate, like I said one loses the introspective self-documenting aspect and, like I said, I have mixed emotions that the tradeoffs are worth it. Perhaps, when you get around to discussing "quite a number of things really" in a future post you could elaborate and, in the process, convince those of us who may be on the fence.
And stop dissin' my function names. (grin)
@Sean1- No reason. I just thought of find first. As for your second para, when I write beans, I normally write my set like so
<cffunction name="setfoo" ....>
<cfargument name="foo" ...
So my arg for setX is always X.
@Sean2 - Ug. So get this. When I search just for onMissingMethod, it isn't found. Let me guess - the PDF search ignores it because it is code? Or in a different format? I had to search for another block of text from your paste to find it.
Now tell me this Sean, from that doc, you read that the arguments must be named like that? It says "the following format", but I don't think that is specific enough. Look at App.cfc - while you have to follow the format, you _can_ use different argument names.
@Sean3 - let me know. Outside of the bean I'm not sure where else I'd use it - so I'd love to see examples.
@Michael - you could alleviate that a bit by using hint= in your cfcomponent tag. "I'm a Customer bean with these properties: name,id,gender,number"
@Sean3 -
Searching the PDF? I opened it up in Preview on the Mac and typed in onmissinm and it found a reference on pg 116 and loads of references on pg 117 (in the section I quoted). I agree it's odd that the arguments must be exactly as named - I'm assuming that's because the missing method call is handled at the call site and it is trying to avoid the overhead of introspecting the actual arguments etc?
@Justin, onMissingMethod() was specifically added for "expected processing". In the same way that Smalltalk and Ruby have missing method handlers for dynamic behavior. This is what really sets CF apart from Java, for example.
<cfset mybean.setName(name="foo")>
And it worked fine. I had "name" as a struct key in the arguments struct.
HAHA, got a kick out of that one geek style...
interesting commentary...
cheers
http://cf-examples.net/index.cfm/2008/8/25/onMissi...
[Add Comment] [Subscribe to Comments]