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 RIA Unleashed (Nov. 2009) with:

Learning ColdFusion 9: CFScript Updates For Tag Operators

By Ben Nadel on
Tags: ColdFusion

ColdFusion 9 has made a number of upgrades to CFScript. Yesterday, I explored the CFScript-based updates in ColdFusion Components; today, I wanted to take a quick look at the other CFScript operators that were added to mimic ColdFusion tag functionality. When it comes to these tag operators, we have two basic kinds: those that have content bodies, such as CFThread and CFLock, and those that do not, such as CFParam and CFExit. Service-based tags that have bodies, such as CFMail and CFHTTP, are handles as actual ColdFusion components, not as operators, and will be covered in a later post.

To be completely honest, I find the tag operators to be very inconsistent. Some of them use name-value attribute pairs; some use anonymous strings; some can use either attribute pairs or anonymous strings, but not both (ex. property); some can use comment-based, JavaDoc style attribute definitions; some even have function-based alternatives (ex. throw); and some tags don't even have operator or object-based script equivalents (ex. CFContent)! It seems to be all over the map as far as implementation goes. When it comes down to it, I think you simply have to memorize what goes where and how specific tags operators are implemented (if they're implemented at all) - deduction doesn't seem to be an option.

That said, let's take a look at the tag operators that do not have content bodies. I won't be covering the CFProperty tag operator as that was discussed in the previous post. As this is really just a lesson in syntax, I'll leave most of my explanation in the comments.

  • <cfscript>
  •  
  • // Import a component name space for use with the NEW
  • // opeartor.
  • //
  • // NOTE: You cannot use the import operator to import
  • // tag libraries.
  • import "demo.com.*";
  •  
  •  
  • // Catch any error that was thrown. We will be catching
  • // two errors here to demonstrate both the throw and the
  • // rethrow tag operators.
  • try {
  •  
  • // Thow an error. This tag cannot use name-value
  • // attribute pairs. You can only use one anonymous
  • // string as demonstrate.
  • //
  • // NOTE: This has a function-based alternative,
  • // throw(), that has many more options including
  • // message, detail, and type arguments:
  • // throw( type='', message='', detail='' );
  • throw "You can't do that!";
  •  
  • } catch( any error ){
  •  
  • // Try to catch another error.
  • try {
  •  
  • // Rethrow the current top-level error. This tag
  • // operator does not take any attributes.
  • rethrow;
  •  
  • } catch( any subError ){
  •  
  • // Nothing to do here...
  •  
  • }
  •  
  • }
  •  
  •  
  • // Include another template. This can only use the anonymous
  • // string, not name-value attribute pairs.
  • include "include.cfm";
  •  
  •  
  • // Param the given values. This can handle all of the name-
  • // value attribute pairs that CFParam can handle.
  • param
  • name="name"
  • type="string"
  • default="Tricia"
  • ;
  •  
  •  
  • // Exit out of this page.
  • // NOTE: This cannot use name-value attribute pairs, only
  • // the anonymous string.
  • exit "exittemplate";
  •  
  • </cfscript>

As you can see, I left a number of comments in the code above as to the limitations or alternatives of the tag operators. Now, let's take a look at the tag operators that have content bodies. Once again, as this is really just a lesson in syntax, I'll leave most of the explanation in the comments.

  • <cfscript>
  •  
  • // Lock the current temp on the named lock.
  • lock
  • scope="application"
  • type="exlusive"
  • timeout="10"
  • {
  •  
  • // Copy the application scope.
  • request.appData = duplicate( application );
  •  
  • }
  •  
  •  
  • // Start a transaction
  • // NOTE: We are not going to run any queries since that
  • // is not relevant at this point.
  • // NOTE: Trasnaction functionality has greatly increased
  • // in ColdFusion 9 as well, but can be covered later.
  • transaction
  • action="begin"
  • {
  •  
  • // Run a query.
  • include "insert_query.cfm";
  •  
  • // Save this rollback point.
  • // NOTE: This could have also been accomplished with the
  • // function-based alternateive:
  • // transactionSetSavepoint( "SP1" );
  • transaction
  • action="setSavePoint"
  • savepoint="SP1"
  • ;
  •  
  • // Run a query.
  • include "insert_query.cfm";
  •  
  • // Check to see if something went wrong.
  • if (false){
  •  
  • // Roll back the transaction.
  • // NOTE: This can also be accomplished with the
  • // function-based laternative:
  • // transactionRollback();
  • transaction action="rollback";
  •  
  • }
  •  
  • // Roll back to our save point above.
  • // NOTE: This could have also been accomplished with the
  • // function-based alternative:
  • // transactionRollback( "SP1" );
  • transaction
  • action="rollback"
  • savepoint="SP1"
  • ;
  •  
  • }
  •  
  •  
  • // Launch an asynchronous thread.
  • thread
  • action="run"
  • name="firstThread"
  • appname="scriptdemo"
  • {
  •  
  • // Store a return value.
  • thread.returnValue = "From Thread One";
  • }
  •  
  • // Launch a second thread.
  • thread
  • action="run"
  • name="secondThread"
  • appname="scriptdemo"
  • {
  •  
  • // Store a return value.
  • thread.returnValue = "From Thread Two";
  • }
  •  
  • // Sleep the current thread.
  • thread
  • action="sleep"
  • duration="10"
  • ;
  •  
  • // Check to see if our async thread has completed. If it has
  • // not completed, then terminate it.
  • if (cfthread.firstThread.status neq "completed"){
  •  
  • // Terminate the thread.
  • // NOTE: This could have been accomplished with the
  • // function-based alternative:
  • // threadTerminate( "firstThread" );
  • thread
  • action="terminate"
  • name="firstThread"
  • ;
  •  
  • }
  •  
  • // Join the second thread to the page.
  • // NOTE: This could have also been accomplished with the
  • // function-based alternateive:
  • // threadJoin( "secondThread" );
  • thread
  • action="join"
  • name="secondThread"
  • ;
  •  
  • </cfscript>

As you can see above, the tag operators with bodies, namely CFThread and CFTransaction, have many function-based alternatives. I guess these function-based alternatives will also apply to the tag-based versions of these operators; that said, I wonder how Adobe decided which tag actions to turn into functions and which to leave as just operators. All in all, something here feels very inconsistent. I don't foresee myself switching over to CFScript any time - I'm just a tag man at heart.




Reader Comments

Hey,

For me, I am a fan of the work to advance cfscript. I do about 75% of my coding in script so most of this is a welcome addition to me. Having said that, I have to agree with you. The improvements made for CF9 do seem a little inconstant. It is going to be a challenge to remember what to use and where. I think that the beginner is going to find this all to confusing and shy away from it.

--Dave

@Dave,

I guess I shouldn't view the function alternatives as a problem; after all, those can be use with both script and tag-based coding. But one thing that I don't get is why CFLocation is !only! in method format? This never even made it to an operator.

Of course, in my previous post, Adam Cameron pointed out that not everything with parenthesis is actually a method call (ie. if(), catch()). But, that doesn't make me feel any better. If that was the case, then how do we know when it's an operator and when its a method...and why some have one, some have the other, and some have both?

So ... we've gone from this:

<cflock scope="session" timeout="5" type="exclusive"></cflock>

... to this:

lock scope="session" timeout="5" type="exclusive" { }

That is, we've taken off the angle brackets and replaced an explicit block terminator with an anonymous closing brace.

Welcome back to brace-counting Hell, boys. But it was worth it, right? Right?

While CF remains very verbose, it will be gaining a slightly more lightweight syntax.

One of the issues that disturbs me with Adobe's implementation is the large number of language-level constructs they are introducing, rather than implementing these things as library-level constructs.

For example, in Rails, transactions look like:

ActiveRecord::Base.transaction do
david.withdrawal(100)
mary.deposit(100)
end

'ActiveRecord::Base' is a class, 'transaction' is a class method on class 'ActiveRecord::Base', and the 'do ... end' syntax creates a code block and passes it to the 'transaction' method. The 'transaction' method internally sets up a transaction, runs the code block passed to it, and either commits the transaction or rolls it back depending on whether the block threw an exception.

With NHibernate, transactions look like:

using(var txn = nh.BeginTransaction()) {
david.Withdrawal(100);
mary.deposit(100);
txn.Commit();
}

'using' is a syntax element that permits the object defined in the parentheses to run code after the block of code in the braces ends, whether that block of code in the braces ended successfully or threw an error.

Neither C# nor Ruby has any language-level concept of transactions. It worries me that CF is introducing a language-level concept of transactions into CFScript, making the CFScript language rather complex and irregular. What Adobe should instead be working on is implementing namespaced library functions to implement such things as threads and transactions. (Language-level function-looking things, such as most of the CF standard functions, are not namespaced library functions but rather are language-level syntax elements.)

For example, can I continue to do the following? Or is this CF change a breaking change?

var transaction = new lib.MyCustomTransactionClass(1, 2, "three");
transaction.doSomeTransactionyStuff();

@Ben, please note that methods are functions bound to classes or objects. Unattached functions are not methods - this includes all CF standard functions.

@Justice,

I appreciate your note about methods, but I like to keep things informal around here. As such, I will swap terms like method and function, or class and object and component. I guess it's just my revolting against the fact that in my QBasic class the teacher kept trying to tell me that "subroutines" didn't return values, only "functions" did. Really?? Really?? Well guess what, I like the term FUNCTION better... ok, ok, breathe, that was so many years ago, it's ok, it's over now :)

I guess old habits are hard to break.

@Justice,

What's funny now that I think about it is that it can be completely fluid in ColdFusion. For example, you can take an unattached Function and inject it into a component and bam, you have a Method. So, I guess it really depends on the context, not even so much on what it is.

@Ben, you are correct in noting that, in ColdFusion, functions are methods only when they are invoked in a certain way. But functions cannot intrinsically be methods in and of themselves.

This is actually how JavaScript works as well: the object that the 'this' keyword in a JavaScript function refers to depends on how the function was invoked.

Ben, you mentioned that cfhttp and cfmail are handled as components and you'd cover them later... I'm tying to find documentation on these, and not seeing them in the Adobe beta cfml or dev guide documentation any idea where I can find that?

I have a question:
I'm considering printing Barcodes from CF using a WIndows Mobile device and a Zebra QL420+ mobile printer. Will <cfscript> send form and query data to C# so that I can utilize Zebra's SDK? I haven't tried anything yet. I would like to know is this possible?

Thanks,

Khalif

@Khalif,

I am not sure I understand your question. I would assume that any way that ColdFusion tags integrate with non-CF languages would be the same for CFScript (except for perhaps CFImport).

How do you currently do this with tags?

Hey Ben,

Sorry for digging up an old post but I haven't been able to find this information anywhere.

Is there a CFScript equivalent of cfexecute?

Josh

@Josh,

I don't think there is; but, that would actually be a fun little blog post. Maybe I can whip something up for this.