Sunday, 15 September 2013

CFML: When to use pound-signs

G'day:
I'm writing this so I can link someone to it. It's just what it says on the wrapper, so won't be of any interest to anyone who already knows this.

I still fairly frequently see people using pound-signs (#) in their CFML code when they don't need to. This also includes code in the CFML docs, which is concerning! Pound-signs seem to be the apostrophe's of the CFML world: people know they exist, and are necessary, but don't necessarily know the (fairly simple) rules, so "if in doubt, pepper them about".

However pound-signs have even more simple rules than apostrophe's. Here are a list of rules:
  1. Pound-signs are necessary when there is scope for a CFML expression to be interpreted as a literal string rather than an expression;
  2. err...
  3. ... that's it.
When can this happen?

In tag-based code, outside of a tag

For example:
<cfset aVariable = "A variable">
<cfoutput>
    aVariables<br>
    #aVariable#<br>
</cfoutput>

One needs the pound-signs around the variable reference so that the CFML parser knows it's a variable. This code outputs:

aVariable
A variable


Which demonstrates the difference.

Within an actual tag

For example:

<cffunction name="f">
    <cfargument name="x" default=getDefault()>
    <cfargument name="y" default=#getDefault()#>
    
    <cfoutput>#serializeJson(arguments)#</cfoutput>
</cffunction>

<cffunction name="getDefault">
    <cfreturn "default value">
</cffunction>

<cfset f()>

This outputs:

{"Y":"default value","X":"getDefault()"}


Note the un-pound-signed default value is treated like a string. This is the one instance that - to me - is counter-intuitive behaviour.

When the expression is within a string


And for the same reason as the previous situation. Consider this code:

<cfset aVariable = "A variable">
<cfset message = "aVariable<br>#aVariable#">

<cfoutput>#message#</cfoutput>

Obviously (?) one needs to use the pound-signs there to identify the text as a variable, not as a string literal.

This also applies within tag attributes:

<cfset list = "tahi,rua,toru,wha">
<cfloop list="list" index="element">
    <cfoutput>#element#</cfoutput><br>
</cfloop>
<hr>
<cfloop list="#list#" index="element">
    <cfoutput>#element#</cfoutput><br>
</cfloop>
<hr>

This outputs:

list

tahi
rua
toru
wha


In the first loop, the list is literally the string "list".

Note that this also applies if the attribute value is not actually quoted:

<cfset list = "tahi,rua,toru,wha">
<cfloop list=list index=element>
    <cfoutput>#element#</cfoutput><br>
</cfloop>
<hr>
<cfloop list=#list# index=element>
    <cfoutput>#element#</cfoutput><br>
</cfloop>
<hr>

(same output as before).

CFML looks godawful if you don't quote your attributes, so just don't do that.

That's it. That's when you need to use pound-signs. So that's like: hardly ever.

When does one definitely not need pound-signs?


Well, other than "all occasions other than those I mentioned just before", here a are a coupla common cock-ups:

<cfset firstName = "Zachary">
<cfset lastName = "Cameron Lynch">
<cfset fullName = #firstName# & " " & #lastName#>
<cfoutput>#fullName#</cfoutput> 

NO. Do not do that. It's just this:

<cfset fullName = firstName & " " & lastName>

Also one does not need to do this:

<cfif #someValue# EQ #someOtherValue#>

It's just this:
<cfif someValue EQ someOtherValue>

Why? Because we're already in the middle of a CFML statement, so the CFML parser is gonna assume that everything is CFML unless it's not (like it's a literal string).

Either/Or

Update:
Dan Skaggs put me onto this from his comment below.

There's at least one weirdo situation in which it doesn't matter if one uses pound-signs or not. Up until - and including - CF9, when one was looping over a query one, passed the NAME of the query, not the actual query, into the loop:

<cfset rainbow = queryNew("")>
<cfset queryAddColumn(rainbow, "id", "integer", [1,2,3,4,5,6,7])>
<cfset queryAddColumn(rainbow, "maori", "varchar", ["whero","karaka","kowhai","kakariki","kikorangi","tawatawa","mawhero"])>
<cfset queryAddColumn(rainbow, "english", "varchar", ["red","orange","yellow","green","blue","indigo","violet"])>

<cfloop query="rainbow">
    <cfoutput>#id#: #maori# (#english#)<br></cfoutput>
</cfloop>

Note I'm passing the literal string "rainbow" there, not the query itself (which would be "#rainbow#"). Why CF used to behave like this is anyone's guess. One can pass a dynamic expression to a query loop, but the expression needed to evaluate to the name of the query, eg:

<cfset queryName="rainbow">
<cfloop query="#queryName#">
    <cfoutput>#id#: #maori# (#english#)<br></cfoutput>
</cfloop>

However as of ColdFusion 10, one can either pass the name of the query, or the query itself, eg:

<cfloop query="#rainbow#">
    <cfoutput>#id#: #maori# (#english#)<br></cfoutput>
</cfloop>

Cheers for the heads-up there Dan.

What else?

Don't call them octothorps. The person you're talking to will either:

  • not know what you're on about, which is a fail; Or:
  • they will know what you're talking about, and just think you're a dick. Well if that person is me, anyhow ;-)

--
Adam

NB: I spelt it "apostrophe's" on purpose, before anyone decide's to point that out.