Yesterday a friend on twitter was upset by the fact there was no CFWDDX support within ColdFusion scripting. This got me thinking other things not yet supported and ways that developers can work around them. While scripting has gotten very close to being "100% complete" under ColdFusion 901, there are definitely a few things left you still cannot do (WDDX being one of them) and I thought a discussion about ho to handle that might be nice.
First though - remember that it can sometimes be difficult to see what is supported in scripting because some features aren't listed where you might expect them to be. Consider "try", as in cftry. If you look in the CFML Reference you will find documentation for cftry but see no mention of how to use try within script. You may be included to check the function listing, but it is not there. That's because the try command is a keyword, not a function. You can find a good list of these keywords in the documentation within the Developer's Guide: What is supported in CFScript. Unfortunately this page doesn't tell you how to use these keywords. So for example - consider cflocation. Here is an example of how it is used:
location(url=group.getHomeUrl(),addtoken=false);
That's nice - but on the flip side, include, the script equivalent of cfinclude, works like so:
include "render/blogpaneledit.cfm";
These are kind of documented here: Using system level functions But there is no mention of include, or try for that matter.
So that's keywords, but then you also things that are supported via new components. These components may be found in cfinstall\customtags\com\adobe\coldfusion. A documented list may be found here: Script Functions added in ColdFusion 9. These are detailed in the Reference: Script Functions Implemented as CFCs. Be sure to notice the link to the enhancements in 901: Script functions implemented as CFCs in ColdFusion 9 Update 1.
Speaking of 901, don't forget that support for handling file uploads, via fileUpload, as well as directory commands, were added in 901. These were both things that forced me to use tag based CFCs at times in CF9.
Ok - so I said this blog entry was about workarounds for things not supported. Hopefully though you check these docs first to ensure that what you want to do is actually impossible. I've used a few different workarounds in my development and at work. Here are a few examples.
- What about cfsetting?
cfsetting has a few different options in it, but the one we use mostly is showdebugoutput="false". This is critical for pages that may serve up JSON or XML to remote clients. Unfortunately this is not possible to do in scripting. In the past, I've seen this workaround used:
include "setting_debug.cfm";
The file, setting_debug.cfm, will have a grand total of one line - a tag based cfsetting. That kinda sucks. But it also means the calling CFC can stay in script. Since you typically only need one of these, the one hack by itself isn't so bad.
- What about tags like wddx, etc, not supported?
You have a few options. For something like WDDX, it wouldn't take very long to write a UDF "wrapper" for the tag that handles serializing and deserializing WDDX strings. Once done, you can include this into your script based CFC or inject it like a service via your favorite dependency injection tool. This is the route I took for Adobe Groups and file uploads. I wrote Adobe Groups when CF9 was released. I had a few services that needed file operations, like uploading processing, so I simply wrote one tag based CFC to handle all file operations. It was a simple utility component that other services (fancy script based ones) could easily make use of. This wouldn't be necessary in 901.
Another route is to go the component route. Consider that cffeed was added in 901 as a component and not a new function. Unlike WDDX support, RSS parsing is somewhat more complex. Therefore it makes since to have a CFC/object based type approach to handle it. I helped write this component for Adobe. The code in that folder is all unencrypted and follows a standard style that you could mimic yourself. That's exactly what I did. I looked at Adobe's code for the other CFCs, wrote the feed support, and just stored it in the same directory. This gave me feed support in CF9. When 901 was released, I just removed my version.
Finally - don't forget you can file an enhancement request for things like this (script based version of cfwddx). If Adobe doesn't know that this is important to you, it won't be very high on their list of things to work on. The more you document, and vote, the easier it gets for Adobe to prioritize development.
Archived Comments
Cool approaches. One thing I have done is create a tag.cfc like so:
tag.cfc:
<cfcomponent>
<cffunction name="cookie">
<cfcookie attributeCollection="#arguments#">
</cffunction>
</cfcomponent>
Then in the script do:
tag=new tag();
tag.cookie( name="cf", value="cool", httpOnly="true" );
You can drop tag.cfc in the directory as you mention or define a folder in your app via custom tag mapping and put it in there.
Cookie is another good example. Thanks Sam.
Great stuff Ray. I didn't realize you had helped to write the port for cffeed. Very cool.
Some time back I wrote a post about Extending the Server (http://blog.cutterscrossing... that covered the basics of creating component objects for use across the entire server. I've lacked time, this past year, but think it would be great, as a community, to begin to fill in the gaps. Maybe Adobe could then use those projects as the basis for final implementation? Not sure of the best way to handle some of the smaller items, like cfsetting, but more complex tags, like cfsavecontent, might be really good candidates. I guess it's a matter of defining a standard convention, so that stuff isn't overly fragmented.
Dude - did you know savecontent is supported in script?
No, I didn't. I'll have to go check the docs on that. Was that added in 9.01?
Added in 9 (but hidden deep in the docs).
Syntax:
savecontent variable="local.content" {
writeOutput(string);
writeDump(arguments);
}
It's not hidden - it's in one of the links above. However, the _syntax_ is hidden, like most of the stuff.
What we need is a blog post with all these new keywords and simple examples.
Agreed?
Well, what we *need* is for Adobe to write the docs coherently.
That would be better than a blog post. No dis' meant, Ray ;-)
But a blog post would be more like to happen.
Actually, if you were inclined to write something on this, a better approach would be to annotate livedocs rather than write something entirely separate & disconnected, wouldn't it?
Do livedocs annotations get indexed by Google (etc)?
--
Adam
@Adam: I'm offended. You are not allowed to comment until you buy me another beer. ;)
Um - I'm petty sure livedocs do get aggregated as they come up in my search. Do the _comments_? I don't know.
I agree in general - but I'm not sure this would be appropriate for comments. May be a bit much for the context. Meh.
I need to excuse to blog - and frankly - I forget myself. I use CFB Snippets for some things (like query in CFC form), so this could be useful to me.
Hi Ray - FYI I tried to read this on my Droid X and I could not seem to get past this page:
/www.coldfusionjedi.com/mobi...
Great stuff (as always!)
Andy
Ping me via email please. The mobile version was updated yesterday - still has kinks to work out. If you can, tell me what you had clicked first, where you ended up, etc.
Looks like Ben's already got a good one pager on the new keywords:
http://www.bennadel.com/blo...
Damn - and after I had written a little demo.
You should still post your entry Ray.
Just because one person has posted something doesn't mean others can't! Plus I don't have to scroll as much on your entries! :)
how would you handle the group attribute of cfquery?
I would love to see cfmodule implemented in script. Yes, I've still got a bunch of custom tags I use!!!
@Sam: Heh, I don't think I'd have anything better than his - but - I may share my CFB snippets. I made some as I had issues with remembering the syntax. To be honest, it is weird to me that same of these things work like this
include "foo.cfm"
and some work like so
throw(.....)
@Daria: You mean cfoutput don't you? If so - I don't believe you can handle that at all now. You would need to do it manually.
@JP: In theory - you could build a UDF to replicate that. Need an example?
Mate, just keep a tally of how many beers you figure I owe you, and I'll see ya right next time you're across here (which is more likely than me being over there).
Livedocs itself is indeed indexed by Google. Fortunately. Because Google does a much better job than Adobe's search engine (not that that is so different from most site's search engines, so no indictment of Adobe meant there).
However the annotations are *not* indexed, unfortunately. I guess this is because - for reasons best known to themselves - Adobe load those as separate requests, rather than just do it as the main request. Sigh.
I think there should be a separate section in the docs that covers the non-tag-implementations of all functionality as thoroughly as the existing reference covers the tags and the functions. And each should be cross-referenced to the other.
But anyway.
I too was bemused why sometimes various - seemingly equivalent - operations take parentheses, and others just quotes. It's the sort of non-uniformity CFML really doesn't need.
Cheers for the heads-up from Sam re savecontent. I didn't know that. I think it's good it's there for completeness sake, but it's not very elegant compared to the tag analogue, is it?
Grouped query looping using a for() would be bloody handy, I agree. It doesn't specifically mention gropupings, but there's an issue in the bug tracker for getting query-looping added to script. It might be worth voting / commenting on.
--
Adam
@Adam: re: savecontent
I agree - it isn't elegant - but you don't normally do a lot of outputs in script anyway. I _have_ used this in Adobe Groups though. A lot of the CMS is CFC driven, of course, and many of my CFCs need to generate output. Since my CFCs were script based and I didn't want a bunch of string ops, I did something like this:
function renderPoo() {
var result="";
savecontent variable="result" {
include template="poo.cfm";
}
return result;
}
So the savecontent keywords works great for me here. Certainly a rare use case for me, but I'm happy it's there.
@Ray
I'm dying over here...
I mean, renderPoo()? Really?
... ow, I feel like I'm laughing up some six pack abs ...
Sorry for the comment that adds nothing to the topic. :-)
I turned 38 today. Unfortunately my maturity is still hovering around 12. On a good day.
Happy birthday matey.
Add another beer to the list.
--
Adam
Thank you sir.
Many (belated) Happy Returns, Raymond (only 38?)
Totally agree with Sam, you shouldn't worry about duplicating topics: if an issue's important to me, then the more perspectives I can get on it the better.
I'll post it up Monday.
I did post it on Monday. Just not the Monday I thought:
http://www.coldfusionjedi.c...