I thought CFC's methods defined as functions in cfscript are output=false by default, but when I open the CFC in cfcexplorer.cfc (browser directly to the CFC), it says Output: enabled.
cfcexplorer.cfc's Bug?
Short answer:
It doesn't matter.
cfscript does not output anything unless you explicitly call writeOutput() from it. This includes functions in cfscript as well as any cfscript code outside of a function.
This is different from CF tags' syntax, which, by default, output at least whitespace between the tags. In cfscript, any text you write will be compiled by the CFML engine. in CF tags, any text you write will be written to the output buffer and sent to browser.
Long answer:
It's the same as not specifying an output attribute.
cfscript blocks don't output anything. Any tag blocks, unless wrapped in cfsilent, do output whitespace if nothing else. Yes, even cffunctions do, but the output is discarded if the output attribute is set to false.
The essence of Peter Boughton's answer is correct. It's neither wrapped in cfsilent nor cfoutput. Output is not forbidden, but it doesn't happen unless you do it explicitly.
You can always combine a tag-based cffunction with scripting to get the best of both worlds. Something like...
<cffunction name="x" output="false" access="package">
<cfargument name="y" type="string" required="false" default="YY" />
<cfscript>
var someValue = y & "something";
writeOutput("test"); // this will not be written
return someValue;
</cfscript>
</cffunction>
This lets you specify an output and access on the cffunction tag as well as allow arguments to be optional (which you can't do through cfscript functions), then fill the body with cfscript, including var statements and the function return.
Of course, for that function, if you remove the output attribute or change it to true, it will output "test" before returning.
cfscript functions are a weird monkey. They are kind of both. You can't specify that they are output="false", but they are until you use a writeOutput(), but they are reported by cfcexplorer as being output="true". It is an odd issue I think the cfml advisory committee is looking at right now.
I'm not entirely certain, but my guess would be that script functions are the same as cffunction tags in this regard - in that the default is neither true nor false.
Setting the output attribute for a cffunction, the following are the case:
true is equivalent to the function being wrapped in cfoutput.
false is equivalent to the function being wrapped in cfsilent.
Leaving it default (undefined) is equivalent to standard code that is wrapped neither with cfoutput nor cfsilent.
However, I almost never use cfscript, and this may not actually be the case - we'll have to wait for others to come along and either confirm or correct this.
Related
I am migrating an application from ColdFusion 9 to ColdFusion 11.
In the existing code there is an assignment of a variable to false:
<cfset VARIABLES.roleTypeId = false >
And then, farther down, a function that expects this variable to be numeric:
<cffunction name="rolesForStudy" >
<cfargument name="id" hint="Study Id">
<cfargument name="roleTypeId" default="#VARIABLES.roleTypeId#" type="numeric"/>
</cffunction>
I inherited the code, and I cannot defend the original programmer's decision to set it up this way -- but, in short, it worked in ColdFusion 9, and it doesn't work in ColdFusion 11 (returning a data type error). I assume that ColdFusion 9 was automatically converting false to 0.
My question: Is there a configuration setting that I can change in ColdFusion 11 to make it do the conversion like ColdFusion 9 did? Or will I have to fix this code, along with probably lots of other similar examples throughout the application? Neither I nor our ColdFusion administrator has been able to find any information about this in the ColdFusion Administrator interface, the ColdFusion documentation, or online.
Edit in Response to Adam Cameron in comments
I have created a file that consists of the following 10 lines (and nothing else):
<cfset VARIABLES.roleTypeId = false >
<cfoutput>
<p>#rolesForStudy( 1, VARIABLES.roleTypeId )#</p>
</cfoutput>
<cffunction name="rolesForStudy" >
<cfargument name="id" hint="Study Id">
<cfargument name="roleTypeId" default="#VARIABLES.roleTypeId#" type="numeric"/>
<cfreturn "It worked" >
</cffunction>
When I execute it in ColdFusion 9, it displays the words "It worked".
when I execute it in ColdFusion 11, it returns the following error message:
If the component name is specified as a type of this argument, it is possible that either a definition file for the component cannot be found or is not accessible.
I believe you will have to fix the code. There are no settings (that I know of at any rate) that alter the way CF handles boolean types. You should be able to change the assignement above from "false" to 0 and your function code will work. However I suspect elsewhere you might have something like <cfif variables.roletypeID IS "False"> which will then be broken as it is in truth looking for a string - which also works (ha). CF's handling of a myriad of values as boolean (0 or not 0, true, false, yes and no) is a legacy of it's origin. It's convenient at times but definitely leads to things like this.
Meanwhile I wonder if this change of behavior is a new bug or the fixing of an old bug. In my view passing "false" as an argument and having it read as numeric seems inconsistent so the new way of doing it seems right to me. However, many many languages treat 0 or not 0 as default values for true and false.
EDIT:
According to Adam's comments below, my example of code where someone would say:
<cfif somevar IS "false">
...would work even if somevar was indeed numeric. His example (useful) is that:
#0 is "False"#
...will output "yes" - so CF is recasting the string "false" to a zero under the hood for comparison. That makes my example incorrect.
My answer is still correct I believe. The issue he's running into is that the argument passed to his function - being of the type "boolean" is throwing an error because the function expects a numeric value. But Adam's point and example makes me think perhaps this behavior is a bug - since it appears CF is not casting to a number before checkign the type (something it did do in CF 9 according to the Joe).
NumberFormat is your saviour, credit going to this comment on one of Ben Nadel's articles.
<cfset VARIABLES.roleTypeId = false>
#NumberFormat(VARIABLES.roleTypeId)# <!-- = 0 -->
<cfset VARIABLES.roleTypeId = true>
#NumberFormat(VARIABLES.roleTypeId)# <!-- = 1 -->
So you should be able to either convert it to the expected numeric type before calling the function, or just doing
<cfargument name="roleTypeId" default="#NumberFormat(VARIABLES.roleTypeId)#" type="numeric">
I've raised this as a bug in ColdFusion 11: "Type coercion failure when passing boolean to a numeric arg".
I recommend you work around this by taking the type check off the argument. This is probably the lowest-impact fix.
This is also a rare case in which I'd add a comment to code, explaining why you've taken the type checking off.
Apologies to Joe, Mark & Duncan for emphatically contradicting what they were finding. That said, I don't agree that their answers are the best approach here ;-)
Do any of the ColdFusion IDEs/IDE plugins allow you to perform actions similar to Visual Studio's go to definition and find usages (some details of which are on this page)?
For example, in one .cfc file I might have:
<cfset variables.fooResult = CreateObject(
"component",
"Components.com.company.myClass").fooMethod()>
And in myClass.cfc I have:
<cffunction name="fooMethod" access="public">
<!-- function body -->
</cffunction>
If I have my cursor set over .fooMethod in the first file, a go to definition action should place me on the declaration of that method in myClass.cfc.
At present I'm using the CFEclipse plugin for Eclipse to view some legacy ColdFusion.
CFEclipse doesn't have this functionality, partly because CFML is a dynamic language with a fair bit of complexity parsing-wise.
You can use regex searching to get you most of the way there in most cases.
Function Definitions
To find a function definition, most times searching for...
(name="|ion )methodname
...is enough, and quicker than the more thorough form of:
(<cffunction\s+name\s*=\s*['"]|\bfunction\s+)methodname
Function Calls
To find a function call, against just do:
methodname\s*\(
Though against you might need to be more thorough, with:
['"]methodname['"]\s*\]\s*\(
...if bracket notation has been used.
You might also want to check for cfinvoke usage:
<cfinvoke[^>]+?method\s*=\s*['"]methodname
Of course, neither of these methods will find if you have code that is:
<cfset meth = "methodname" />
<cfinvoke ... method="#meth#" />
...nor any other form of dynamic method names.
If you really need to be thorough and find all instances, it's probably best to search for the method name alone (or wrapped as \bmethodname\b), and manually walk through the code for any variables using it.
IF you use
<cfset c = new Components.com.company.myClass()>
<cfset variables.fooResult = c.fooMethod()>
I believe in CFBuilder you can click Ctrl and the CFC class and method will turn into a hyperlink. See "Code insight" on http://www.adobe.com/ca/products/coldfusion-builder/features.html It works when it work, it doesn't when mapping is incorrect or certain syntax may not be supported. I'm not sure if they support the CreateObject() way.
There's no find usage as CF is not a static language. However, the Find can find what you need most of the time unless the code invokes method dynamically or uses Evaulate()
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to do a cfdump inside a cfscript tag?
I'm new to Coldfusion and wondered if anyone knew of a way to access function when inside a script block. I cant find way of calling it from there.
Why have Adobe removed it? Or have they just called it something else now?
Sorry - I know this is probably really basic question, but like I said I'm new.
The script equivalent of cfdump is writeDump().
The same pattern has been used for cfoutput and cflog, with writeOutput() and writeLog() functions.
writeDump()
Apparently in ColdFusion 9 it has been added as writeDump() so you should be able to use this from a cfscript block.
So if you have CF9, then you are fine.
If you using an older legacy system which does not support this newly introduced writeDump() function, as we are, you could write a wrapper for it and put it somewhere accessible to all your files.
This is the wrapper I have written, which for short term debugging use can be put on the cfc file you are working on (although remove it before you commit your code - otherwise it's just a mess), or you can put it somewhere global so you can call it from shared scopes.
Here is an example of a wrapper function you can use:
<cffunction name="dump" access="private" returntype="void" hint="dump wrapper">
<cfargument name="stuffToDump" type="any" required="true" hint="what you want to dump">
<cfargument name="abort" type="any" default="false" hint="abort after dump">
<cfargument name="expand" type="any" default="false" hint="expand output">
<cfdump var="#arguments.stuffToDump#" expand="#arguments.expand#">
<cfif #arguments.abort# EQ 1>
<cfabort>
</cfif>
</cffunction>
There are probably better ways around this problem, but this is what I currently use.
You can put it (temporarily) on the cfc file that you are currently working on, but obviously don't commit this to your code base as you don't want the dump function on all your files.
Or you could put it permanently onto a cfinclude file, and just include that (again - temporarily) to files you are debugging.
Another alternative I guess is to put it onto the Application.cfc. This file has a number of standard methods but you can also define your own additional methods to be included into it. Then you have your dump() function available in the APPLICATION scope.
There's a good info page on the Application.cfc file here. http://www.bennadel.com/blog/726-ColdFusion-Application-cfc-Tutorial-And-Application-cfc-Reference.htm
I have even seen it used in the Server scope, but this should never be done on production code. OK for debugging I guess as a last resort, just make sure you remember to remove it.
Personally I think both these options are probably far from ideal, and it's a great shame it took Adobe so long to provide a script alternative to the function. Would have saved a lot of pain.
But hopefully if you are using CF9 then all this will be irrelevent to you and you can just use the new writedump() function now they have finally added it in.
To be a little clearer and provide an example:
<cfscript>
SomeString = 'ABC';
writeDump(SomeString);
</cfscript>
Peter's answer is 100% correct.
After my form submits I am calling a controller method that runs an orm EntitySave in my cfc. I would like to dump out the arguments before I save my data via ORM just to visually validate those are indeed the values I want to save in the database.
So when I use this
<cfthrow message="value = #arguments#">
I am getting this:
Error: Complex object types cannot be converted to simple values.
I understand you are not allowed to do this with complex objects, so in those cases I would use <cfdump> but I can't find a way to dump in a <cfthrow>. I am sure there is a better way to accomplish this. I have also tried doing a <cfmail> to myself which works amazingly but the email will take a minute or two. Any suggestions would be greatly appreciated. I am currently checking into ValidateThis.
You could serialise it:
<cfthrow message="value = #serializeJson(arguments)#">
But I don't think you want that sort of thing showing up on the screen.
I'd log it if I was you (so same notion, just <cflog> before the <cfthrow>, and put the arguments in the log entry, and in the <cfthrow> just put a brief explanation of the error (you should also use a TYPE attribute, for subsequent handling of the exception that you've raised.
Rather than throwing it, you could try dumping it to a file and see if that meets your needs:
<cfdump var="#arguments#" output="C:\dump.html" format="html">
If you need to abort (as a throw would do), you can add abort on to the end of the tag, <cfdump... abort>.
You could do the following in order to use <cfdump>:
<cfsavecontent variable="arguments_dump">
<cfdump var="#arguments#" />
</cfsavecontent>
<cfthrow message="#arguments_dump#" />
Takes a little bit more code however and is not as elegant as Adam Cameron's answer above.
I've get a variable that can contain a CF custom tag. E.g.
<cfset a = '<model:sparkline id="1"/>'/>
And I'd like that to be evaluated into HTML and outputted. Not sure how/if I can do this.
Can you modify the custom tag? If so you can use the caller scope to set a variable in the calling page. So inside the custom tag you could do <cfset caller.a = "whatever" /> and that will set the value in the calling page's variables scope.
If you don't want to modify the custom tag, then you can use <cfsavecontent> to save the output to a variable. Example:
<cfsavecontent variable="a">
<model:sparkline id="1" />
</cfsavecontent>
Sean Coyne's answer is the correct one, provided the import is included within the same context as the cfsavecontent tag:
<cfimport taglib="./tags" prefix="model">
<cfsavecontent variable="a">
<model:sparkline id="1" />
</cfsavecontent>
<cfoutput>#a#</cfoutput>
Will result in the dynamically evaluated output of the sparkline customtag.
It's impossible to OUTPUT the code and have it execute. OUTPUT just means output. It doesn't mean "run".
The only way to get CF code to be executed by CF is to follow normal channels:
* request a template;
* include a template;
* call a template as a custom tag or CFMODULE;
* call a method in a CFC;
* any others? ANyway, you get the point.
So if you have code that you create dynamically and want to execute... you need to write it to a file and then call it via the most appropriate of those mechanisms. Be warned though: running dynamic code like this has a fair overhead, as the code needs to be compiled before it's run, and compilation is not the fastest process in the scheme of things. The "best" thing to do here is to try to write and compile the file before it's needed, and only re-write the file it it needs updatng. Don't re-do it every request. But, ideally, don't do this sort of thing at all. One can usually approach things a different way.