Tonight I played around with onMissingMethod, a cool new feature in ColdFusion 8. See Ben Nadel's entry on it for more detail. I knew that the method took two arguments - the name of the method invoked and a struct of the arguments that was passed.
What I didn't realize and was surprised by - you can't rename these arguments. So consider this:
<cffunction name="onMissingMethod" access="public" returnType="any" output="false">
<cfargument name="method" type="string" required="true">
<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:
<cffunction name="onMissingMethod" access="public" returnType="any" output="false">
<cfargument name="missingMethodName" type="string" required="true">
<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:
<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>
This let's me define a simple bean like so:
<cfcomponent output="false" extends="bean">
<cfset variables.id = "">
<cfset variables.username = "">
<cfset variables.password = "">
<cfset variables.name = "">
<cfset variables.email = "">
</cfcomponent>
Archived Comments
I'm curious, why would you use find() and replaceNoCase() instead of left(arguments.missingMethodName,3) is "get" (or "set") and then right(arguments.missingMethodName, len(arguments.missingMethodName)-3)
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.
CFML Reference pg 117:
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>
onMissingMethod() is probably my favorite CF8 feature. Partly because I asked for it (in a hallway conversation with Ashwin Mathew early in 2006 - while I was still at Adobe). Partly because it gives us - CFers - access to a feature that Smalltalk and Ruby have had for a long time. Whilst the docs suggest that it might be used for error handling, it's really all about dynamic proxies - even the "error handling" is a dynamic proxy that determines the behavior of method calls at runtime.
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 :)
I have mixed emotions regarding using onMissingMethod vs. a code generator and actually creating getter/setter methods. The biggest drawback I see is that you lose the introspective self-documenting aspect. Dump a CFC and you can see all of the methods available. Do a getMetaData and you can walk the list and do your own documentation routines.
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.
@Michael, like all things in life, it's a tradeoff - in this case between productivity and... well, quite a number of things really.
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 gave an off-the-top-of-my-head example of how not having visible or easily referenced names can lead to confusion and you're making assumptions about our object models and naming conventions? Okay, since we're talking about customer id's (or numbers) how about order.getCustomerID()? (vs. order.getCustomerNumber())
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)
I really like that approach strictly from a reduction of clutter standpoint. I use code generators, too, but you really don't loose a whole lot by being able to presume that there is at least a simple getter/setter mechanism in place for any property you add.
Wow, lots of comments.
@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 -
Does anyone else feel that using the missing method handler for expected processing is kind of a wrong usage of it? I realize that taking the approach that Ray had pointed saves time in continuously writing monotonous getters and setters, but I some how feel as though the implicit exception handling is not the way to go. If you are willing to extend your component to include the 'core bean', why not write a generic getter and setter method (ie. get(attrib) & set(attrib,value) ) instead?
@Ray, for setFoo() - remember that if the method is not present there is no named argument and therefore the argument will be anonymous in onMissingMethod(). That was the point I was trying to make.
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.
@Sean: I don't get what you mean. I did:
<cfset mybean.setName(name="foo")>
And it worked fine. I had "name" as a struct key in the arguments struct.
I meant: <cfset myBean.setName("foo") /> which is how most people write their code, yes? In that case, your onMissingMethod() handler won't work unless you add code to handle unnamed arguments.
"And stop dissin' my function names."
HAHA, got a kick out of that one geek style...
interesting commentary...
cheers
I have posted a simple example on onMissingmethod here.
http://cf-examples.net/inde...