Saturday, 12 April 2014

Bugs in iterator functions in both Railo and ColdFusion

G'day:
I decided to "do my bit" for the cfbackport project, and am looking at implementing the new collection iteration functions for older versions of ColdFusion. I'm aiming for CMFX6.0 onwards, but am having to guess at some of the language restrictions as I'm on my back-up laptop and only have CF10 & 11 to test with.

Anyway, I decided to start with the list iteration functions, and quickly came across a bug in both Railo and ColdFusion with listFilter().

Here's my repro case:

original = "11,23;31:43^53-61";
delims = ",;:^-";

filtered = listFilter(original,function(v){
     return reFind("3$", v);
},delims);

writeDump([original,filtered]);

On both Railo (4.2.0.007) and ColdFusion (11 beta) we get this:

Array
1
string11,23;31:43^53-61
2
string23,;:^-43,;:^-53

Guys?... no. You both seem to be forgetting that when passing the delimiters argument to a list function, it's a multi-char value, not a string. So one should not simply bung the entire string between each list element.

If we "fake" the filtering... just remove the list entries by hand:

viaDeletions = listDeleteAt(original, listFind(original, 11, delims), delims);
viaDeletions = listDeleteAt(viaDeletions, listFind(viaDeletions, 31, delims), delims);
viaDeletions = listDeleteAt(viaDeletions, listFind(viaDeletions, 61, delims), delims);

writeDump([original,viaDeletions]);

We get what we should expect here:

Array
1
string11,23;31:43^53-61
2
string23;43^53

The filter operation seems to be rebuilding the list, not simply conditionally removing elements from it.

So... to be clear... everyone should be aiming to end up the the dump I show immediately above this sentence. I'll raise a bug for both Railo and ColdFusion here (and cross ref here when done. CF: 3750733; Railo: RAILO-3047).

Next I had a look at how both platforms respected the idea of "empty elements". By default list operations completely ignore empty elements - which is wrong, and very poor language design on the part of Allaire - but since... what... CFMX7?... there's been an optional argument on list functions to respect empty elements. So the list iteration functions would have this argument too, right? Test code:

(btw... Jesus this is hard work. I'm at the pub sampling beers like they're going out of fashion, wrote a different blog article in the middle of doing this and chatting with Gavin on Google at the same time. My keystroke error rate is about 50%), Anyway, test code:

list = ",2,,4,,,7,,,,";
lengthNoEmpties = listLen(list);
lengthWithEmpties = listLen(list,",",true);

writeDump([list,lengthNoEmpties,lengthWithEmpties]);

writeOutput("<br>listFilter() with empties: ");
filtered = listFilter(list, function(v){
    if (isNumeric(v)){
        return v mod 2; // ie: odd numbers
    }
    return true;
}, ",", true);
writeDump([filtered]);

And results (Railo first):

Array
1
string,2,,4,,,7,,,,
2
number3
3
number11

listFilter() with empties:
Array
1
string,,,,7,,,,

So Railo gets it dead right.

ColdFusion:

The following information is meant for the website developer for debugging purposes.
Error Occurred While Processing Request

Parameter validation error for the LISTFILTER function.

The function accepts 2 to 3 parameters.

That's not right. The list iteration functions need to - across the board - also expect both a "delimiters" and a "includeEmptyValues" argument. Raised as bug 3750734.

OK. I surrender. My schoolboy error at the pub today was - whilst drinking 1/3 pints - focusing mostly on beers which an alcohol percentage of around 8-9%. So each third pint is like 2/3pint,  and that's forgiving the very real truth than alcohol percentage in beer does not affect a person in a linear fashion (whether that's fact or anecdotal remains to be proven). But I'm well and truly arseholed, so I am gonna give up and post banal interjections on Twitter instead.

--
Adam (hic)