parent component's sample init method
<cffunction name="init" output="false" access="public" returntype="NavigationBase">
<cfset this.index = 0 />
<cfset this.length = 0 />
<cfset this.current = "" />
<cfreturn this />
</cffunction>
.
.
.
child component sample code
<cfcomponent displayname="Navigation" output="false" extends="NavigationBase">
<cffunction name="init" output="false" access="public" returntype="Navigation">
<cfreturn this/>
</cffunction>
.
.
And if i instanciate the child component, the variables defined in parent's init function are always undefined. What I am doing here wrong?
Thanks
The init() of the child overrides the parent init() method, so the variables are not getting defined anywhere. Add this line to your child init
<cfset super.init()>
to run the parent init and define/initialize those variables.
Related
I am currently checking all my functions and components for unscoped variables. I am using a tool called varscoper4 to check all functions. Given the following code varscoper tells me that the variable "input" at line 4 in unscoped. Is this true and do i need to scope the argument variable if I alter it?
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<Cfset input = 3>
<cfreturn input>
</cffunction>
FYI if I do not alter the argument variable input in the function varscoper4 does not report any unscoped variables.
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<Cfset var output = 3 + input>
<cfreturn output>
</cffunction>
In your fist code block, input is "unscoped" but CF interprets it as arguments scope. CF will always try to find your unscoped variables by looking through a priority order. You can find more information on that here: http://www.learncfinaweek.com/week1/Scopes/
You can also view for yourself what that looks like by dumping the different scopes and seeing the output.
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<cfset input = 3>
<cfdump var="#variables#" label="variables">
<cfdump var="#arguments#" label="arguments">
<cfdump var="#local#" label="local">
<cfreturn input>
</cffunction>
I would strongly encourage you to take the output of your varscoper tool as a guide to where you should explicitly scope your variables. In that case, your first block of code would look like this. This is for clarity and certainty in your code.
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<cfset arguments.input = 3>
<cfdump var="#variables#" label="variables">
<cfdump var="#arguments#" label="arguments">
<cfdump var="#local#" label="local">
<cfreturn arguments.input>
</cffunction>
Personally, I don't like setting or changing arguments in my functions and methods. I'd rather keep them unadulterated as you have in your second block of code. But even there, I would explicitly scope the arguments so that you know where it came from -- even if not flagged by varscoper
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<cfset var output = 3 + arguments.input>
<cfdump var="#variables#" label="variables">
<cfdump var="#arguments#" label="arguments">
<cfdump var="#local#" label="local">
<cfreturn output>
</cffunction>
Last thing to be added here if it's not clear is that var scoping puts everything int he local scope. You could also do this and it would be functionally equivalent to the previous block of code:
<cfoutput>#testit(1)#</cfoutput>
<cffunction name="testit">
<cfargument name="input">
<cfset local.output = 3 + arguments.input>
<cfdump var="#variables#" label="variables">
<cfdump var="#arguments#" label="arguments">
<cfdump var="#local#" label="local">
<cfreturn local.output>
</cffunction>
You should use arguments.input:
<cfset arguments.input = 3>
Although even better would be to use your second example, with the arguments scope:
<cfset var output = 3 + arguments.input >
Don't modify arguments, leave them as they are when they arrive, incase you want to re-use the original value later on.
http://help.adobe.com/livedocs/coldfusion/8/htmldocs/help.html?content=buildingComponents_29.html
I am getting the following error message. I have already created Uploads.cfc handler in handlers directory. Checked everything, can't find the solution.
Error Type: HandlerService.EventHandlerNotRegisteredException : [N/A]
Error Messages: The event: uploads is not valid registered event.
Here is the Uploads.cfc code:
<!--- Default Action --->
<cffunction name="index" returntype="string" output="false" hint="My main event">
<cfargument name="event">
<cfargument name="rc">
<cfargument name="prc">
<cfobject component="model.Uploader" name="fileUploader">
<cfset filesJson = fileUploader.Upload(rc.file)>
<cfreturn filesJson>
</cffunction>
Can you please suggest a solution?
You shouldn't have to restart the whole CF server. You just need to re-initialize ColdBox. Just add ?fwreint= or ?fwreint={password} to the URL. The password is set in ColdBox.cfc: "reinitpassword". You can also configure in ColdBox.cfc to not cache handlers in non-production environments.
If you're using ColdBox 3.6 or newer, you don't have to define event, rc and prc anymore.
<cffunction name="index" returntype="string" output="false" hint="My main event">
<cfobject component="model.Uploader" name="fileUploader">
<cfset filesJson = fileUploader.Upload(rc.file)>
<cfreturn filesJson>
</cffunction>
Secondly, you should use WireBox instead of creating objects on the fly as you're doing. And finally, don't define variables specific to a function into the variables scope of the handler CFC file. Prefix them with local to make sure that those variables are "function local": only available to the particular function that is using them.
<cffunction name="index" returntype="string" output="false" hint="My main event">
<cfset local.fileUploader = getModel("Uploader") >
<cfset local.filesJson = fileUploader.Upload(rc.file)>
<cfreturn local.filesJson>
</cffunction>
I have also encountered the same problem in the past. Try restart the ColdFusion Application Server.
iKnowKungFu misspelled ?fwreint= (missing i)
Should be ?fwreinit
I have a test application structured like so:
Application.cfc
ApplicationProxy.cfc
index.cfm
sub_app/
Application.cfc
index.cfm
and I want my sub app to inherit all variables and events from the top level Application.cfc.
I have read and implemented Sean Corfield's ApplicationProxy method for extending an Application component but I can't get it to work as when I visit sub_app/index.cfm I get this error:
Could not find the ColdFusion Component or Interface ApplicationProxy.
From the error I can only guess that the application is looking in the wrong place, how do I correct this?
Application.cfc:
<cfcomponent name="Application" output="true">
<cfset THIS.name = "testAppA">
<cfset THIS.sessionManagement="Yes">
<cfset THIS.applicationTimeout = createTimeSpan(0,0,10,0)>
<cfset THIS.sessionTimeout = createtimespan(0,0,10,0)>
<cfset THIS.clientManagement = true>
<cfset THIS.clientStorage = "cookie">
<cfset THIS.loginStorage = "cookie">
<cfset THIS.setDomainCookies = false>
<cfset THIS.setClientCookies = true>
<cfset THIS.scriptProtect = true>
<cfset THIS.secureJSON = true>
<cffunction name="onApplicationStart" returntype="void">
<cfset APPLICATION.name = "testAppA">
<cfset APPLICATION.test = "test var">
</cffunction>
<cffunction name="onSessionStart" returntype="void">
<cfset SESSION.loggedIn = 1>
</cffunction>
</cfcomponent>
ApplicationProxy:
<cfcomponent name="ApplicationProxy" extends="Application">
</cfcomponent>
index.cfm:
Sub app
sub_app/Application.cfc:
<cfcomponent extends="ApplicationProxy" output="true">
<!---
Uses parent Application settings
--->
</cfcomponent>
sub_app/index.cfm:
Parent app
<cfdump var="#Application#">
Best way to do is create ApplicationProxy.cfc file with all function you need to inherit and then extends in Application.cfc wherever needed. I am using this method since long time and no issue face with this approach.
I am trying to create a custom debug tool and I need to use a component with two separate functions in it. The first function (startTimer) has some arguments such as startCount and the other one (endTimer) has endCount. What I am trying to accomplish is something like the following code:
<cffunction name="startTimer" access="public" returntype="void">
<cfargument name="startActionTime" type="string" required="no">
</cffunction>
<cffunction name="endTimer" returntype="void" access="public">
<cfargument name="endActionTime" type="string" required="no">
<cfset finalTime = endActionTime - startTimer.startActionTime>
<!---Some SQL will go here to record the data in a db table --->
</cffunction>
And this is how I am calling the function
<cfscript>
location = CreateObject("component","timer");
loc =location.startTimer(
startActionTime = getTickCount()
);
end = location.endTimer(
endActionTime = getTickCount()
);
</cfscript>
I guess I am having scope issues because when I am trying to run the code I am getting an undefined error on startTimer.startActionTime. What is the correct way to do something like this?
You can use the variables scope like so:
<cfcomponent>
<cfset variables.startActionTime = 0>
<cffunction name="startTimer" access="public" returntype="void">
<cfargument name="startActionTime" type="numeric" required="no">
<cfset variables.startActionTime = arguments.startActionTime>
</cffunction>
<cffunction name="endTimer" returntype="string" access="public">
<cfargument name="endActionTime" type="numeric" required="no">
<cfset finalTime = endActionTime - variables.startActionTime>
<!---Some SQL will go here to record the data in a db table --->
<Cfreturn finaltime>
</cffunction>
</cfcomponent>
From the Adobe Docs: Variables scope variables created in a CFC are available only to the component and its functions, and not to the page that instantiates the component or calls its functions.
I am creating an API, and within each method I make a call to a logging method for auditing and troubleshooting. Something like:
<cffunction name="isUsernameAvailable">
<cfset logAccess(request.userid,"isUsernameAvailable")>
......
</cffunction>
I'd like to avoid manually repeating the method name. Is there a way to programatically determine it?
I've looked at GetMetaData() but it only returns info about the component (including all the methods) but nothing about which method is currently being called.
So now 3 ways.
If you are using ColdFusion 9.0 or higher there is now a function named GetFunctionCalledName(). It will return what you are looking for.
http://help.adobe.com/en_US/ColdFusion/9.0/CFMLRef/WS7cc222be8a31a47d-6e8b7083122cebfc8f2-8000.html
OR
Use ColdSpring and Aspect Oriented Programming (http://www.coldspringframework.org/coldspring/examples/quickstart/index.cfm?page=aop) to handle this for you.
OR
Use a cfthrow to generate a stack trace that has the information for you:
<cffunction name="determineFunction" output="FALSE" access="public" returntype="string" hint="" >
<cfset var functionName ="" />
<cfset var i = 0 />
<cfset var stackTraceArray = "" />
<cftry>
<cfthrow />
<cfcatch type="any">
<cfset stacktraceArray = ListToArray(Replace(cfcatch.stacktrace, "at ", " | ", "All"), "|") />
<!---Rip the right rows out of the stacktrace --->
<cfloop index ="i" to="1" from="#ArrayLen(stackTraceArray)#" step="-1">
<cfif not findNoCase("runFunction", stackTraceArray[i]) or FindNoCase("determineFunction", stackTraceArray[i])>
<cfset arrayDeleteAt(stackTraceArray, i) />
</cfif>
</cfloop>
<!---Whittle down the string to the func name --->
<cfset functionName =GetToken(stacktraceArray[1], 1, ".") />
<cfset functionName =GetToken(functionName, 2, "$")/>
<cfset functionName =ReplaceNoCase(functionName, "func", "", "once")/>
<cfreturn functionName />
</cfcatch>
</cftry></cffunction>
My recommendation would be use getFunctionCalledName, or if not on CF 9 ColdSpring, as it will probably buy you some other things.
I agree w/ tpryan. ColdSpring makes this very easy. However, here is another alternative. Instead of parsing the stack trace, you can parse the CFC file itself.
<cffunction name="foo" displayname="foo" hint="this is just a test function" access="public" returntype="string">
<cfset var test = getFunctionName(getMetaData().path, getPageContext().getCurrentLineNo()) />
<cfreturn test />
</cffunction>
<cffunction name="getFunctionName" hint="returns the function name based on the line number" access="public" returntype="string">
<cfargument name="filepath" type="string" required="true" />
<cfargument name="linenum" type="any" required="true" />
<cfset var line = "" />
<cfset var functionName = "" />
<cfset var i = 1 />
<!---- loop over CFC by line ---->
<cfloop file="#ARGUMENTS.filepath#" index="line">
<cfif findNoCase('cffunction', line, 1)>
<cfset functionName = line />
</cfif>
<cfif i EQ ARGUMENTS.linenum><cfbreak /></cfif>
<cfset i++ />
</cfloop>
<!---- parse function name ---->
<cfset functionName = REMatchNoCase("(\bname=[""|'])+[a-z]*[""|']", functionName) />
<cfset functionName = REMatchNoCase("[""']+[a-z]*[""']", functionName[1]) />
<cfset functionName = ReReplaceNoCase(functionName[1], "[""']", "", "all") />
<!---- return success ---->
<cfreturn functionName />
</cffunction>
The above is written for ColdFusion 8. CFLOOP added support for looping over files line by line (and doesn't read the entire file into memory). I did a few tests comparing the stack trace method vs. file parsing. Both performed equally well on a small CFC being called directly from a single CFM template. Obviously if you have very large CFCs the parsing method might be a bit slower. On the other hand, if you have a large stack trace (like if you are using any of the popular frameworks) then file parsing may be faster.
-= Viva ColdFusion =-
Well you might try this:
<cffunction name="getFunctionName" returntype="any">
<cfset meta =getMetaData(this)>
<cfreturn meta.functions[numberOfFunction].name>
</cffunction>
I've tried various things, and this is not accurate as the functions seem to be added to the array of functions in reverse alphabetical order. This seems limiting (and not solving the problem). I would imagine some native java code could be invoked, but i'm going to need to look into that.
This and This look like interesting reading on related internal functions.
Re: The other answer on coldspring. I found this in depth article on function metadata with coldspring.
Related question : How to get the name of the component that’s extending mine in ColdFusion?
I thought of another way that could work.
Setup an OnMissingMethod something like this:
<cffunction name="onMissingMethod">
<cfargument name="missingMethodName" type="string">
<cfargument name="missingMethodNameArguments" type="struct">
<cfset var tmpReturn = "">
<cfset var functionToCallName = "Hidden" & Arguments.missingMethodName>
<cfset arguments.missingMethodArguments.calledMethodName = Arguments.missingMethodName>
<cfinvoke method="#functionToCallName#" argumentcollection="#Arguments.missingMethodArguments#" returnvariable="tmpReturn" />
<cfreturn tmpReturn>
</cffunction>
Then name each of the regular methods with a prefix ("Hidden" in this example), and mark them as private. So my initial example would become:
<cffunction name="HiddenisUsernameAvailable" access="private">
<cfset logAccess(request.userid,Arguments.calledMethodName)>
......
</cffunction>
Now all the calls will be intercepted by onMissingMethod, which will add the method name to the arguments that get passed to the real method.
The downsides I see to this are that introspection no longer works properly, and you must be using named arguments to call all your functions. If you are not using named arguments, the args will randomly change order in the missingMethodNameArguments structure.
getFunctionCalledName() gives you the name of the active method.