Sunday, 4 May 2014

ColdFusion 11: cfloop in CFScript very broken

G'day:
I started having a further look at what Adobe have done for CFScript in ColdFusion 11. According to their docs ("CFScript: Script support for tags") "ColdFusion 11 allows you to write all the tags in the script format in a very generic manner". A colleague of mine was looking at how <cfloop> was implemented in CFScript (yeah... why? I dunno... they shoulda just used for() loops), and we quickly came away going "shit, what a mess".

First attempt:

cfloop(index="i", from=1, to=4){
    writeOutput(i);
}

Result:

Error Occurred While Processing Request

Variable I is undefined.

The error occurred inC:/webroots/CF11/shared/git/blogExamples/coldfusion/CF11/script/loop/baseline.cfm: line 3
1 : <cfscript>
2 :  cfloop(index="i", from=1, to=4){
3 :   writeOutput(i);
4 :  }
5 : </cfscript>

Huh?

Second attempt:

cfloop(index="i", from=1, to=4){
    writeDump(variables);
}

Result:

struct [empty]

Huh?

Third attempt:

cfloop(index="i", from=1, to=4, step=2){
    writeDump(variables);
}

Result:

Error Occurred While Processing Request

Attribute validation error for the loop tag.

The tag does not have an attribute called step. The valid attribute(s) are query, startrow, endrow, file, index, from, to, characters, charset, group, groupcasesensitive.
ColdFusion cannot determine the line of the template that caused this error.This is often caused by an error in the exception handling subsystem.

What the hell, Adobe?

Also, I had begun to think that perhaps cfloop() simply wasn't implemented, but if not:

  1. I should get a compile error (along these lines, but just saying "nuh-uh");
  2. given this compile error, it's clear that it has been implemented, otherwise it would not be telling me what the correct syntax should be.
Additional observations:
  1. the error message is saying loop tag. This is not a tag I am using cfloop();
  2. according to the error, my first two examples should work fine: index, from, to loops are supported;
  3. step should be supported too.
OK, so flag indexed loops. How about collection loops:

numbers={one="tahi",two="rua",three="toru", four="wha"};
cfloop(collection=numbers, item="key"){
    writeOutput("#key#: #numbers[key]#<br>");
}

Result:

Error Occurred While Processing Request

Attribute validation error for the loop tag.

The tag does not have an attribute called collection. The valid attribute(s) are query, startrow, endrow, file, index, from, to, characters, charset, group, groupcasesensitive.
ColdFusion cannot determine the line of the template that caused this error.This is often caused by an error in the exception handling subsystem.

OK, I should have noticed the lack of collection or item being mentioned in the previous error. Oops. But, still: where is it? <cfloop> supports these. So its script equivalent should too.

Forget collections. Arrays...

colours =  ["Whero","Karaka","Kowhai","Kakariki","Kikorangi","Tawatawa","Mawhero"];
cfloop(aray=colours, index="colour"){
    writeOutput("#colour#<br>");
}

Result:

Error Occurred While Processing Request

Attribute validation error for CFLOOP tag in cfscript.

It does not allow the attribute(s) ARAY. The valid attribute(s) are ARRAY,CHARACTERS,CHARSET,COLLECTION,CONDITION, DELIMITERS,ENDROW,FILE,FROM,GROUP,GROUPCASESENSITIVE, INDEX,ITEM,LIST,QUERY,STARTROW,STEP,TO.
The error occurred in /blogExamples/coldfusion/CF11/script/loop/typo.cfm: line 3
1 : <cfscript>
2 : colours =  ["Whero","Karaka","Kowhai","Kakariki", "Kikorangi","Tawatawa","Mawhero"];
3 : cfloop(aray=colours, index="colour"){
4 :  writeOutput("#colour#<br>");
5 : }

Dammit. Typo (disclosure: the typo occurred in a different situation, but the results were the same. The code above is just a variation of the next example I was gonna try).

But what's this? Suddenly collection, item and step are supposedly supported. I'm now wondering if this is just some sort of elaborate wind-up on the part of Adobe, and they have something in the ColdFusion codebase that detects me writing blog articles and just does weird random shit to my code.

OK, so fix that typo:

colours =  ["Whero","Karaka","Kowhai","Kakariki","Kikorangi","Tawatawa","Mawhero"];
cfloop(array=colours, index="colour"){
    writeOutput("#colour#<br>");
}

And, hohoho, we're back to this error message again:

Error Occurred While Processing Request

Attribute validation error for the loop tag.

The tag does not have an attribute called array. The valid attribute(s) are query, startrow, endrow, file, index, from, to, characters, charset, group, groupcasesensitive.
ColdFusion cannot determine the line of the template that caused this error.This is often caused by an error in the exception handling subsystem.

What about a query then?

week = queryNew("id,en,mi", "integer,varchar,varchar", [
    [1,"Monday","Rāhina"],
    [2,"Tuesday","Rātū"],
    [3,"Wednesday","Rāapa"],
    [4,"Thursday","Rāpare"],
    [5,"Friday","Rāmere"],
    [6,"Saturday","Rāhoroi"],
    [7,"Sunday","Rātapu"]
]);

cfloop(query=week, startrow=2, endrow=4){
    writeOutput("#id#: #mi# (#en#)<br>");
}

Result:

