Here's another one that I already knew about, but hadn't looked at it before. Gert brought it up in his keynote today, so I thought I'd have a look. Railo has added labels to <cfloop> and <cfbreak> / <cfcontinue>. But the implementation is either incomplete or non-intuitve (at least to me).
Here's an example, demonstrating how Railo can label a loop, and then reference that label in a <cfbreak> or <cfcontinue>:
<cfset innerThreshold = 6>
<cfset outerThreshold = 11>
<cfoutput>
<cfloop index="x" from="1" to="5" label="outer">
Outer: x:#x#<br>
<cfloop index="y" from="1" to="5" label="inner">
Inner: y:#y#<br>
<cfset sum = x+y>
<cfset product = x*y>
sum:#sum#; product:#product#<br>
<cfif product GT outerThreshold>
#product# > #outerThreshold#: Breaking out of outer<br>
<cfbreak "outer">
</cfif>
<cfif sum GT innerThreshold>
#sum# > #innerThreshold#: Continuing out of inner<br>
<cfcontinue "inner">
</cfif>
Bottom of inner loop<br>
</cfloop>
Bottom of outer loop<hr>
</cfloop>
</cfoutput>
This outputs:
Outer: x:1
Inner: y:1
sum:2; product:1
Bottom of inner loop
Inner: y:2
sum:3; product:2
Bottom of inner loop
Inner: y:3
sum:4; product:3
Bottom of inner loop
Inner: y:4
sum:5; product:4
Bottom of inner loop
Inner: y:5
sum:6; product:5
Bottom of inner loop
Bottom of outer loop
Outer: x:2
Inner: y:1
sum:3; product:2
Bottom of inner loop
Inner: y:2
sum:4; product:4
Bottom of inner loop
Inner: y:3
sum:5; product:6
Bottom of inner loop
Inner: y:4
sum:6; product:8
Bottom of inner loop
Inner: y:5
sum:7; product:10
7 > 6: Continuing out of inner
Bottom of outer loop
Outer: x:3
Inner: y:1
sum:4; product:3
Bottom of inner loop
Inner: y:2
sum:5; product:6
Bottom of inner loop
Inner: y:3
sum:6; product:9
Bottom of inner loop
Inner: y:4
sum:7; product:12
12 > 11: Breaking out of outer
This is ludicrously contrived, but the gist is that the loops have labels, and <cfbreak> and <cfcontinue> can break/continue a specific loop, not simply the "nearest" one to them. Handy!
This however doesn't work with Railo's generic script treatment of <cfloop>:
loopThreshold = 3;
loop index="x" from="1" to="5" label="loopLabel" {
if (x >= loopThreshold){
break "loopLabel";
}
}
This errors with:
Railo 4.1.0.004 Error (template) | |
Message | Wrong Context, tag cfbreak must be inside a cfloop or cfwhile tag with the label [loopLabel] |
Stacktrace | The Error Occurred in C:\webroots\railo-express-4.1.0.004-jre-win32\webapps\www\shared\git\blogExamples\railo\loopLabelsScript1.cfm: line 6 4: loop index="x" from="1" to="5" label="loopLabel" { |
It looks like the CFScript parser hasn't been updated to expect this. To test the syntax, I just converted this back to tags:
<cfset loopThreshold = 6>
<cfloop index="x" from="1" to="5" label="loopLabel">
<cfif x GE loopThreshold>
<cfbreak "loopLabel">
</cfif>
</cfloop>
This is the same code, except with the <cf and > put back on, and it works (ie: it compiles. I know the code itself doesn't really demonstrate breaking out of labelled loops in a sensible way). So I think my attempted CFScript syntax is correct, but it dun't work.
I also tried with a proper for() loop, and that just errored too. I really had to guess at the syntax for that, though:
loopThreshold = 3;
for (x=1; x <= 5; x++; "loopLabel") {
if (x >= loopThreshold){
break "loopLabel";
}
}
So that was a bad guess, as this is what happened:
Railo 4.1.0.004 Error (template) | |
Message | invalid syntax in for statement, for statement must end with a [)] |
Stacktrace | The Error Occurred in C:\webroots\railo-express-4.1.0.004-jre-win32\webapps\www\shared\git\blogExamples\railo\loopLabelsScript2.cfm: line 4 2: loopThreshold = 6; |
Also... no mention of loop labels in the docs for <cfloop>, either.
Update:
I collared Mark Drew in the bar last night, and he gave me the correct syntax for labelled loops in CFScript:
This makes sense, in hindsight: decoupling the label from the looping construct. I means labels can be used by other constructs too.<cfgoto>, anyone? ;-)
I collared Mark Drew in the bar last night, and he gave me the correct syntax for labelled loops in CFScript:
loopThreshold = 3;
loopLabel: for(x=1; x <= 5; x++){
writeOutput("Top of loop with #x#<br>");
if (x >= loopThreshold){
break loopLabel;
}
writeOutput("Bottom of loop<br>");
}
writeOutput("After loop<br>");
This makes sense, in hindsight: decoupling the label from the looping construct. I means labels can be used by other constructs too.<cfgoto>, anyone? ;-)
I did test with a conditional loop, and that worked as one would expect:
<cfset count = 0>
<cfset threshold = 5>
<cfset outerPanicAt = 10>
<cfset outerPanicCount = 0>
<cfset innerPanicAt = 10>
<cfset innerPanicCount = 0>
<cfloop condition="outerPanicCount LE outerPanicAt" label="outer">
<cfset outerPanicCount++>
<cfoutput>outerPanicCount: #outerPanicCount#<br></cfoutput>
<cfloop condition="innerPanicCount LE innerPanicAt">
<cfset innerPanicCount++>
<cfoutput>innerPanicCount: #innerPanicCount#<br></cfoutput>
<cfset count++>
<cfoutput>count: #count#<br></cfoutput>
<cfif count GE threshold>
Threshold met<br>
<cfbreak "outer">
</cfif>
Bottom of inner loop<br>
</cfloop>
Bottom of outer loop<br>
</cfloop>
After loops<br>
This outputs:
outerPanicCount: 1
innerPanicCount: 1
count: 1
Bottom of inner loop
innerPanicCount: 2
count: 2
Bottom of inner loop
innerPanicCount: 3
count: 3
Bottom of inner loop
innerPanicCount: 4
count: 4
Bottom of inner loop
innerPanicCount: 5
count: 5
Threshold met
After loops
Spot on.
So not a bad feature, but maybe not as complete as I've come to expect from the Railo guys. I've definitely needed this in the past, but I mostly use CFScript so I'd like to see support extended out to script code too.
--
Adam