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

Adding Differ() And DifferNoCase() Built-In Function Extensions In Lucee CFML 5.3.7.47

By Ben Nadel on
Tags: ColdFusion

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 ) - Returns true if the values are different using a case sensitive comparison.

  • differNoCase( a, b ) - Returns true 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 ) &rarr;", differ( "a", "A" ) );
	echoMany( "differ( A, A ) &rarr;", differ( "A", "A" ) );
	echoMany( "differ( A, Z ) &rarr;", differ( "A", "Z" ) );
	echoMany();
	echoMany( "differNoCase( a, A ) &rarr;", differNoCase( "a", "A" ) );
	echoMany( "differNoCase( A, A ) &rarr;", differNoCase( "A", "A" ) );
	echoMany( "differNoCase( A, Z ) &rarr;", 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:

differ() and differNoCase() showing strings comparison results in Lucee CFML

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.



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

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.