Skip to content

Latest commit

 

History

History
1909 lines (1522 loc) · 50.3 KB

README.md

File metadata and controls

1909 lines (1522 loc) · 50.3 KB

CFML Tag to Script Conversions

A collection of examples demonstrating the conversion of CFML code blocks written in tags to CFScript.

Currently a work in progress!

This is not the CFScript syntax documentation you're (possibly) looking for. That awesome piece of work, by Adam Cameron, can be found here and is highly recommended as a reference to the content you'll find here.

If you happen to be searching for a resource to learn CFML, check out CFML in 100 Minutes by Mike Henke & Learn CF in a Week by this solid bunch of devs.

The examples in this document are an attempt to demonstrate conversions of CFML tag-based code to script-based code as a way of learning / migrating to CFScript. It is assumed that you already have a relatively firm understanding of the ColdFusion Markup Language (at least on a tag-based level). For the sake of modernity, the converted examples will be written to comply (from an agnostic approach) with the most current versions of the various CFML engines at the time of writing - (ColdFusion 11, Railo 4.2 & Lucee 4.5).

Table of Contents

  1. The Modern Implementation of Tags to Script
  2. Comments
  3. Variables
  4. Operators
  5. Conditions
  6. Iterations
  7. Other Flow Control Tags
  8. General
  9. Debugging
  10. Database
  11. File System Operations
  12. Image Manipulation
  13. Spreadsheet Integration
  14. Tags Implemented as Components
  15. Interfaces, Components & Functions
  16. Real World Conversions By Example
  17. Tags Have Their Place

The Modern Implementation of Tags to Script

I want to open with this before diving into the depths of conversions. Adobe ColdFusion 11+, Railo 3.2+(?) and Lucee 4.5+ all offer some kind of full script syntax support (Railo & Lucee sharing the implementation) involving near identical syntax between tag-base and script-base. Adobe ColdFusion 11 rolled it's own variation but Railo and Lucee both support ACF's syntax as of versions 4.2 and 4.5 respectively.

As per Adam Cameron's CFScript Documentation:

On Railo/Lucee this is a matter of removing the "<cf" and the ">", and using normal block syntax (curly braces) where the tag-version is a block-oriented tag.

On ColdFusion, replace the "<cftagname" with "cftagname(", and the ">" with ")", and comma-separate the attributes. Note that this will make the construct look like a function, but it actually is not, and cannot be used like a function, eg this is invalid syntax:

result = cfhttp(method="post", url="http://example.com");

Where the correct way would be: cfhttp(result="result", method="post", url="http://example.com");

Comments

Tags:

<!--- I'm a single-line comment. --->

<!---
I'm a multi-
line comment.
--->

Script:

<cfscript>

// I'm a single-line comment.
// Notice that CFScript code within a ".cfm" file must be wrapped in <cfscript> tags.

/*
I'm a multi-
line comment.
*/

</cfscript>

Variables

Tags:

<!--- Some simple variable statements in tags --->

<!--- Default variable declarations --->
<cfparam name="title" default="">
<cfparam name="views" type="numeric" default=0>

<!--- Regular assignment --->
<cfset title = "Tags">
<cfset views = 1>
<cfset page = "Title: #title#, Number: #views#">

Script:

<cfscript>

// Some simple variable statements in script

// Default variable declarations
param name="title" default="";
param name="views" type="numeric" default=0;

// Regular assignment
title = "Script";
views = 2;
page = "Title: #title#, Number: #views#";
// or
page = "Title: " & title & ", " & "Number: " & views;

</cfscript>

Operators

Decision

Tags:

<cfset x = 1>
<cfset y = [
	x EQ 1,
	x NEQ 1,
	x LT 1,
	x GT 1,
	x LTE 1,
	x GTE 1
]>
<cfdump var="#y#">

Script:

<cfscript>

// All tag based operators still work in script
// There are also these equivalents

x = 1;
y = [
	x == 1,
	x != 1,
	x < 1,
	x > 1,
	x <= 1,
	x >= 1
];
writeDump(y);

</cfscript>

Increment / Decrement

Tags:

<!--- Increment --->
<cfset x = 1>
<cfset y = x++>
<cfset z = ++x> <!--- or z = x + 1 --->
<cfdump var="#[x, y, z]#">

<!--- Decrement --->
<cfset x = 1>
<cfset a = x-->
<cfset b = --x> <!--- or b = x - 1 --->
<cfdump var="#[a, b]#">

Script:

<cfscript>

// Increment
x = 1;
y = x++;
z = ++x // or z = x + 1
writeDump([x, y, z]);

// Decrement
a = x--;
b = --x // or b = x - 1
writeDump([a, b]);

</cfscript>

Inline Assignment

Tags:

<cfset x = 0>
<cfset y = "string">

<cfset x += 10>
<!--- or x = x + 10 --->
<cfdump var="#x#">

<cfset x -= 8>
<!--- or x = x - 8 --->
<cfdump var="#x#">

<cfset x /= 6>
<!--- or x = x / 6 --->
<cfdump var="#x#">

<cfset x *= 4>
<!--- or x = x * 4 --->
<cfdump var="#x#">

<cfset x %= 2>
<!--- or x = x MOD 2 or x = x % 2 --->
<cfdump var="#x#">

<cfset y &= "s">
<!--- or y = y & "s" --->
<cfdump var="#y#">

Script:

<cfscript>

x = 0;
y = "string";

x += 10;
// or x = x + 10
writeDump(x);

x -= 8;
// or x = x - 8
writeDump(x);

x /= 6;
// or x = x / 6
writeDump(x);

x *= 4;
// or x = x * 4
writeDump(x);

