Skip to main content
Ben Nadel at TechCrunch Disrupt (New York, NY) with: Mark C. Webster
Ben Nadel at TechCrunch Disrupt (New York, NY) with: Mark C. Webster ( @markcwebster )

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

By on
Tags:

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.

Want to use code from this post? Check out the license.

Reader Comments

55 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

15,674 Comments

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

15,674 Comments

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.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel