Using ColdFusion Tags In CFScript In Lucee CFML
Last week, I had lunch with Gert Franz - co-creator of the open source ColdFusion-compatible language, Lucee CFML. At the meeting, I was telling Gert how using Lucee over the last year or so has really reignited my love and passion for ColdFusion programming. ColdFusion just makes things easy, combining the best of both worlds when it comes to synchronous and asynchronous programming. In response to this, Gert was trying to list out some interesting things that Lucee does, one point of which was that I can generically use ColdFusion Tags in CFScript by just removing the <cf
prefix. Being quite late to the Lucee CFML party, this was news to me! So, I wanted to take a minute and try it out for myself.
Now, to be clear, I am not saying that I want to use all of the following ColdFusion Tags in CFScript. For example, I don't think I would want to use the <cfquery>
tag in CFScript when I could just use queryExecute()
. But, I am intrigued that all of the following work. And, I will definitely be making use of tags like <cfheader>
, <cfcookie>
, <cfhttp>
, <cfexecute>
, <cfthread>
, and <cfmail>
in my CFScript.
<cfscript>
// The following is just a NOTE TO SELF about the fact that ColdFusion tags can be
// used in CFScript within Lucee CFML by generically removing the "<cf" prefix and
// then using Open/Close {curly braces} to indicate Tag Bodies (as needed).
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
param
name = "url.foo"
type = "string"
default = "bar"
;
dump( url.foo );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
header
statusCode = 202
statusText = "Accepted"
;
header
name = "X-Ben-Test"
value = "I iz in ur heder makin it bettr!"
;
flush
interval = 10
;
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
loop
index = "i"
from = 1
to = 5
step = 1
{
dump( i );
}
loop times = 3 {
dump( "Times!" );
}
loop
index = "line"
file = expandPath( "./data.txt" )
startLine = 2
endLine = 4
{
dump( line );
}
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
execute
variable = "lsResult"
name = "ls"
arguments = "-al"
timeout = 5
;
dump( lsResult.left( 30 ) );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
http
result = "httpResult"
method = "get"
url = "https://www.bennadel.com"
timeout = 5
cachedWithin = createTimeSpan( 0, 0, 10, 0 )
;
dump( httpResult.statusCode );
http
result = "httpResult"
method = "get"
url = "https://www.bennadel.com"
timeout = 5
cachedWithin = createTimeSpan( 0, 0, 10, 0 )
{
httpParam
type = "url"
name = "site-photo"
value = 552
;
}
dump( httpResult.statusCode );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
cookie
name = "benTestCookie",
value = "cfscript be hot!",
expires = 1,
secure = true,
httpOnly = true,
domain = ".invisionapp.com",
preserveCase = true
;
dump( cookie.benTestCookie );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
zip
action = "zip"
file = expandPath( "./things.zip" )
overwrite = true
{
zipParam
content = "I am some content."
entryPath = "/README.md"
;
}
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
include "./other-test.cfm";
include
template = "./other-test.cfm"
cachedWithin = "request"
;
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
log
text = "Check yourself, before you wreck yourself."
type = "warning"
;
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
timer
type = "outline"
label = "Go go go!"
{
sleep( randRange( 10, 20 ) );
dump( "And, done." );
}
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
mail
to = "kim@bennadel.com"
from = "ben@bennadel.com"
subject = "Is it me you're looking for?"
type = "html"
async = true
{
mailParam
file = "lyrics.txt"
type = "text/plain"
content = "Hello, is it me you're looking for?"
disposition = "attachment"
;
mailPart type = "text/html" {
echo( "<strong>Hello</strong>..." );
include template = "./mail-body.cfm";
}
mailPart type = "text/plain" {
echo( "This is the plain-text version." );
}
}
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
thread
name = "myThread"
action = "run"
priority = "low"
{
thread.foo = "bar";
}
thread action = "join";
dump( cfthread.myThread.foo );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
query
name = "data"
returnType = "array"
{
echo( "SELECT * FROM user WHERE id = " );
queryParam
value = 1
sqlType = "integer"
;
}
dump( data[ 1 ].name );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
xml variable = "xmlDoc" {
echo( "<p>Boop!</p>" );
}
dump( xmlSearch( xmlDoc, "//p/text()" ) );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
silent bufferedOutput = false {
dump( "Can you hear me now?" );
}
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
setting
requestTimeout = 10
enableCFOutputOnly = false
;
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
module
template = "./tagger.cfm"
foo = "bar"
;
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
function makeItSo( a, b ) {
return( "Indeed! #a#, #b#" );
}
invoke
returnVariable = "result"
method = "makeItSo"
{
invokeArgument
name = "1"
value = "Hello"
;
invokeArgument
name = "2"
value = "World"
;
}
dump( result );
</cfscript>
At you can see, all I'm doing is take a ColdFusion tag like <cfhttp>
, removing the <cf
prefix, and then using open/close curly braces to indicate the tag body. And it just works.
I'm sure that for those of you who have been using Lucee CFML for years, this is old hat. However, I went from Adobe ColdFusion 10 - which had minor CFScript support, including a hodge-podge of native constructs and shoe-horned ColdFusion component - to Lucee CFML 5.2, which has, more-or-less, complete ColdFusion tag support in CFScript. So, this is new and exciting and fresh for me!
Want to use code from this post? Check out the license.
Reader Comments
Lucee, and Ortus, have done more for ColdFusion in the past few years than I think Adobe has done in a decade.
@Ray,
10000% I was actually just saying that to Gert on Friday! You took the words right out of my mouth :D
Lucee also introduced something called "tag islands" which are basically the opposite of cfscript tags inside a tag-based cfc and allow you to write tag based code inside a script-based cfc.
So for instance, if you wanted to have all of your cfc's in script, but still prefer writing your queries using the cfquery tag, you could do so by putting the tag-based code inside 3 backticks.
Whether or not this is a good practice and/or your editor can parse such code is another story...
@Andrew,
Oh, really interesting! I had not heard - or don't remember hearing - about this before. I know years ago, people at Adobe were talking about trying to implement E4X as a kind of tag-based implementation inside CFScript (for things like XML). But, I don't think anything came of it.
I'll have to take a look. I agree in that I am not sure if it's a good idea :D But, worth at least poking at it a few times.
@Andrew,
Hey man, thanks for pointing this out! I did a little exploration this morning:
www.bennadel.com/blog/3768-exploring-tag-islands-tags-in-cfscript-in-lucee-cfml-5-3-1-13.htm
This is really cool! I am particularly excited at the idea of using
CFQuery
andCFQueryParam
tags within Script-based Data Access Objects. Woot woot!Does this work with custom tags as well?
@Frederic,
It works for custom tags if you invoke them via
module
as in:But, I don't think it works with the
cf_
terminology. Though, I'm not 100% sure on that.It works also with custom tags as well as with built in cfc based custom tags. But this has only been introduced lately say 3 months ago.
Originally we defined the feature because we wanted it to be very seamless, when switching from tag to script. That's why we introduced the tag islands as well.
In addition the tag notation in cfscript is a LOT faster. Take cfloop vs. for() and you can see the difference. Also it works with every type of loop vs. the for() whereas Adobe only supports a limited subset with cfloop().
@Gert,
Are you saying that in Lucee ALL "tag notation in cfscript" are faster than their cfscript counterparts? Or is your comment just in reference to cfloop vs. for()?
If the latter, then I imagine is has to do with compiler optimizations made for those specific looping tags but if you meant the former, then I am curious why that would be the case?
It has to do with the comparison in the for construct. When you use a cfloop with fixed values, the comparison is easy when converting to java. So some tags might be faster some equally fast but never slower...
Gert
I came from a background of using ACF as well and when I started using Lucee I was very happy to find out that you had the option to remove the cf_prefix. I don't remember how far back it was posted but at what point Lucee was talking about making a .lucee extension that would support lucee specific implementation of cfml as well as ACF implementation. That post mention removing the cf_prefix from tags as well. Instead of
<cfscript>
, we'd be looking at<:script>
, or<:output>
. Wonderbar!! I'm hoping they implement this is Swansea Jack.On analyzing this, I was trying to figure out why removing the cf would matter? For now, It just seems cool.
@All,
One thing I stumbled upon is that the
throw()
BIF (built-in function) works really well inCFScript
; but, thethrow
tag seems to ignore most of the attributes. I may be doing it wrong, so I'll go back an double-check. But, just a heads-up.@All,
In the month-and-a-half since I wrote this post, I've been making heavy use of both the unified tag-syntax and tag islands in Lucee CFML, and I've come to believe this kind of makes CFScript perfect:
www.bennadel.com/blog/3793-tag-islands-and-cfscript-based-tags-bring-perfection-to-coldfusion-in-lucee-cfml-5-3-4-80.htm
I'm a little bit freaking out about it. It just feels like it brings unparalleled developer ergonomics to ColdFusion that I haven't seen in any other language (though, to be fair, my expose to other languages is fairly limited). But, it's just kind of bananas. And, I'm freaking out that more people aren't freaking out about how awesome this is!
@Raymond,
What are these Ortus contributions you speak of? I, like Ben, have only recently been introduced to the magical world of Lucee. Amazing effort to see for those of us that thought CF2 was the best thing since Server Side JavaScript (btw, I hear it's back on the server side). I just wish an EC2 AMI was easier to come by so everyone could see how awesome it is.
@Igor - a lot of things, they have a ton of open source contributions, they run a great conference, but most of all (for me) is the "box" command line, which lets you go into a directory, type "box server start", and it fires up a CF or Lucee server right from the directory. No need to install CF (or modify an existing one to use the folder you are in). You can have N projects with each one having their own version of CF or Lucee and all fired up quickly (after the first run) from the terminal.
I like to say that if they had had that feature years ago, I may not have left CF. :)
@Raymond, Ah, cool. I've heard the terms ColdBox and CommandBox thrown around, but never dove down that rabbit hole and was too embarrassed to display my ignorance. Good to know!
But no one ever leaves CF. The Allaire brothers embedded a subliminal code into the old lightning bolt that triggers a withdrawal anytime someone tries to learn a new programming language. It's happened to me a few times now.
@Ben @Raymond I downloaded commandBox about a year ago and then never did anything with it. After I saw @Raymonds post on spinning up servers within a directory I upgraded my install with brew and am looking forward to diving into it some.
@Ray, @Igor, @Hugh,
I've only scratched the surface with CommandBox, and already it's been a huge help. For example, I can put these two files in a directory,
server.json
And,
.cfconfig.json
:And then run
box
andstart
from the terminal and CommandBox will automatically spin up a Lucee CFML server using the latest engine; and then, the CFConfig module (which I think is built-in) will use the.cfconfig.json
file to auto-configure the server (in this case, setting the password). Of course, the CFConfig module can do loads more, like setting up data-sources and mail-servers and all that jazz.Once of these days, I have to learn more about the whole module system (ie, their version of
npm
). I haven't quite gotten that far; but, I'm using CommandBox every day for R&D.Ben, first of all, another helpful article (and complemented by great discussion in the comments!) explaining things in ways I tend to understand better than at many other blogs :-) . Secondly, having often marveled at your aforementioned CF competence, it made me smile I actually discovered commandbox slightly before you, haha. But what a game changer it truly is - Brad and the other Ortus guys are super helpful over at the CFML / box-products & Lucee channels in Slack - join it if you haven't already, very nice gang over there. Greetings from Sweden and another old-timer - Allaire ColdFusion 4.0 was my intro I think.
@Jonas,
The Ortus team is just amazing. I feel like they, along with the Lucee team, are single-handedly saving the ColdFusion community. I don't know what I would be doing without them.
@Ben, yes, totally feel the same way. I only recently became a small* Patreon to Ortus which I hope more will do (*after having a major client-project go bust last year it's a bit tight atm but I plan on becoming a business supporter next year), they truly churn out massive positive energy & projects to the community.
For those interested in learning more about the open-source "box" products like spinning up servers easily with Commandbox, or writing MVC apps with Coldbox, you can check out Ortus' CFCasts website here which has a variety of free and paid tutorials: https://cfcasts.com/ 💪
Also, if you haven't been to the Into the Box conference before, I highly recommend checking it out. It's both informative and a total blast. Plus, they always have a mariachi band one night. 🎉