Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at the jQuery Conference 2010 (Boston, MA) with:

Cannot Invoke Method On An Object Of Type coldfusion.runtime.VariableScope With Named Arguments

By Ben Nadel on
Tags: ColdFusion

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:


 
 
 

 
ColdFusion Component Variables Scope CFDump  
 
 
 

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?




Reader Comments

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 =)

Reply to this Comment

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.

Reply to this Comment

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.

Reply to this Comment

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.

Reply to this Comment

@Leon,

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.

Reply to this Comment

@ben

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:

component
function privateMethod private
function publicMethod public
variables.privateMethod

Which is the same as this:

x = createObject(component, foo).privateMethod()

You need to do this:

component
function privateMethod private
function publicMethod public
this.privateMethod

Reply to this Comment

@Alex,

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.

Reply to this Comment

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.

Reply to this Comment

@Steve,

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 :)

Reply to this Comment

@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.

Reply to this Comment

@Ben,
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.

Reply to this Comment

@Steve,

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.

Reply to this Comment

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 :)

Reply to this Comment

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>
  • </cfcatch>

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.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.