Skip to content

Commit 3104774

Browse files
authored
Merge pull request #1269 from HubSpot/eager-break-and-continue
Enable break and continue tags to work in eager execution
2 parents 462eaf3 + 09b6c35 commit 3104774

File tree

15 files changed

+168
-39
lines changed

15 files changed

+168
-39
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.hubspot.jinjava.lib.tag.eager;
2+
3+
import com.google.common.annotations.Beta;
4+
import com.hubspot.jinjava.interpret.DeferredValue;
5+
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
6+
import com.hubspot.jinjava.lib.tag.ContinueTag;
7+
import com.hubspot.jinjava.lib.tag.ForTag;
8+
import com.hubspot.jinjava.tree.parse.TagToken;
9+
10+
/**
11+
* Eager decorator for the continue tag that handles reconstruction when the continue
12+
* is inside a deferred context (e.g., when in deferred execution mode such as
13+
* inside a deferred if condition within a for loop).
14+
*/
15+
@Beta
16+
public class EagerContinueTag extends EagerTagDecorator<ContinueTag> {
17+
18+
public EagerContinueTag() {
19+
super(new ContinueTag());
20+
}
21+
22+
public EagerContinueTag(ContinueTag continueTag) {
23+
super(continueTag);
24+
}
25+
26+
@Override
27+
public String getEagerTagImage(TagToken tagToken, JinjavaInterpreter interpreter) {
28+
if (!(interpreter.getContext().get(ForTag.LOOP) instanceof DeferredValue)) {
29+
interpreter.getContext().replace(ForTag.LOOP, DeferredValue.instance());
30+
}
31+
return super.getEagerTagImage(tagToken, interpreter);
32+
}
33+
}

