I am getting a very strange error when I try to scope a private method within a ColdFusion component. I have this very simple ColdFusion component:
<cfcomponent output="false"> <cffunction name="PrivateMethod" access="private" returntype="string" output="false" hint="Returns an updated string."> <!--- Define arguments. ---> <cfargument name="Value" type="string" required="true" /> <!--- Return formatted string. ---> <cfreturn ("<strong>" & ARGUMENTS.Value & "</strong>") /> </cffunction> <cffunction name="CallMethod" access="public" returntype="void" output="false" hint="Invokes the private method."> <cfset VARIABLES.PrivateMethod( Value = "Crazy Bananas!" ) /> <!--- Return out. ---> <cfreturn /> </cffunction> <cffunction name="Debug" access="public" returntype="void" output="true" hint="Dumps out private variables."> <cfdump var="#VARIABLES#" label="VARIABLES Scope Dump" /> <cfabort /> </cffunction> </cfcomponent>
It does absolutely nothing. It has one private method named "PrivateMethod". It has two public methods; one, Debug(), that dumps out the variables scope, and another, CallMethod(), that invokes the private method in the VARIABLES scope using named arguments.
Now, if I call the Debug() method on this ColdFusion component I get:
As you can clearly see, the method PrivateMethod() is clearly within the VARIABLES scope of this component. However, when I try to call CallMethod():
<!--- Create CFC instance. ---> <cfset objTest = CreateObject( "component", "Test" ) /> <!--- Invoke method (which calls private method). ---> <cfset objTest.CallMethod() />
... I get this ColdFusion error:
Cannot invoke method PrivateMethod on an object of type coldfusion.runtime.VariableScope with named arguments. Use ordered arguments instead.
Now, I have two options to fix it here. I can either take out the names arguments to the PrivateMethod() function:
<cfset VARIABLES.PrivateMethod( "Crazy Bananas!" ) />
... and just use ordered arguments (values are assigned to arguments in the same order in which they were passed). Or, I can simply not use the a method scope when invoking the private method and let ColdFusion search for the appropriate method:
<cfset PrivateMethod( Value = "Crazy Bananas!" ) />
Both of the "solutions" will allow the code to execute fine. But, of course, this should not be the case. There is no reason that I can see that I should not be able to invoke a private method using its scope.
Any one have any ideas? Is this a bug? Am I just not seeing something?
Cool. I knew about the variables.methodcall(namedArg="blah") because I tried it a while back. I just settled on taking out the reference to variables though, since I don't think it does anything for the readability of the program in that case (actually degrades it, imo). But I didn't know I could simply have used ordered arguments.
Strange stuff... keep us updated =)
I head what you are saying on readability. My issues it that I like using the THIS scope when referencing public method within the same CFC. I learned to do this after I once had an ARGUMENTS key and a method name conflicting (it kept trying to invoke the argument "Commit" like it was a method (in the THIS scope)).
So anyway, if I use the THIS scope for public method invocation, which to me ups the readability (very clear what I am referring to), I figure I should use the VARIABLES scope for private methods in order to keep things very consistent.
I agree with that - certainly in my mind if you can do this.publicmethod() you should be able to do this.privatemethod(). And adding the fact that you can use it with ordered arguments just strengthens the case.
I had the same problem today invoking a function in the request scope. We typically copy our UDFs into the request scope so we can reference them from within CFCs.
The original code was:
<cfset var convRate = request.getConversionRate(foo = bar)>
The error was:
Cannot invoke method getConversionRate on an object of type coldfusion.runtime.RequestScope with named arguments.
Use ordered arguments instead.
The bizarre workaround was to create a copy of the function as a local variable and then invoke that copy instead. E.g.
<cfset var getConversionRate = request.getConversionRate>
<cfset var convRate = getConversionRate(foo = bar)>
This is pretty lame. Using ordered arguments wasn't an option because I have multiple optional arguments in my function.
Yeah, I am not sure what the problem is with it. It must be some weird wiring issue behind the scenes that makes it very complicated. Cause to me, a scope is a scope is a scope.
The variables scope of a component is part of the class definition, not an object instance definition. You can use it for class methods, class constants, et al.
The this scope of a component is part of an object instance definition. You can use it for instance methods, instance properties, et al.
The problem is that ColdFusion has confusing syntax. Implicit scope = variables, unless it = url, form, or other. Explicit scope this. Don't even get me started on the super variable, and how it's a class variable, not an object instance variable. [Say what?]
What's more, is that createObject doesn't necessarily create an object instance. [Say what?] It just creates a reference to the class. You can double check this by creating Java objects. You need to call object=Class.init() to actually get an object instance.
You can't call a private method as a class method. You can only call a private method from an instance.
You called variables.privateMethod, which basically treats the call as if it originated from outside the class. Hence, like a class method.
COldFusion wants you to always use this.privateMethod, which treats the call instead as if it originated from inside the class. Hence, an instance method.
Examples are psuedocode-ey.
This is what you did:
function privateMethod private
function publicMethod public
Which is the same as this:
x = createObject(component, foo).privateMethod()
You need to do this:
function privateMethod private
function publicMethod public
I think that some of what you're saying is on the money. However, calling a method on the variables scope is not the same as calling it on the public face of the component instance.
The reason that you cannot use named arguments on calling the variables scope is because ColdFusion simply doesn't allow it. This, however, is something that is changing in CF9.
Just googled this error because I hadn't seen it in awhile and stumbled across your post Ben.
Funny thing is, this works now in CF9 ... but I ran into this error when deploying something I was developing in CF9 onto a CF8 server.
Yeah, I'm pretty excited that this is now fixed. Previously, you could only use ordered arguments. This is one of the reasons that I very rarely use privately-scoped functions. So now with this fixed AND with the synthesized getters / setters, I might just be using the variables scope a whole lot more :)
@Ben, actually, I think I lied ... after testing a few more things out, I think my error was being generated by something else ... hmm. still investigating though. Oh well. sorry i got you all hot and bothered man.
You're still right about this being fixed in CF9 though :)
Yeah, I just confirmed that myself ... man, trying to remember what you can/can't do from one version to the next drives me nutty sometimes. ugh.
WORD UP :D I keep forgetting that there are new functions for a lot of the tag-based features. Like all the file/directory functions, which keep growing even in CF9.
Just an additional bit of info for people still using CF7:
1. I was only able to resolve this issue by using Leon's method, above.
2. My methods were public. At least on CF7 it didn't matter if the method was public or private.
3. Calling templates could access APPLICATION scoped methods just fine, but Application.cfc itself stumbled on this error until I applied Leon's technique.
4. If you are still banging your head on CF7, you are not alone :)
I ran into this issue trying to invoke an object I have for logging errors into a database. The following code ran fine on my dev server, which runs cf9, but when moved to qa which runs cf8, it produced an error.
- <cfcatch type="any">
- <cfset variables.logError = createObject("component", "cfc/logError").logError>
- <cfset variables.logError(reference = 'Catch Error', objCatch = cfcatch)>
- <cfreturn 0>
I came up with a quick fix and resubmitted back to qa, but wanted more information on why that error happened in the first place, which led me to this page.
My quick fix was simply removing the variables. from the second reference. It apparently didn't mind me scoping to variables when I created the object, but didn't like it when I tried to use the object in that scope. I don't know what affect leaving the variables. when it creates the object if any. At this point I am not too concerned with it. I have been moving over to trying to appropriately scope all of my variables both when setting and referencing them. Both for the value of it being more efficient for the compiler and to reduce confusion as to what a particular variable is used for. This was a nice lesson since a lot of the servers I code for are cf8.