x %= 2;
// or x = x MOD 2 or x = x % 2
writeDump(x);

y &= "s";
// or y = y & "s"
writeDump(x);

</cfscript>

Boolean

Tags:

<cfset x = y = 1>
<cfset z = [
	NOT x,
	x EQ 1 AND y EQ 2,
	x EQ 1 OR y EQ 2
]>
<cfdump var="#z#">

Script:

<cfscript>

x = y = 1;
z = [
	!x,
	x == 1 && y == 2,
	x == 1 || y == 2
];
writeDump(z);

</cfscript>

Ternary & Null-Coalescing

Tags:

<!--- Ternary --->
<cfset x = "Tags">
<cfset y = len(x) ? x : "something else">
<cfdump var="#y#">

<!--- Null-Coalescing --->
<cfset y = z ?: "something else">
<cfdump var="#y#">

Script:

<cfscript>

// Ternary
x = "CFScript";
y = len(x) ? x : "something else";
writeDump(y);

// Null-Coalescing
y = z ?: "something else";
writeDump(y);

</cfscript>

Conditions

cfif / cfelseif / cfelse

Tags:

<cfset count = 10>
<cfif count GT 20>
	<cfoutput>#count#</cfoutput>
<cfelseif count EQ 8>
	<cfoutput>#count#</cfoutput>
<cfelse>
	<cfoutput>#count#</cfoutput>
</cfif>

Script:

<cfscript>

count = 10;
if (count > 20) {
	writeOutput(count);
} else if (count == 8) {
	writeOutput(count);
} else {
	writeOutput(count);
}

</cfscript>

cfswitch

Tags:

<cfset fruit = "">
<cfswitch expression="#fruit#">
    <cfcase value="Apple">
        <cfoutput>I like apples!</cfoutput>
    </cfcase>
    <cfcase value="Orange">
        <cfoutput>I like oranges!</cfoutput>
    </cfcase>
    <cfcase value="Kiwi">
        <cfoutput>I like kiwi!</cfoutput>
    </cfcase>
    <cfdefaultcase>
        <cfoutput>Fruit, what fruit?</cfoutput>
    </cfdefaultcase>
</cfswitch>

Script:

<cfscript>

fruit = "";
switch(fruit) {
	case "Apple":
		writeOutput("I like apples!");
	break;
	case "Orange":
		writeOutput("I like oranges!");
	break;
	case "Kiwi":
		writeOutput("I like kiwi!");
	break;
	default:
		writeOutput("Fruit, what fruit?");
	break;
}

</cfscript>

cftry / cfcatch / cffinally

Tags:

<cftry>
	<cfset x = y + z>
	<cfcatch type="any">
		<cfoutput>#cfcatch.message#</cfoutput>
	</cfcatch>
	<cffinally>
		<cfoutput>Finally at the end.</cfoutput>
	</cffinally>
</cftry>

Script:

<cfscript>

try {
	x = y + z;
}
catch(any e) {
	writeOutput(e.message);
}
finally {
	writeOutput("Finally at the end");
}

</cfscript>

cfthrow & cfrethrow

Tags:

<!--- <cfthrow> --->
<cfthrow message="Oh no an error!!1" type="RealBadErrorException" detail="Something bad happened in detail">

<!--- <cfrethrow> --->
<cfrethrow>

Script:

<cfscript>

// <cfthrow>
throw(message = "Oh no an error!!1", type = "RealBadErrorException", detail = "Something bad happened in detail");
// throw() can even be as simple as this...
throw "Oh no an error!!1";

// <cfrethrow>
rethrow;

</cfscript>

Iterations

General Purpose Loop

Tags:

<cfloop index="i" from="1" to="10">
	<cfoutput>#i#</cfoutput>
</cfloop>

Script:

<cfscript>

for (i = 1; i <= 10; i++) {
	writeOutput(i);
}

</cfscript>

Array Loop

Tags:

<!--- Define our array --->
<cfset myArray = ["a", "b", "c"]>

<!--- By index --->
<cfloop index="i" from="1" to="#arrayLen(myArray)#">
	<cfoutput>#myArray[i]#</cfoutput>
</cfloop>

<!--- By array --->
<cfloop index="currentIndex" array="#myArray#">
	<cfoutput>#currentIndex#</cfoutput>
</cfloop>

Script:

<cfscript>

// Define our array
myArray = ["a", "b", "c"];

// By index
// Note the use of the newer member function syntax for arrayLen()
for (i = 1; i <= myArray.len(); i++) {
	writeOutput(myArray[i]);
}

// By array
for (currentIndex in myArray) {
	writeOutput(currentIndex);
}

// By arrayEach()
myArray.each(function(element, index) {
	writeOuput(element & " : " & index);
});

</cfscript>

Struct Loop

Tags:

<!--- Define our struct --->
<cfset myStruct = {name: "Tony", state: "Florida"}>

<!--- By struct --->
<cfloop item="currentKey" collection="#myStruct#">
	<cfoutput><li>#currentKey# : #myStruct[currentKey]#</li></cfoutput>
</cfloop>

Script:

<cfscript>

// Define our struct
myStruct = {name: "Tony", state: "Florida"};

// By struct
for (currentKey in myStruct) {
	writeOutput("<li>#currentKey# : #myStruct[currentKey]#</li>");
}

// By structEach()
myStruct.each(function(key, value) {
	writeOutput("<li>#key# : #value#</li>");
});

</cfscript>

List Loop

Tags:

<!--- Define our list --->
<cfset myList = "a, b, c">

<!--- By list --->
<cfloop index="item" list="#myList#">
	<cfoutput>#item#</cfoutput>
</cfloop>

