Coldfusion: How to dump out arguments scope with cfthrow? - coldfusion

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.

Related

Coldfusion 8 how do I get a data log in a cfscript tag?

So I have a cfscript tag that runs some XML data through but I'm getting an error on one of my executes its getting weird data it cant use, I am trying to find out what this data is however, all of my attempts to cfdump this data have failed miserably. Does anyone know of a way to get a data log from inside cfscript data?
I've tried:
-Ending the script writing the dump and restarting the script
-ending the script writing the dump and aborting
-putting the dump at the end of the cfscript
I'm running out of ideas. Thank you ahead of time!
Here's are my tips:
First, create a UDF in tag format - I call my "sdump":
<cfunction name="sdump">
<cfargument name="anyvar" required="true"/>
<cfdump var="#anyvar#"/>
</cffunction>
Then in your cfscript while debugging you can simply do:
<cfscript>
sdump(myProblemobject);
</cfscript>
That keeps you from having to break your script block all the time. I actually include mine in a CFC library of functions that's loaded in Onrequest.
Second, sometimes cfdump doesn't play nice with complex XML. It's gotten really smart in CF 9 and later, but I remember CF8 not always loving complicated XML. In that case you have to use your noggin.
For example, try using toString() on the XML object itself and dumping it in source as in tmpvar = toString(myXml); This is more useful if you split out the specific node that is causing you problems into it's own little xml object. Then use dump or (if dump fails) try "writeoutput();" - which is made like a cfoutput for cfscript - like so:
<cfscript>
xmlTmp = toString(myXml);
writeoutput(xmlTmp);
</cfscript>
Working your way through the xml attribute by attribute and node by node might be tediuous, but it might be easier than walking your eyballs through the raw XML looking for issues.
Remember too that you can validate your XML document or var using the various isXML() functions native to CF (isXML(), isXMLAttribute() etc.). Not sure what the list looked like in CF8. Good luck - these sort of problems are always trial and error I'm afraid. :)

cfInclude without cfoutput tags

Ok, I have template files which I know will require to be between <cfoutput> tags, however placing a <cfoutput> tag around a <cfinclude> won't work, and anything with a <cfmodule> won't work as that doesn't allow me to manipulate the variables scope.
So, any ideas - no matter how complex - which would allow me to include such a template file and have it act as if it's between <cfoutput> tags?
Bit of context: It's for a 'framework' I am working on and requiring every template file to start and end with a <cfoutput> tag seems a real waste, especially because the less 'coldfusiony' the template files look, the better IMO.
I was going to suggest the render() option, with the caveat it's a bloody awful bit of functionality in OpenBDML (or whatever they want to call their version of CFML), and I think should be avoided. I can't see how it doesn't cause the CFML to be recompiled every request, which is a helluva overhead. I think you're better off modifying the mark-up when it gets saved/read to wrap/unwrap it in <cfoutput> tags, that way the resultant CFML will only be recompiled when the file actually changes. Which will be really a lot less often than it's read.
Prompted by your question, I did a bit of a write up on the CFML compile process, showing why having the <cfoutput> tags in a separate file doesn't work. Apologies it took so long to write & follow-up with here.
What we do in wheels is just wrap the cfinclude with a cfsavecontent tag
<cffunction name="renderInclude">
<cfargument name="template" value="string" required="true">
<cfset var myOutput = "">
<cfsavecontent variable="myOutput"><cfoutput><cfinclude template="#arguments.template#"></cfoutput></cfsavecontent>
<cfreturn trim(myOutput)>
</cffunction>
Found an OpenBD specific way to solve this problem. Apparently I wasn't the only one who encountered this problem and OpenBD contains a useful render() which takes care of evaluating cfml content. So I ended up with
<cfset cfml = fileRead(expandPath(...))>
...
<cfoutput>#render("<cfoutput>"&cfml&"</cfoutput>")#</cfoutput>
It's not a beautiful solution, as I destroy the per page compilation the engine would otherwise do, however as the cfml is relatively simple on these pages I assume this not to be too much of an issue. At least it should be less of a performance hit than actually writing the file to disk.

Getting "The format method was not found" when trying to use java.lang.string format method from ColdFusion

