Skip to main content
Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.

Using ColdFusion Tags In CFScript In Lucee CFML

By Ben Nadel on
Tags: ColdFusion

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.

Ben Nadel with Gert Franz in New York City, February 2020.

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 = "www.bennadel.com"
		timeout = 5
		cachedWithin = createTimeSpan( 0, 0, 10, 0 )
	;

	dump( httpResult.statusCode );

	http
		result = "httpResult"
		method = "get"
		url = "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 <cfprefix, 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!



Reader Comments

@Ray,

10000% I was actually just saying that to Gert on Friday! You took the words right out of my mouth :D

Reply to this Comment

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...

Reply to this Comment

@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.

Reply to this Comment

@Frederic,

It works for custom tags if you invoke them via module as in:

<cfscript>

	module template="my_custom_tag.cfm" {
		/// ... custom tag body ....
	}

</cfscript>

But, I don't think it works with the cf_ terminology. Though, I'm not 100% sure on that.

Reply to this Comment

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().

Reply to this Comment

@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?

Reply to this Comment

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

Reply to this Comment

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.

Reply to this Comment

@All,

One thing I stumbled upon is that the throw() BIF (built-in function) works really well in CFScript; but, the throw 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.

Reply to this Comment

@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!

Reply to this Comment

@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.

Reply to this Comment

@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. :)

Reply to this Comment

@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.

Reply to this Comment

@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.

Reply to this Comment

@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

{
    "app":{
        "cfengine":"lucee"
    }
}

And, .cfconfig.json:

{
    "adminPassword": "password"
}

And then run box and start 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.

Reply to this Comment

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.

Reply to this Comment

@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.

Reply to this Comment

@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.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Blog
Live in the Now
Oops!
NEW: Some basic markdown formatting is now supported: bold, italic, blockquotes, lists, fenced code-blocks. Read more about markdown syntax »
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.