<!--- By array --->
<cfloop index="currentIndex" array="#listToArray(myList, ",")#">
	<cfoutput>#currentIndex#</cfoutput>
</cfloop>

Script:

<cfscript>

// Define our list
myList = "a, b, c";

// By array
for (item in listToArray(myList, ",")) {
	writeOutput(item);
}

// By listEach()
myList.each(function(element, index) {
	writeOuput(element & " : " & index);
}, ",");

</cfscript>

Query Loop

Tags:

<!--- Define our query --->
<cfset platform = ["Adobe ColdFusion", "Railo", "Lucee"]>
<cfset myQuery = queryNew("")>
<cfset queryAddColumn(myQuery, "platform", "CF_SQL_VARCHAR", platform)>

<!--- By row index --->
<cfloop index="i" from="1" to="#myQuery.recordCount#">
	<cfoutput><li>#myQuery["platform"][i]#</li></cfoutput>
</cfloop>

<!--- By group --->
<cfloop query="myQuery" group="platform">
	<cfoutput><li>#platform#</li></cfoutput>
</cfloop>

Script:

<cfscript>

// Define our query
platform = ["Adobe ColdFusion", "Railo", "Lucee"];
myQuery = queryNew("");
queryAddColumn(myQuery, "platform", "CF_SQL_VARCHAR", platform);

// By row index
for (i = 1; i <= myQuery.recordCount; i++) {
    writeOutput("<li>#myQuery["platform"][i]#</li>");
}

// By query
for (row in myQuery) {
    writeOutput("<li>#row.platform#</li>");
}

</cfscript>

Other Flow Control Tags

cfabort & cfexit

Tags:

<!--- <cfabort> --->
<cfabort statusError="My error message">

<!--- <cfexit> --->
<cfexit method="method">

General

cfoutput

Tags:

<cfoutput>Some text and a #variable#</cfoutput>

Script:

<cfscript>

writeOutput("Some text and a #variable#");
// or
writeOutput("Some text and a " & variable);

</cfscript>

cfsavecontent

Tags:

<cfsavecontent variable="myContent">
	<cfoutput>Some content.</cfoutput>
</cfsavecontent>

Script:

<cfscript>

savecontent variable="myContent" {
	writeOutput("Some content.");
}

</cfscript>

cfthread

Tags:

<cfthread action="run" name="myThread">
	<!--- Do single thread stuff --->
</cfthread>

<cfthread action="join" name="myThread,myOtherThread" />

Script:

<cfscript>

thread action="run" name="myThread" {
	// do single thread stuff
}

thread action="join" name="myThread,myOtherThread";

</cfscript>

cflock

Tags:

<cflock timeout="60" scope="session" type="exclusive">
	<cfset session.myVar = "Hello">
</cflock>

Script:

<cfscript>

lock timeout="60" scope="session" type="exclusive" {
	session.myVar = "Hello";
}

</cfscript>

cfinclude

Tags:

<cfinclude template="mypage.cfm">

Script:

<cfscript>

include "mypage.cfm";

</cfscript>

cflocation

Tags:

<cflocation url="mypage.cfm" addToken="false" statusCode="301">

Script:

<cfscript>

location("mypage.cfm", "false", "301");

</cfscript>

Debugging

cfdump

Tags:

<cfdump var="#cgi#" label="CGI Scope">

Script:

<cfscript>

writeDump(var = cgi, label = "CGI Scope");

</cfscript>

cflog

Tags:

<cflog text="Logging some info." type="information" application="no" file="myLogFile">

Script:

<cfscript>

writeLog(text = "Logging some info.", type = "information", application = "no", file = "myLogFile");

</cfscript>

Script:

<cfscript>

// <cfabort>
abort "My error message";

// <cfexit>
exit "method";

</cfscript>

Database

cfquery

Tags:

<cfquery name="myQuery" datasource="myDSN">
	SELECT myCol1, myCol2 FROM myTable
	WHERE myCol1 = <cfqueryparam value="#myId#" cfsqlype="cf_sql_integer">
	ORDER BY myCol1 ASC
</cfquery>

Script:

<cfscript>

// Also see the "Tags Implemented as Components" section for another method of using <cfquery> in script

myQuery = queryExecute(
	"SELECT myCol1, myCol2 FROM myTable
	WHERE myCol1 = :myId
	ORDER BY myCol1 ASC",
	{myId: {value: 5, cfsqltype: "cf_sql_integer"}},
	{datasource = "myDSN"}
);

</cfscript>

cftransaction

Tags:

<cftransaction>
<cftry>
	<!--- code to run --->
	<cftransaction action="commit" />
	<cfcatch type="any">
		<cftransaction action="rollback" />
	</cfcatch>
</cftry>
</cftransaction>

Script:

<cfscript>

transaction {
	try {
		// code to run
		transaction action="commit";
	}
	catch(any e) {
		transaction action="rollback";
	}
}

</cfscript>

File System Operations

cfdirectory

Tags:

<!--- Directory List --->
<cfdirectory action="list" directory="#expandPath("./")#" recurse="false" name="myList">

<!--- Directory Create --->
<cfdirectory action="create" directory="#expandPath("./new_directory")#">

<!--- Directory Delete --->
<cfdirectory action="delete" directory="#expandPath("./my_directory")#">

<!--- Directory Rename --->
<cfdirectory action="rename" directory="#expandPath("./my_directory")#" newdirectory="#expandPath("./new_directory")#">

Script:

<cfscript>

// Directory List
myList = directoryList(expandPath("./"), false, "query");

// Directory Create
directoryCreate(expandPath("./new_directory"));

// Directory Delete
directoryDelete(expandPath("./my_directory"));

// Directory Rename
directoryRename(expandPath("./my_directory"), expandPath("./new_driectory"));