src/main/java/com/hubspot/jinjava/lib/tag/eager/EagerTagFactory.java

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import com.google.common.collect.ImmutableMap;
66
import com.google.common.collect.ImmutableSet;
77
import com.hubspot.jinjava.lib.tag.BlockTag;
8-
import com.hubspot.jinjava.lib.tag.BreakTag;
98
import com.hubspot.jinjava.lib.tag.CallTag;
109
import com.hubspot.jinjava.lib.tag.ContinueTag;
1110
import com.hubspot.jinjava.lib.tag.CycleTag;
@@ -46,6 +45,7 @@ public class EagerTagFactory {
4645
.put(IfTag.class, EagerIfTag.class)
4746
.put(UnlessTag.class, EagerUnlessTag.class)
4847
.put(CallTag.class, EagerCallTag.class)
48+
.put(ContinueTag.class, EagerContinueTag.class)
4949
.build();
5050
// These classes don't need an eager decorator.
5151
public static final Set<Class<? extends Tag>> TAG_CLASSES_TO_SKIP = ImmutableSet
@@ -56,8 +56,6 @@ public class EagerTagFactory {
5656
.add(ElseTag.class)
5757
.add(RawTag.class)
5858
.add(ExtendsTag.class) // TODO support reconstructing extends tags
59-
.add(BreakTag.class) // TODO support eager break tag
60-
.add(ContinueTag.class) // TODO support eager continue tag
6159
.build();
6260

6361
@SuppressWarnings("unchecked")

src/test/java/com/hubspot/jinjava/EagerTest.java

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.ArrayList;
2525
import java.util.Collections;
2626
import java.util.HashMap;
27+
import java.util.List;
2728
import java.util.Map;
2829
import java.util.Optional;
2930
import java.util.Set;
@@ -1647,42 +1648,74 @@ public void itWrapsMacroThatWouldChangeCurrentPathInChildScope() {
16471648

16481649
@Test
16491650
public void itHandlesDeferredBreakInForLoop() {
1650-
String input = expectedTemplateInterpreter.getFixtureTemplate(
1651+
expectedTemplateInterpreter.assertExpectedOutput(
16511652
"handles-deferred-break-in-for-loop/test"
16521653
);
1653-
interpreter.render(input);
1654-
// We don't support this yet
1655-
assertThat(interpreter.getContext().getDeferredNodes()).isNotEmpty();
1654+
}
1655+
1656+
@Test
1657+
public void itHandlesDeferredBreakInForLoopSecondPass() {
1658+
localContext.put("deferred", 1);
1659+
expectedTemplateInterpreter.assertExpectedOutput(
1660+
"handles-deferred-break-in-for-loop/test.expected"
1661+
);
1662+
expectedTemplateInterpreter.assertExpectedNonEagerOutput(
1663+
"handles-deferred-break-in-for-loop/test.expected"
1664+
);
16561665
}
16571666

16581667
@Test
16591668
public void itHandlesBreakInDeferredForLoop() {
1660-
String input = expectedTemplateInterpreter.getFixtureTemplate(
1669+
expectedTemplateInterpreter.assertExpectedOutput(
16611670
"handles-break-in-deferred-for-loop/test"
16621671
);
1663-
interpreter.render(input);
1664-
// We don't support this yet
1665-
assertThat(interpreter.getContext().getDeferredNodes()).isNotEmpty();
1672+
}
1673+
1674+
@Test
1675+
public void itHandlesBreakInDeferredForLoopSecondPass() {
1676+
localContext.put("deferred", List.of(0, 1, 2, 3, 4, 5));
1677+
expectedTemplateInterpreter.assertExpectedOutput(
1678+
"handles-break-in-deferred-for-loop/test.expected"
1679+
);
1680+
expectedTemplateInterpreter.assertExpectedNonEagerOutput(
1681+
"handles-break-in-deferred-for-loop/test.expected"
1682+
);
16661683
}
16671684

16681685
@Test
16691686
public void itHandlesDeferredContinueInForLoop() {
1670-
String input = expectedTemplateInterpreter.getFixtureTemplate(
1687+
expectedTemplateInterpreter.assertExpectedOutput(
16711688
"handles-deferred-continue-in-for-loop/test"
16721689
);
1673-
interpreter.render(input);
1674-
// We don't support this yet
1675-
assertThat(interpreter.getContext().getDeferredNodes()).isNotEmpty();
1690+
}
1691+
1692+
@Test
1693+
public void itHandlesDeferredContinueInForLoopSecondPass() {
1694+
localContext.put("deferred", 2);
1695+
expectedTemplateInterpreter.assertExpectedOutput(
1696+
"handles-deferred-continue-in-for-loop/test.expected"
1697+
);
1698+
expectedTemplateInterpreter.assertExpectedNonEagerOutput(
1699+
"handles-deferred-continue-in-for-loop/test.expected"
1700+
);
16761701
}
16771702

16781703
@Test
16791704
public void itHandlesContinueInDeferredForLoop() {
1680-
String input = expectedTemplateInterpreter.getFixtureTemplate(
1705+
expectedTemplateInterpreter.assertExpectedOutput(
16811706
"handles-continue-in-deferred-for-loop/test"
16821707
);
1683-
interpreter.render(input);
1684-
// We don't support this yet
1685-
assertThat(interpreter.getContext().getDeferredNodes()).isNotEmpty();
1708+
}
1709+
1710+
@Test
1711+
public void itHandlesContinueInDeferredForLoopSecondPass() {
1712+
localContext.put("deferred", List.of(0, 1, 2, 3, 4, 5));
1713+
expectedTemplateInterpreter.assertExpectedOutput(
1714+
"handles-continue-in-deferred-for-loop/test.expected"
1715+
);
1716+
expectedTemplateInterpreter.assertExpectedNonEagerOutput(
1717+
"handles-continue-in-deferred-for-loop/test.expected"
1718+
);
16861719
}
16871720

16881721
@Test
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Start loop
2+
i is: 0
3+
i is: 1
4+
End loop
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Start loop
2+
{% for i in deferred %}\
3+
{% if i > 1 %}\
4+
{% break '' %}\
5+
{% endif %}\
6+
i is: {{ i }}
7+
{% endfor %}\
8+
End loop
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
Start loop
2-
{% for i in deferred %}
3-
{% if i %}
4-
{% break %}
5-
{% endif %}
2+
{% for i in deferred -%}
3+
{% if i > 1 -%}
4+
{% break -%}
5+
{% endif -%}
66
i is: {{ i }}
7-
{% endfor %}
7+
{% endfor -%}
88
End loop
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Start loop
2+
i is: 1
3+
i is: 3
4+
i is: 5
5+
End loop
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Start loop
2+
{% for i in deferred %}\
3+
{% if i % 2 == 0 %}\
4+
{% continue '' %}\
5+
{% endif %}\
6+
i is: {{ i }}
7+
{% endfor %}\
8+
End loop
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
Start loop
2-
{% for i in deferred %}
3-
{% if i %}
4-
{% continue %}
5-
{% endif %}
2+
{% for i in deferred -%}
3+
{% if i % 2 == 0 -%}
4+
{% continue -%}
5+
{% endif -%}
66
i is: {{ i }}
7-
{% endfor %}
7+
{% endfor -%}
88
End loop
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Start loop
2+
i is: 0
3+
i is: 1
4+
End loop

0 commit comments

Comments
 (0)