I want to do something in ColdFusion that is similar to sprintf in C or Perl. I found this
answer, which seems to be what I'm looking for. However, I can't get it to work.
Here is what I'm trying:
<cftry>
<cfset firstName="John">
<cfset output=createObject("java","java.lang.String").format("Hello, %s!", firstName)>
<cfcatch type="any">
<cfdump var="#cfcatch#" expand="false">
</cfcatch>
<cftry>
And here is what I get:
cfcatch.Message: The format method was not found.
cfcatch.Detail: Either there are no methods with the specified method
name and argument types or the format method is overloaded with
argument types that ColdFusion cannot decipher reliably. ColdFusion
found 0 methods that match the provided arguments. If this is a Java
object and you verified that the method exists, use the javacast
function to reduce ambiguity.
This is an overloaded method, so I did as suggested and used JavaCast on the arguments:
<cfset output=createObject("java","java.lang.String").format(JavaCast('string', "Hello, %s!"), firstName)>
<cfset output=createObject("java","java.lang.String").format("Hello, %s!", JavaCast('string', firstName))>
<cfset output=createObject("java","java.lang.String").format(JavaCast('string', "Hello, %s!"), JavaCast('string', firstName))>
and got the same error every time.
I tried another static method on the String class, valueOf, and it worked fine.
Edit:
I've already seen a comment, and I'm not sure how to respond to those, but maybe I should explain here. What I've shown above is an extremely simplified example of what I am trying to do. The goal is to use a format string to provide lots of formatting in one place, and then simply pass in a list of variables, instead of formatting a bunch of variables and outputting them, or formatting them as I'm outputting them. With the format method, I plan to build a set of format strings that match the output I need, then I will just cfloop or cfoutput over a query, run this one method inside, and get the output I want. No DateFormat, NumberFormat, Left, Right, etc. If I can't get this working, that is plan B though.
I'm running ColdFusion 9.01, Windows 7, Java 1.6.0_22.
Any and all help is greatly appreciated.
Answer based on my comment above:
It probably can't match the format method you're looking for because its signature is format(String, Object[]) and your second String argument can't be automatically converted to an Object array.
You could change the argument to encapsulate the name in an array as follows: format("Hello, %s!", [firstName]).
Thanks to Leigh for being so courteous, I upvoted your comments :)

How can I have ColdFusion tags in a variable and have them evaluated?

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.

How do I force evaluation of a cfif stored in a string?