</cfscript>

cffile

Tags:

<!--- File Write --->
<cffile action="write" file="#expandPath("./myFile.txt")#" output="Here's some content for my file.">

<!--- File Append --->
<cffile action="append" file="#expandPath("./myFile.txt")#" attributes="normal" output="Here's some new content.">

<!--- File Read --->
<cffile action="read" file="#expandPath("./myFile.txt")#" variable="myFile">

<!--- File Read Binary --->
<cffile action="readBinary" file="#expandPath("./myImage.jpg")#" variable="myImageBinary">

<!--- File Rename --->
<cffile action="rename" source="#expandPath("./myFile.txt")#" destination="#expandPath("./myNewFileName.txt")#" attributes="normal">

<!--- File Copy --->
<cffile action="copy" source="#expandPath("./myFile.txt")#" destination="#expandPath("./some/other/path")#">

<!--- File Move --->
<cffile action="move" source="#expandPath("./myFile.txt")#" destination="#expandPath("./some/other/path")#">

<!--- File Delete --->
<cffile action="delete" file="#expandPath("./myFile.txt")#">

<!--- File Upload --->
<cffile action="upload" destination="#expandPath("./destination)#" filefield="form.myFile" nameconflict="makeunique">

<!--- File Upload All --->
<cffile action="uploadall" destination="#expandPath("./destination")#" nameconflict="makeunique">

Script:

<cfscript>

// File Write
fileWrite(expandPath("./myFile.txt"), "Here's some content for my file.");

// File Append
// There is no "fileAppend()" so we access the file and use fileWriteLine()
myFile = fileOpen(expandPath("./myFile.txt"), "write");
fileWriteLine(myFile, "Here's some new content.");
fileClose(myFile);

// File Read
myFile = fileRead(expandPath("./myFile.txt"));

// File Read Binary
myImageBinary = fileReadBinary(expandPath("./myImage.jpg"));

// File Rename
// Since there is no "fileRename()", fileMove() works just as well
fileMove(expandPath("./myFile.txt"), expandPath("./myNewFileName.txt"));

// File Copy
fileCopy(expandPath("./myFile.txt"), expandPath("./some/other/path"));

// File Move
fileMove(expandPath("./myFile.txt"), expandPath("./some/other/path"));

// File Delete
fileDelete(expandPath("./myFile.txt"));

// File Upload
fileUpload(expandPath("./destination"), form.myFile, "", "makeunique");

// File Upload All
fileUploadAll(expandPath("./destination"), "", "makeunique");

</cfscript>

Image Manipulation

cfimage

There is quite the lot of image-related functions for <cfimage> that are already decently documented by Adobe. For the sake of avoiding a lengthy bit of repeating, you can check out those functions here - ColdFusion Wiki: Image Functions

Spreadsheet Integration

cfspreadsheet

Much like <cfimage>, <cfspreadsheet> has a fair collection of functions for working with spreadsheets which you can find here - ColdFusion Wiki: Microsoft Office Integration Functions

Tags Implemented as Components

Note: If you are using ColdFusion 11+, (highly) consider using the script versions over the CFC implementations for example: cfhttp(result="result", method="post", url="http://mydomain.com"); over Http.cfc and queryExecute() over Query.cfc.

cfquery / query.cfc

Tags:

<cfquery name="myQuery" datasource="myDSN">
	SELECT myCol1, myCol2 FROM myTable
	WHERE myCol1=<cfqueryparam value="#myId#" cfsqlype="cf_sql_integer">
	ORDER BY myCol1 ASC
</cfquery>

Script:

<cfscript>