2: Ratu (Tuesday)
3: Raapa (Wednesday)
4: Rapare (Thursday)


Blimey. Something actually worked. Let's ramp this up a bit

numbers = queryNew("id,digit,number", "integer,varchar,varchar", [
    [1, 1, "tahi"],
    [2, 1, "один"],
    [3, 2, "rua"],
    [4, 2, "два"],
    [5, 3, "toru"],
    [6, 3, "три"],
    [7, 4, "wha"],
    [8, 4, "четыре"]
]);

cfloop(query=numbers, group="digit"){
    writeOutput("#digit#:");
    cfloop(){
        writeOutput(" #number#");
    }
    writeOutput("<br>");
}

Result:
1: tahi один
2: rua два
3: toru три
4: wha четыре


Another win! It looks like query support for cfloop() actually works.

What about a condition loop:

months = queryNew("id,mi,anglicised,en", "integer,varchar,varchar,varchar", [
    [1,"Pipiri","Hune","June"],
    [2,"Hōngongoi","Hūrae","July"],
    [3,"Here-turi-kōkā","Akuhata","August"],
    [4,"Mahuru","Hepetema","September"],
    [5,"Whiringa-ā-nuku","Oketopa","October"],
    [6,"Whiringa-ā-rangi","Noema","November"],
    [7,"Hakihea","Tihema","December"],
    [8,"Kohi-tātea","Hānuere","January"],
    [9,"Hui-tanguru","Pēpuere","February"],
    [10,"Poutū-te-rangi","Maehe","March"],
    [11,"Paenga-whāwhā","Āperira","April"],
    [12,"Haratua","Mei","May"]
]);
row=1;
cfloop(condition="row LE 6"){
    rowData = queryGetRow(months,row);
    writeOutput("#row#: #rowData.mi# #rowData.anglicised# (#rowData.en#)<br>");
    row++;
}

Nope:

Error Occurred While Processing Request

Attribute validation error for the loop tag.

The tag does not have an attribute called condition. The valid attribute(s) are query, startrow, endrow, file, index, from, to, characters, charset, group, groupcasesensitive.
ColdFusion cannot determine the line of the template that caused this error.This is often caused by an error in the exception handling subsystem.

Note: Railo also chokes on this:

Railo 4.2.0.008 Error (template)
Messagetag loop-condition is not supported within cfscript, use instead a while statement.
StacktraceThe Error Occurred inx
C:\webroots\railo-express-4.1.x-jre-win64\webapps\www\shared\git\blogExamples\coldfusion\CF11\script\loop\condition.cfm: line 21 
19: writeOutput("#row#: #rowData.mi# #rowData.anglicised# (#rowData.en#)<br>");
20: row++;
21: }
22: </cfscript>

(all the other ones worked fine on Railo, btw. Except the typo one, obviously)

The most common error message on ColdFusion suggests file looping should work, so I'll try that:

i=0;
cfloop(file=expandPath("./file.dat"), index="line", charset="UTF-8"){
    writeOutput("#++i#: #line#<br>");
}

And it works:

1: Pipiri Hune June
2: Hōngongoi Hūrae July
3: Here-turi-kōkā Akuhata August
4: Mahuru Hepetema September
5: Whiringa-ā-nuku Oketopa October
6: Whiringa-ā-rangi Noema November
7: Hakihea Tihema December
8: Kohi-tātea Hānuere January
9: Hui-tanguru Pēpuere February
10: Poutū-te-rangi Maehe March
11: Paenga-whāwhā Āperira April
12: Haratua Mei May

And what about a character-based file read:

i=0;
cfloop(file=expandPath("./file.dat"), index="chars", characters=64, charset="UTF-8"){
    writeOutput("#++i#: #chars#<br>");
}

(this worked too: I'll spare you the output though... you get the idea)

For completeness, I'll try a list loop too:

seasons = "kōanga,raumati,ngahuru,hōtoke";

cfloop(list=seasons, index="season"){
    writeOutput("#season#<br>");
}

And, somewhat predictably by now, this errors too:

Error Occurred While Processing Request

Attribute validation error for the loop tag.

The tag does not have an attribute called list. The valid attribute(s) are query, startrow, endrow, file, index, from, to, characters, charset, group, groupcasesensitive.
ColdFusion cannot determine the line of the template that caused this error.This is often caused by an error in the exception handling subsystem.

So, in summary. These sort of <cfloop> operations don't work:

  • collection
  • array
  • list
  • indexed
  • condition
These ones do work:

  • query
  • file
It's as if Adobe have only bothered to implement the functionality which isn't already present in other loops. Except forgetting about list loops whilst they were at it (although arguably this is covered with the list iteration functions).

I think this is another example where Adobe have taken a shortcut and added uncertainty to the language. The CFScript equivalents of tags should mirror the tags lock-stock. Especially as that's what Adobe have claimed they have done: "ColdFusion 11 allows you to write all the tags in the script format in a very generic manner". If they're going to support this cfloop() construct at all (and I question its merit), then they should support it completely. Not pick and choose which bits best suit them to implement.

They also need to get their error handling sorted out. The error messages are incoherent, refer to tags when I'm not using any tags, and contradict themselves. I'm going to raise bugs for the missing functionality (3754577), and for the incoherent error messaging (3754578).

I have to say I think Adobe have made a bit of a mess of this one.

--
Adam