I am trying to store coldfusion code in a database to be used for the subject of a cfmail. The code stored is as follows:
"RE: <cfif myData.general.legalName NEQ """"> {{dotlegalname}}<cfelse>{{docketLegalName}}</cfif>,
DOT## {{dot}}, Docket ##(s) {{docketString}}"
When I retrieve string from the database, I use cfsavecontent to attempt to evaluate it.
<cfsavecontent variable="subject">
<cfoutput>#myData.email.subject#</cfoutput>
</cfsavecontent>
I also tried
<cfsavecontent variable="subject">
<cfoutput>#evaluate(myData.email.subject)#</cfoutput>
</cfsavecontent>
And then I replace all the {{ }} with the appropriate values.
However, the subject of the email is stubbornly refusing to contain an evaluated cfif, and is instead showing the cfif as if it were a string.
Any ideas?
The only way to dynamically evaluate code that you are creating at runtime is via writing it out to a file, and then executing it.
The easiest way would be to write it a .cfm page in the Virtual File System (probably name the file after a UUID, so it's unique), and then it where you need to run the contents.
I wouldn't normally advocate generating code at runtime like this, but it can be the most elegant solution in some cases.
As an alternative, instead of storing the CFML code in the database, you have a set of CFML email template files that get stored in a directory on your server, and in your database you simply record which template needs to be included either via cfinclude or cfmodule.
You can't dynamically evaluate CFML stored in a database without first writing it to file and then using <cfinclude> to include it.
Further to Mark's answer here is some psuedo code:
<cfset fileName = createUUID() & ".cfm">
<cfset fileWrite( fileName, [CODE_FROM_DB]>
<cfinclude template="#fileName#">
<cfset fileDelete( fileName )>
I have used code like this before with no problems. Anything in the Virtual File System flies as it is all run in RAM. For best practice do remember to delete the files created ;)
If you absolutely have to do this, look at the evaluate() function. This, essentially, fires up a new CF thread, compiles the string passed to it, runs it, and returns the result.
If at all possible, I would try to find a way to move your logic to the actual file being run, not the string from the database. I assume you are pulling the data based on some string you've already built, so you might consider appending something to it, so you are looking up subjectDotLegal and subjectDocketLegal or something similar.
Remember, evaluate() is slow, ugly, and can be dangerous (it will run anything passed to it!). If there's a way around it, I suggest you use it.
why not just use something like mustache?
http://mustache.github.com/
https://github.com/pmcelhaney/Mustache.cfc
it has the ability to not only do some of the logic that you want in your script dynamically. i really would suggest you check out the project and maybe even improve and contribute on it.
OH and just for the chance to be on a soapbox: I've been emailing Adobe for years saying that we need the ability to dynamically parse and render CFML. Sadly my cries have only gotten ignored. maybe if more people complained that this feature needs to be added, it would get the attention it deserves.
To give an example: Assume code.txt is a text file that contains the following (just to facilitate simulating CFML stored in a db): <cfoutput>#now()#</cfoutput>
The following code would work:
<cfset q = queryNew("code") />
<cfset queryAddRow(q,1) />
<cfset querySetCell(q, "code", fileRead(expandPath('code.txt')), 1) />
<cfdump var="#q#">
<cfset newCodeFile = expandPath('dynamic.cfm') />
<cfset fileWrite(newCodeFile, q.code[1]) />
<cfinclude template="dynamic.cfm" />
In OpenBlueDragon there is the render function, which can do this.
You can mimic this function in Railo by creating a custom built-in function that saves the file into RAM then cfincludes it, using the following code:
<cffunction name="render" output="Yes" returntype="string"><!---
---><cfargument name="Code" required="Yes" type="string"><!---
---><cfset local.mapping = {'/render_ram_resource':'ram://'}><!---
---><cfapplication action="update" mappings="#local.mapping#"><!---
---><cfset local.fileName = "/render_ram_resource/_render_" &
createUUID() & ".cfm"><!---
---><cffile action="WRITE" file="#fileName#"
output="#arguments.Code#"><!---
---><cfinclude template="#fileName#"><!---
---><cffile action="DELETE" file="#fileName#"><!---
---></cffunction>
(This looks unusual because it needs to allow output, but prevent extra whitespace, hence why all the comments. Unfortunately SO's syntax highlighting seems to be confused by them.)
If you need an ACF-compatible solution, you'll need to use the regular filesystem and a pre-created mapping. (Well, in ACF9 and above you can use the RAM virtual filesystem, but afaik you can't create mappings on the fly like this.)
There's a better way, namely using in memory files. This way you don't have any I/O on the disk and therefore much faster:
For tags that take logical path, define mapping in Administrator. Execute in-memory CFM pages using the cfinclude tag:
Create a mapping for ram:/// so that it can be used in the tags. In this example, /inmemory is the mapping that points to ram:///.
For tags that take absolute path, specify the syntax as provided in the following example:
You can also delete the file from the ram usinf cffile and action delete.
Here's how I stored my header and footers for all pages in a record. This code can go at the top of each page. But I have it in the APPLICATION.cfm and it seems to be working great.
The key here is not use #pound# signs on your expressions. User [square braces]. The code will pick them and evaluate them and return the result back to the template.
It will substitute the number 0 if it can not evaluate an expression as a means of error handling.
<CFSET FooterID=1234> <!-- ID of the record you want to use -->
<CFQUERY NAME="StoredHeader" Datasource="DS1">
Select Body from templates where id=#FooterID#
</CFQUERY>
<CFSET Parse=StoredHeader.Body>
<CFLOOP CONDITION="FindNoCase('[',Parse,1) GT 0">
<CFSET STB=FindNoCase('[',Parse,1)>
<CFSET ENB=FindNoCase(']',Parse,1)>
<CFIF ENB-STB GT 0>
<CFSET BracketExp=Mid(Parse,STB+1,ENB-1-STB)>
<CFTRY>
<CFSET BracketValue=Evaluate(BracketExp)>
<CFSET Parse=ReplaceNoCase(Parse,'['&BracketExp&']',Evaluate(#BracketExp#))>
<cfcatch type="any">
<div>'Using ZERO 0 for missing <cfoutput>#BracketExp#' </cfoutput> </div>
<CFSET Parse=ReplaceNoCase(Parse,'['&BracketExp&']','0')>
</cfcatch>
</CFTRY>
</CFIF>
</CFLOOP>
<CFSET Footer=Parse>
<cfoutput>FOOTER</cfoutput>
I would try the built-in QuoteName function.