myQuery = new Query().setDatasource("myDSN");
myQuery.setSQL("
	SELECT myCol1, myCol2 FROM myTable
	WHERE myCol1=:myId
	ORDER BY myCol1 ASC
");
myQuery.addParam(name: "myCol1", value: "#myId#", cfsqltype: "cf_sql_integer");
myQuery = myQuery.execute().getResult();

</cfscript>

cfhttp / http.cfc

Tags:

<cfhttp result="result" method="GET" charset="utf-8" url="https://www.google.com/">
	<cfhttpparam name="q" type="formfield" value="cfml">
</cfhttp>

<cfdump var="#result#">

Script:

<cfscript>

httpService = new http(method = "GET", charset = "utf-8", url = "https://www.google.com/");
httpService.addParam(name = "q", type = "formfield", value = "cfml");
result = httpService.send().getPrefix();

writeDump(result);

</cfscript>

cfmail / mail.cfc

Tags:

<cfmail to="[email protected]" from="[email protected]" subject="CFMail Example">
	Your Email Message!!1
</cfmail>

Script:

<cfscript>

// It's a good idea to build your message inside a save content block first
savecontent variable="mailBody" {
	writeOutput("Your Email Message!!1");
};
// Create and populate the mail object
mailService = new mail(
	to = "[email protected]",
	from = "[email protected]",
	subject = "CFMail Example",
	body = mailBody
);
// Send
mailService.send();

</cfscript>

cffeed / feed.cfc

Tags:

<!--- Read --->
<cffeed
	name="feed"
	action="read"
	source="http://feeds.feedburner.com/ColdfusionbloggersorgFeed?format=xml"
	query="feedQuery"
	properties="feedMetadata"
>
<cfdump var="#feed#">

Script:

<cfscript>

// Read
feedService = new feed();
feed = feedService.read(
	source = "http://feeds.feedburner.com/ColdfusionbloggersorgFeed?format=xml",
	query = "feedQuery",
	properties = "feedMetadata"
);
writeDump(feed);

</cfscript>

cfftp / ftp.cfc

Tags:

<!--- Open connection --->
<cfftp action="open" connection="myConn" username="myUName" password="myPW" server="ftp.server.com" stopOnError="true">
<!--- Get list of dir --->
<cfftp action="listdir" connection="myConn" name="filesList" directory="/" stopOnError="true">
<!--- Close connection --->
<cfftp action="close" connection="myConn" stopOnError="true">

Script:

<cfscript>

// Create FTP service and set attributes for connection
ftpService = new ftp(); 
ftpService.setConnection("myConn");
ftpService.setUsername("myUName");
ftpService.setPassword("myPW");
ftpService.setServer("ftp.server.com");
ftpService.setStopOnError(true);
// Open connection
ftpService.open();
// Get list of dir
fileList = ftpService.listdir(directory = "/", stopOnError = true).getResult();
// Close connection
ftpService.close();

</cfscript>

Interfaces, Components & Functions

cfinterface

Tags:

<cfinterface displayname="My Interface">
	<cfunction name="myFunction" returntype="numeric">
		<cfargument required="true" type="string" name="myArgument">
	</cffunction>
</cfinterface>

Script:

interface displayname="My Interface" {
	numeric function myFunction(required string myArgument);
}

cfcomponent

Tags:

<cfcomponent displayname="myComponent" output="false">
	<!--- functions and other values here --->
</cfcomponent>

Script:

component displayname="myComponent" output="false" {
	// functions and other values here
}

cffunction

Tags:

<cffunction access="public" returntype="boolean" name="myFunction">
	<cfargument required="true" type="any" name="myArgument">
	
	<!--- Some function bits --->
	
	<cfreturn true>
</cffunction>

Script:

public boolean function myFunction(required any myArgument) {
	// Some function bits
	
	return true;
}

A More Complete Component Example

Tags:

<cfcomponent displayname="Utils" output="false">
	<cfproperty name="version" type="string" default="0.0.1">

	<cffunction access="public" returntype="string" name="doReverse" hint="I reverse the supplied string">
		<cfargument required="true" type="string" name="stringToReverse">
		
		<cfreturn reverse(arguments.stringToReverse)>
	</cffunction>
	
	<cffunction access="public" returntype="array" name="reverseArrayOrder">
		<cfargument type="array" name="arrayToReverse" default="#["Adobe ColdFusion", "Lucee", "Railo"]#" hint="I reverse an array's order">
		
		<cfset var result = []>
		<cfset var i = 0>
		<cfloop index="i" from="#arrayLen(arguments.arrayToReverse)#" to="1" step="-1">
			<cfset arrayAppend(result, arguments.arrayToReverse[i])>
		</cfloop>
		
		<cfreturn result>
	</cffunction>
</cfcomponent>

Script:

component displayname="Utils" output="false" {
	property name="version" type="string" default="0.0.1";

	public string function doReverse(required string stringToReverse)
		hint="I reverse the supplied string"
	{
		return reverse(arguments.stringToReverse);
	}
	
	public array function reverseArrayOrder(array arrayToReverse = ["Adobe ColdFusion", "Lucee", "Railo"])
		hint="I reverse an array's order"
	{
		var result = [];
		for (var i = arrayLen(arguments.arrayToReverse); i >= 1; i--) {
			result.append(arguments.arrayToReverse[i]);
		}
		
		return result;
	}
}

Annotations

Information about a script-based component, property or function - it's attributes & arguments can be defined via the annotation syntax.

Script:

// Consider this code...

public numeric function adder(required numeric x, required numeric y)
	hint="I add things"
{
	return arguments.x + arguments.y;
}

// Now with annotations

/**
* @returnType numeric
* @hint I add things
*/
public function adder(required numeric x, required numeric y) {
	return arguments.x + arguments.y;
}

Function Expressions

Consider the tag-based version of the function below. In CFScript, we can instead define a variable with an anonymous function that does the same task.

Tags:

<cffunction access="public" returntype="numeric" name="adder">
	<cfargument required="true" type="numeric" name="x">
	<cfargument required="true" type="numeric" name="y">
	
	<cfreturn arguments.x + arguments.y>
</cffunction>

<cfoutput>#adder(2, 2)#</cfoutput>

Script:

<cfscript>

adder = function(x, y) {
	return x + y;
};

writeOutput(adder(2, 2));

// You can be more specific with type, scope, required etc.
adder = function(required numeric x, required numeric y) {
	return arguments.x + arguments.y;
};

writeOutput(adder(2, "a")); // this errors

</cfscript>

Real World Conversions By Example

The below examples are pieces of code found from online/offline sources. Anything with a name on it will be given credit and/or linked accordingly. If you see your code as an example and you don't want it here, LET ME KNOW! Cheers. - @cfchef

Example 1

Here's a function, written in tags, picked out from CFLib.org - utcOffsetToMinutes() by Mosh Teitelbaum

Tags:

<!---
Converts UTC Offset to minutes.

@param offset The formatted UTC offset to be converted to minutes. (Required)
@return Returns a number.
@author Mosh Teitelbaum ([email protected])
@version 1, 1/8/2015
--->

<cffunction name="utcOffsetToMinutes" returntype="numeric" output="no" description="Converts UTC Offset to minutes.">
	<cfargument name="offset" type="string" required="yes" hint="The formatted UTC offset to be converted to minutes.">

	<!--- Initialize local variables --->
	<cfset var h = 0>	<!--- hours --->
	<cfset var m = 0>	<!--- minutes --->
	<cfset var s = 1>	<!--- sign --->
	<cfset var str = ReReplaceNoCase(arguments.offset, "[^-:0-9]", "", "ALL")>	<!--- offset with non-important characters removed --->

	<!--- If first character is "-", adjust sign --->
	<cfif Compare(Left(str, 1), "-") EQ 0>
		<cfset s = -1>
		<cfset str = Right(str, Len(str) - 1)>
	</cfif>

	<!--- Determine number of hours and minutes --->
	<cfif ListLen(str, ":") EQ 2>
		<cfset h = ListFirst(str, ":")>
		<cfset m = ListLast(str, ":")>
	<cfelseif Len(str) EQ 2>
		<cfset h = str>
	<cfelseif Len(str) EQ 3>
		<cfset h = Left(str, 1)>
		<cfset m = Right(str, 2)>
	<cfelseif Len(str) EQ 4>
		<cfset h = Left(str, 2)>
		<cfset m = Right(str, 2)>
	</cfif>

	<!--- Return number of minutes --->
	<cfreturn s * ((h * 60) + m)>
</cffunction>

Script:

<cfscript>

/**
* @hint Converts UTC Offset to minutes.
* @param offset The formatted UTC offset to be converted to minutes. (Required)
* @return Returns a number.
* @author Mosh Teitelbaum ([email protected])
* @version 1, 1/8/2015
*/
public numeric function utcOffsetToMinutes(required string offset) {
	// Initialize local variables
	var h = 0; // hours
	var m = 0; // minutes
	var s = 1; // sign
	var str = reReplaceNoCase(arguments.offset, "[^-:0-9]", "", "ALL"); // offset with non-important characters removed

	// If first character is "-", adjust sign
	if (compare(left(str, 1), "-") == 0) {
		s = -1;
		str = right(str, len(str) - 1);
	}

	// Determine number of hours and minutes
	if (listLen(str, ":") == 2) {
		h = listFirst(str, ":");
		m = listLast(str, ":");
	} else if (len(str) == 2) {
		h = str;
	} else if (len(str) == 3) {
		h = left(str, 1);
		m = right(str, 2);
	} else if (len(str) == 4) {
		h = left(str, 2);
		m = right(str, 2);
	}

	// Return number of minutes
	return s * ((h * 60) + m);
}

</cfscript>

Example 2

Here's another one from CFLib.org - addRemoveDebuggingIPAddress() by Qasim Rasheed

Tags:

<!---
 This function will either add or remove an IP address to the list of debugging ip addresses if you do not have an administrator access.
 
 @param ipAddress 	 IP Address (Required)
 @param action 	 Add or Remove. Defaults to Add. (Optional)
 @return Returns a list of IP addresses. 
 @author Qasim Rasheed ([email protected]) 
 @version 1, February 17, 2004 
--->
<cffunction name="addRemoveDebuggingIPAddress" output="false" returnType="string">
	<cfargument name="IPaddress" type="string" required="Yes" />
	<cfargument name="action" type="string" default="Add" />
	<cfscript>
		var factory = CreateObject("Java","coldfusion.server.ServiceFactory");
		var debuggingService = "";
	</cfscript>
	<cflock name="factory_debuggingservice" type="exclusive" timeout="5">
		<cfset debuggingService = factory.getDebuggingService()>
		<cfswitch expression="#arguments.action#">
			<cfcase value="Add">
				<cfif not listcontainsnocase(debuggingService.iplist.ipList,arguments.IPaddress)>
					<cfset debuggingService.iplist.ipList = ListAppend(debuggingService.iplist.ipList,arguments.IPaddress)>
				</cfif>
			</cfcase>
			<cfcase value="Remove">
				<cfif listcontainsnocase(debuggingService.iplist.ipList,arguments.IPaddress)>
					<cfset debuggingService.iplist.ipList = ListDeleteAt(debuggingService.iplist.ipList,ListFindNoCase(debuggingService.iplist.ipList,arguments.IPaddress))>
				</cfif>
			</cfcase>
		</cfswitch>
		<cfreturn debuggingService.iplist.ipList />
	</cflock>
</cffunction>

Script:

<cfscript>

/**
* @hint This function will either add or remove an IP address to the list of debugging ip addresses if you do not have an administrator access.
* @param ipAddress IP Address (Required)
* @param action Add or Remove. Defaults to Add. (Optional)
* @return Returns a list of IP addresses. 
* @author Qasim Rasheed ([email protected]) 
* @version 1, February 17, 2004 
*/
public string function addRemoveDebuggingIPAddress(required string IPaddress, string action = "Add")
	output="false"
{
	var factory = createObject("java","coldfusion.server.ServiceFactory");
	var debuggingService = "";
	lock name="factory_debuggingservice" type="exclusive" timeout="5" {
		debuggingService = factory.getDebuggingService();
		switch(arguments.action) {
			case "Add":
				if (!listContainsNoCase(debuggingService.iplist.ipList,arguments.IPaddress)) {
					debuggingService.iplist.ipList = listAppend(debuggingService.iplist.ipList, arguments.IPaddress);
				}
			break;
			case "Remove":
				if (listContainsNoCase(debuggingService.iplist.ipList, arguments.IPaddress)) {
					debuggingService.iplist.ipList = listDeleteAt(debuggingService.iplist.ipList, listFindNoCase(debuggingService.iplist.ipList, arguments.IPaddress));
				}
			break;
		}
		
		return debuggingService.iplist.ipList;
	}
}

</cfscript>

Example 3

Yet another example from CFLib.org - collectFiles() by Steven Ross

Tags:

<!---
 Scans a directory (or path) for files of a specified extension and then copies them to the path you specify.
 v2 by Raymond Camden. I just cleaned up the var statements.
 
 @param extensions 	 List of extensions to copy. (Required)
 @param destinationPath 	 Destination directory. (Required)
 @param sourcePath 	 Source directory. (Required)
 @return Returns nothing. 
 @author Steven Ross ([email protected]) 
 @version 2, April 7, 2006 
--->
<cffunction name="collectFiles" access="public" hint="recurses through a directory and collects the file types you want then outputs to another directory" returnType="void">
	<cfargument name="extensions" required="true" type="string" hint="The extensions you want to gather up csv (list) format ex:(asp,cfm,jsp) ">
	<cfargument name="destinationPath" required="true" type="string" hint="absolute path to storage directory">
	<cfargument name="sourcePath" required="true" type="string" hint="absolute path to source directory">
	<cfset var root = arguments.sourcePath/>
	<cfset var i = "">
	<cfset var absPath = "">
	<cfset var relativePath = "">
	<cfset var writeTo = "">
	<cfset var pathAndFile = "">
	
	<cfif not directoryExists(arguments.sourcePath)>
		<cfthrow message="Source Directory (#arguments.sourcePath#) not found" detail="You didn't pass in a valid source directory, check the path and try again.">
	</cfif>
	
	<cfloop list="#arguments.extensions#" index="i">
		<cfdirectory name="getFiles" directory="#root#" recurse="true" filter="*.#i#">
		<cfloop query="getFiles">
			<cfset absPath = getFiles.directory & "/" />
			<cfset relativePath = Replace(absPath, root, "", "all") />
			<cfset writeTo = ARGUMENTS.destinationPath & "/" & relativePath>
			<cfset pathAndFile = getFiles.directory & "/" & getFiles.name />
			<cfif not directoryExists(writeTo)>
				<cfdirectory action="create" directory="#writeTo#">
				<cffile action="copy" source="#pathAndFile#" destination="#writeTo#">
			<cfelse>
				<cffile action="copy" source="#pathAndFile#" destination="#writeTo#">
			</cfif>
		</cfloop>
	</cfloop>
</cffunction>

Script:

<cfscript>

/**
* @hint Scans a directory (or path) for files of a specified extension and then copies them to the path you specify.
* @param extensions List of extensions to copy. (Required)
* @param destinationPath Destination directory. (Required)
* @param sourcePath Source directory. (Required)
* @return Returns nothing.
* @author Steven Ross ([email protected]) - v2 by Raymond Camden. I just cleaned up the var statements.
* @version 2, April 7, 2006
*/
public void function collectFiles(required string extensions, required string destinationPath, required string sourcePath) {
	var root = arguments.sourcePath;
	if (!directoryExists(arguments.sourcePath)) {
		throw(
			message = "Source Directory (#arguments.sourcePath#) not found",
			detail = "You didn't pass in a valid source directory, check the path and try again."
		);
	}
	for (var ext in listToArray(arguments.extensions)) {
		var list = directoryList(path = root, recurse = true, listinfo = "query", filter = "*.#ext#");
		for (var dir in list) {
			var absPath = dir.directory & "/";
			var relativePath = replace(absPath, root, "", "all");
			var writeTo = arguments.destinationPath & "/" & relativePath;
			var pathAndFile = dir.directory & "/" & dir.name;
			if (!directoryExists(writeTo)) {
				directoryCreate(writeTo);
				fileCopy(pathAndFile, writeTo);
			} else {
				fileCopy(pathAndFile, writeTo);
			}
		}
	}
}

</cfscript>

Tags Have Their Place

Keeping Tags in the View

The modern CFML world has evolved with various flavors and mixes of Model View Controller (MVC) & Object Oriented Programming (OOP) practices. While procedural coding might have a place, larger applications often call for a more organized, structured collection of functions and how and where they display their results. As opposed to being all in one file, database calls should reside in their own file (Model), structuring/manipulation of that data can be built in a seperate file based on user supplied variables (Controller) and then that structured data is formatted with HTML for display (View).

From a general standpoint, the view layer should be the home for basic tag functions to help iterate and organize received result data; to be married with your HTML and displayed to the user. Simple tags such as <cfset>, <cfparam>, <cfoutput>, <cfif> & <cfloop>. With the involvement of HTML in the view, tags make sense as they work with the flow of the layout; leaving your Model and Controllers to be uncluttered and have a clean flow of their own in CFScript. It might be a matter of opinion but you will eventually grow to find this separation quite refreshing visually and mentally when scanning through large blocks of logic vs display code.

Consider this general mock breakdown of code presenting blog posts to the user. This would normally be handled more effectively & efficiently in a MVC framework like FW/1 or Coldbox.

Model: BlogService.cfc

/**
* @hint I contain blog related utilities
*/
component displayname="Blog Service"
	output="false"
{
	/**
	* @hint I intiate the service object by setting the datasource
	*/
	public BlogService function init(required string dsn) {
		variables.dsn = arguments.dsn;
		
		return this;
	}
	
	/**
	* @hint I return a query of blog posts
	*/
	public query function getPosts() {
		var qry = new Query();
		qry.setDatasource(variables.dsn);
		qry.setSQL("
			SELECT postId, postTitle, postBody, author, publishDate
			FROM posts
			ORDER BY postId DESC
		");
		
		return qry.execute().getResult();
	}
}

Controller: BlogController.cfc

/**
* @hint I receive user request and translate them into response
*/
component displayname="Blog Controller"
	output="false"
{
	/**
	* @hint I initiate the BlogController and call in all required objects
	*/
	public BlogController funtion init() {
		var DataService = createObject("component", "model.services.DataService").init();
		variables.BlogService = createObject("component", "model.services.BlogService").init(DataService.getDSN());
		
		return this;
	}
	
	/**
	* @hint I return a formatted collection of blog posts for display
	*/
	public struct function posts() {
		// Define out request context struct for holding data to pass on to the view
		var rc = {};
		var postQry = variables.BlogService.getPosts();
		// Let's make a friendlier collection as a array of structs of the data
		rc.posts = [];
		if (postQuery.recordCount) {
			for (var post in postQry) {
				arrayAppend(rc.posts, {
					title: post.postTitle,
					body: post.postBody,
					author: post.author,
					published: post.publishDate
				});
			}
		}
		
		return rc;
	}
}

View: posts.cfm

<cfparam name="rc.posts" default="#[]#">

<cfset BlogController = createObject("component", "blog.controllers.BlogController").init()>
<cfset rc.posts = BlogController.posts()>

<h1>Welcome to my Blog!</h1>
<cfif arrayLen(rc.posts)>
	<div class="post-content">
	<cfoutput>
	<cfloop index="post" array="#rc.posts#">
		<h2>#post.title#</h2>
		<div class="post-meta">Published on: #post.published# By: #post.author#</div>
		<div class="post-body">#post.body#</div>
	</cfloop>
	</cfoutput>
	</div>
<cfelse>
	<h3>No posts to display!</h3>
</cfif>

Building Tag Code on the Script Side

While we generally want to keep our presentation layer separate from our business logic, as briefly portrayed in Keeping Tags in the View, there are some scenarios where we need to do string-building in a function, or similar, to later pass on for an alternate method of viewing in HTML, XML, email etc.

Consider these examples. . .

Email - Tags

<!--- Dummy blog post --->
<cfset post = {
	title: "My New Blog Post!",
	slug: "my-new-blog-post",
	body: "Some body content for the post.",
	publishDate: "{ts '2015-07-06 18:17:01'}"
}>

<!--- Build our email --->
<cfsavecontent variable="mailBody">
	<cfoutput>
		<h4>A New Article Has Been Posted @ myblog.com!</h4>
		<h6>#post.title#</h6>
		<p><b>Posted on:</b> #dateFormat(post.publishDate, "YYYY/MM/DD")#</p>
		<p>
			<cfif len(post.body) GT 500>
				#left(post.body, 500)#. . .
			<cfelse>
				#post.body#
			</cfif>
		</p>
		<a href="http://myblog.com/blog/#post.slug#"><b>Read more...</b></a>
	</cfoutput>
</cfsavecontent>

<!--- Send email to subscribers --->
<cfloop item="subscriber" collection="#SubscriberService.getSubscribers()#">
	<cfmail
		type="html"
		to="#subscriber.getEmail()#"
		from="[email protected]"
		subject="New Blog Post from MyBlog.com"
		body="#mailBody#"
	>
</cfloop>

Email - Script

<cfscript>

// Dummy blog post
post = {
	title: "My New Blog Post!",
	slug: "my-new-blog-post",
	body: "Some body content for the post.",
	publishDate: "{ts '2015-07-06 18:17:01'}"
};

// Build our email
savecontent variable="mailBody" {
	writeOutput('
		<h4>A New Article Has Been Posted @ myblog.com!</h4>
		<h6>#post.title#</h6>
		<p><b>Posted on:</b> #dateFormat(post.publishDate, "YYYY/MM/DD")#</p>
		<p>
			if (len(post.body) > 500) {
				#left(post.body, 500)#. . .
			} else {
				#post.body#
			}
		</p>
		<a href="http://myblog.com/blog/#post.slug#"><b>Read more...</b></a>
	');
};

// Send email to subscribers
for (subscriber in SubscriberService.getSubscribers()) {
	mailService = new mail(
		type = "html",
		to = subscriber.getEmail(),
		from = "[email protected]",
		subject = "New Blog Post from MyBlog.com!",
		body = encodeForHTML(mailBody)
	);
	mailService.send();
}

</cfscript>

XML Sitemap - Tags

<!--- Dummy links --->
<cfset links = [
	"http://myblog.com",
	"http://myblog.com/about",
	"http://myblog.com/blog",
	"http://myblog.com/blog/my-blog-post"
]>

<cftry>
	<!--- Build XML --->
	<cfsavecontent variable="xmlData">
	<cfoutput>
		<?xml version="1.0" encoding="UTF-8"?>
		<urlset
			xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
			xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
			xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
			http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
		<cfloop index="link" array="#links#">
			<url>
			  <loc>#link#</loc>
			  <changefreq>weekly</changefreq>
			  <priority>0.8</priority>
			</url>
		</cfloop>
		</urlset>
	</cfoutput>
	</cfsavecontent>
	
	<!--- Write to file --->
	<cffile action="write" file="#expandPath("./Sitemap.xml")#" output="#xmlData#">

	<cfcatch type="any">
		<cfoutput>#cfcatch.message#</cfoutput>
	</cfcatch>
</cftry>

XML Sitemap - Script

<cfscript>

// Dummy links
links = [
	"http://myblog.com",
	"http://myblog.com/about",
	"http://myblog.com/blog",
	"http://myblog.com/blog/my-blog-post"
];

try {
	// Build XML
	savecontent variable="xmlData" {
		writeOutput('
			<?xml version="1.0" encoding="UTF-8"?>
			<urlset
				xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
				xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
				xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
				http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
		');
		for (link in links) {
			writeOutput('
				<url>
				  <loc>#link#</loc>
				  <changefreq>weekly</changefreq>
				  <priority>0.8</priority>
				</url>
			');
		}
		writeOutput('
			</urlset>
		');
	}
	// Write to file
	fileWrite(expandPath("./Sitemap.xml"), xmlData);
}
catch(any e) {
	writeOutput(e.message);
}

</cfscript>

LICENSE

Creative Commons License
CFML Tag to Script Conversions by Tony Junkes is licensed under a Creative Commons Attribution 4.0 International License.