Adding Differ() And DifferNoCase() Built-In Function Extensions In Lucee CFML 5.3.7.47
Yesterday, I needed to check to see if two Strings were different based solely on character-casing. To do this, I used ColdFusion's native compare()
function which performs a case-sensitive comparison between two values. However, the compare()
function is "funny" in that it returns 0
- a Falsy value - if the two strings are the same. Meaning, they are the same if the expression !compare()
returns true
. My brain is not good at reading "not expressions" - too many negatives for my mental call-stack. As such, it made me wish there was an inverted case-sensitive comparison operation in ColdFusion. In Lucee CFML, we can actually install user-defined function (UDF) extensions right into the runtime. I thought it might be fun to revisit that idea for this scenario in Lucee CFML 5.3.7.47.
For yesterday's task, I ended up with code that looked like this:
var isChanged = !! compare( valueA, valueB );
Since the compare()
returns a 0
when the values are the same, I had to use the "double not" operator (!!
) in order to coerce the non-zero numeric value into a Boolean when translating this into a "difference". Ideally, I'd like to have a function that does this for me. Something like:
var isChanged = differ( valueA, valueB );
I don't love the name, differ
; but, I couldn't think of anything that I liked more. So, I'm going to build two functions to use as extensions:
differ( a, b )
- Returnstrue
if the values are different using a case sensitive comparison.differNoCase( a, b )
- Returnstrue
if the values are different using a case insensitive comparison.
Under the hood, these are just going to use the compare()
and compareNoCase()
built-in functions, respectively.
When building Lucee CFML extensions, each custom function must be in it's own file. If you attempt to put more than one function in the same file, nothing will error - the second Function simply won't register as an extension. As such, I created two files:
<cfscript> | |
/** | |
* I determine if the two values are different use a CASE SENSITIVE operation. | |
* | |
* @valueA I am the first value to compare. | |
* @valueB I am the second value to compare. | |
*/ | |
public boolean function differ( | |
required string valueA, | |
required string valueB | |
) { | |
// The compare() method performs a CASE SENSITIVE operation and returns 0 if the | |
// values are the SAME; and, a non-zero result if the two values are different. | |
var isSameValue = ( compare( valueA, valueB ) == 0 ); | |
return( ! isSameValue ); | |
} | |
</cfscript> |
And, for a case insensitive comparison:
<cfscript> | |
/** | |
* I determine if the two values are different use a CASE INSENSITIVE operation. | |
* | |
* @valueA I am the first value to compare. | |
* @valueB I am the second value to compare. | |
*/ | |
public boolean function differNoCase( | |
required string valueA, | |
required string valueB | |
) { | |
// The compareNoCase() method performs a CASE INSENSITIVE operation and returns 0 | |
// if the values are the SAME; and, a non-zero result if the two values are different. | |
var isSameValue = ( compareNoCase( valueA, valueB ) == 0 ); | |
return( ! isSameValue ); | |
} | |
</cfscript> |
As you can see, there is nothing very special happening here - all I'm doing is taking the native compare()
and compareNoCase()
functions and I'm proxying them with a NOT operator.
My only goal here to make my final code like 2% more readable by using a function name that lends a little bit better to what it's actually doing.
Once I had these files, I had to compile them - along with a MANIFEST.md
file - down into a .lex
archive. I can do this with a single compress()
call:
compress( "zip", "./ext/", "./differ-extension.lex", false );
Then, I uploaded the differ-extension.lex
file into the Lucee CFML Server Admin and restarted the server. At that point, my two User-Defined Functions (UDFs) are globally available in my Lucee CFML runtime! Let's try it out:
<cfscript> | |
// Testing prior to deploying the .lex file. | |
// -- | |
// include "./ext/functions/differ.cfm"; | |
// include "./ext/functions/differNoCase.cfm"; | |
echoMany( "differ( a, A ) →", differ( "a", "A" ) ); | |
echoMany( "differ( A, A ) →", differ( "A", "A" ) ); | |
echoMany( "differ( A, Z ) →", differ( "A", "Z" ) ); | |
echoMany(); | |
echoMany( "differNoCase( a, A ) →", differNoCase( "a", "A" ) ); | |
echoMany( "differNoCase( A, A ) →", differNoCase( "A", "A" ) ); | |
echoMany( "differNoCase( A, Z ) →", differNoCase( "A", "Z" ) ); | |
// ------------------------------------------------------------------------------- // | |
// ------------------------------------------------------------------------------- // | |
/** | |
* I collapse the variadic arguments down into a space-delimited list and echo them to | |
* the output. A <BR> tag is added automatically. | |
* | |
* @1...N Simple values to output. | |
*/ | |
public void function echoMany() { | |
echo( arrayToList( arguments, " " ) & "<br />" ); | |
} | |
</cfscript> |
And, when we run this ColdFusion code, we get the following output:

Works like a charm!
I'm still a little bit on the fence about how I actually feel about installing custom functions as runtime extensions. On the one hand, it's awesome. But, on the other hand, I feel like it could easily confuse developers who are seeing what looks like a native function that doesn't appear to be documented anywhere on the Lucee CFML site. Still, a fun experiment for a Friday morning.
Want to use code from this post? Check out the license.
Reader Comments
I agree about your concerns about slightly invisible BIFs
The extension detail page in the admin should show which tags or functions the extension implements. A summary page of what functions are available via installed extensions would also be good.
Could be a nice PR for Lucee
@Zachary,
One thing I thought of was possibly prefixing custom functions with a key to denote ownership. I was inspired by what Angular suggests with their directives - including an element prefix - such as
inv-
to signify "InVision" - for custom elements. The same could be done with custom global UDFs. So, I might rename the functions to:invDiffer()
invDifferNoCase()
Then, a newer developer looking at this could maybe get a sense that the
inv
prefix would signify that something custom is taking place; and, would be able to search - or ask another developer for help - more effectively.For something non-enterprisey related, maybe a simple
udf
prefix would be better. So, again to make it concrete, maybe I could rename them to:udfDiffer()
udfDifferNoCase()
... just thinking out-loud here.