-
Notifications
You must be signed in to change notification settings - Fork 170
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1219 from ccutrer/loop-extensions
Implement jinja2.ext.loopcontrols extensions
- Loading branch information
Showing
10 changed files
with
253 additions
and
4 deletions.
There are no files selected for viewing
14 changes: 14 additions & 0 deletions
14
src/main/java/com/hubspot/jinjava/interpret/NotInLoopException.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.hubspot.jinjava.interpret; | ||
|
||
/** | ||
* Exception thrown when `continue` or `break` is called outside of a loop | ||
*/ | ||
public class NotInLoopException extends InterpretException { | ||
|
||
public static final String MESSAGE_PREFIX = "`"; | ||
public static final String MESSAGE_SUFFIX = "` called while not in a for loop"; | ||
|
||
public NotInLoopException(String tagName) { | ||
super(MESSAGE_PREFIX + tagName + MESSAGE_SUFFIX); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.hubspot.jinjava.lib.tag; | ||
|
||
import com.hubspot.jinjava.doc.annotations.JinjavaDoc; | ||
import com.hubspot.jinjava.doc.annotations.JinjavaTextMateSnippet; | ||
import com.hubspot.jinjava.interpret.JinjavaInterpreter; | ||
import com.hubspot.jinjava.interpret.NotInLoopException; | ||
import com.hubspot.jinjava.tree.TagNode; | ||
import com.hubspot.jinjava.util.ForLoop; | ||
|
||
/** | ||
* Implements the common loopcontrol `continue`, as in the jinja2.ext.loopcontrols extension | ||
* @author ccutrer | ||
*/ | ||
|
||
@JinjavaDoc( | ||
value = "Stops executing the current for loop, including any further iterations" | ||
) | ||
@JinjavaTextMateSnippet( | ||
code = "{% for item in [1, 2, 3, 4] %}{% if item > 2 == 0 %}{% break %}{% endif %}{{ item }}{% endfor %}" | ||
) | ||
public class BreakTag implements Tag { | ||
|
||
public static final String TAG_NAME = "break"; | ||
|
||
@Override | ||
public String getName() { | ||
return TAG_NAME; | ||
} | ||
|
||
@Override | ||
public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { | ||
Object loop = interpreter.getContext().get(ForTag.LOOP); | ||
if (loop instanceof ForLoop) { | ||
ForLoop forLoop = (ForLoop) loop; | ||
forLoop.doBreak(); | ||
} else { | ||
throw new NotInLoopException(TAG_NAME); | ||
} | ||
return ""; | ||
} | ||
|
||
@Override | ||
public String getEndTagName() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public boolean isRenderedInValidationMode() { | ||
return true; | ||
} | ||
} |
49 changes: 49 additions & 0 deletions
49
src/main/java/com/hubspot/jinjava/lib/tag/ContinueTag.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
package com.hubspot.jinjava.lib.tag; | ||
|
||
import com.hubspot.jinjava.doc.annotations.JinjavaDoc; | ||
import com.hubspot.jinjava.doc.annotations.JinjavaTextMateSnippet; | ||
import com.hubspot.jinjava.interpret.JinjavaInterpreter; | ||
import com.hubspot.jinjava.interpret.NotInLoopException; | ||
import com.hubspot.jinjava.tree.TagNode; | ||
import com.hubspot.jinjava.util.ForLoop; | ||
|
||
/** | ||
* Implements the common loopcontrol `continue`, as in the jinja2.ext.loopcontrols extension | ||
* @author ccutrer | ||
*/ | ||
|
||
@JinjavaDoc(value = "Stops executing the current iteration of the current for loop") | ||
@JinjavaTextMateSnippet( | ||
code = "{% for item in [1, 2, 3, 4] %}{% if item % 2 == 0 %}{% continue %}{% endif %}{{ item }}{% endfor %}" | ||
) | ||
public class ContinueTag implements Tag { | ||
|
||
public static final String TAG_NAME = "continue"; | ||
|
||
@Override | ||
public String getName() { | ||
return TAG_NAME; | ||
} | ||
|
||
@Override | ||
public String interpret(TagNode tagNode, JinjavaInterpreter interpreter) { | ||
Object loop = interpreter.getContext().get(ForTag.LOOP); | ||
if (loop instanceof ForLoop) { | ||
ForLoop forLoop = (ForLoop) loop; | ||
forLoop.doContinue(); | ||
} else { | ||
throw new NotInLoopException(TAG_NAME); | ||
} | ||
return ""; | ||
} | ||
|
||
@Override | ||
public String getEndTagName() { | ||
return null; | ||
} | ||
|
||
@Override | ||
public boolean isRenderedInValidationMode() { | ||
return true; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
51 changes: 51 additions & 0 deletions
51
src/test/java/com/hubspot/jinjava/lib/tag/BreakTagTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.hubspot.jinjava.lib.tag; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import com.hubspot.jinjava.BaseInterpretingTest; | ||
import com.hubspot.jinjava.interpret.RenderResult; | ||
import com.hubspot.jinjava.interpret.TemplateError; | ||
import org.junit.Test; | ||
|
||
public class BreakTagTest extends BaseInterpretingTest { | ||
|
||
@Test | ||
public void testBreak() { | ||
String template = | ||
"{% for item in [1, 2, 3, 4] %}{% if item > 2 %}{% break %}{% endif %}{{ item }}{% endfor %}"; | ||
|
||
RenderResult rendered = jinjava.renderForResult(template, context); | ||
assertThat(rendered.getOutput()).isEqualTo("12"); | ||
} | ||
|
||
@Test | ||
public void testNestedBreak() { | ||
String template = | ||
"{% for item in [1, 2, 3, 4] %}{% for item2 in [5, 6, 7] %}{% break %}{{ item2 }}{% endfor %}{{ item }}{% endfor %}"; | ||
|
||
RenderResult rendered = jinjava.renderForResult(template, context); | ||
assertThat(rendered.getOutput()).isEqualTo("1234"); | ||
} | ||
|
||
@Test | ||
public void testBreakWithEarlierContent() { | ||
String template = | ||
"{% for item in [1, 2, 3, 4] %}{{ item }}{% if item > 2 %}{% break %}{% endif %}{{ item }}{% endfor %}"; | ||
|
||
RenderResult rendered = jinjava.renderForResult(template, context); | ||
assertThat(rendered.getOutput()).isEqualTo("11223"); | ||
} | ||
|
||
@Test | ||
public void testBreakOutOfContext() { | ||
String template = "{% break %}"; | ||
|
||
RenderResult rendered = jinjava.renderForResult(template, context); | ||
assertThat(rendered.getOutput()).isEqualTo(""); | ||
assertThat(rendered.getErrors()).hasSize(1); | ||
assertThat(rendered.getErrors().get(0).getSeverity()) | ||
.isEqualTo(TemplateError.ErrorType.FATAL); | ||
assertThat(rendered.getErrors().get(0).getMessage()) | ||
.contains("NotInLoopException: `break` called while not in a for loop"); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
src/test/java/com/hubspot/jinjava/lib/tag/ContinueTagTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.hubspot.jinjava.lib.tag; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
|
||
import com.hubspot.jinjava.BaseInterpretingTest; | ||
import com.hubspot.jinjava.interpret.RenderResult; | ||
import com.hubspot.jinjava.interpret.TemplateError; | ||
import org.junit.Test; | ||
|
||
public class ContinueTagTest extends BaseInterpretingTest { | ||
|
||
@Test | ||
public void testContinue() { | ||
String template = | ||
"{% for item in [1, 2, 3, 4] %}{% if item % 2 == 0 %}{% continue %}{% endif %}{{ item }}{% endfor %}"; | ||
|
||
RenderResult rendered = jinjava.renderForResult(template, context); | ||
assertThat(rendered.getOutput()).isEqualTo("13"); | ||
} | ||
|
||
@Test | ||
public void testNestedContinue() { | ||
String template = | ||
"{% for item in [1, 2, 3, 4] %}{% for item2 in [5, 6, 7] %}{% continue %}{{ item2 }}{% endfor %}{{ item }}{% endfor %}"; | ||
|
||
RenderResult rendered = jinjava.renderForResult(template, context); | ||
assertThat(rendered.getOutput()).isEqualTo("1234"); | ||
} | ||
|
||
@Test | ||
public void testContinueWithEarlierContent() { | ||
String template = | ||
"{% for item in [1, 2, 3, 4] %}{{ item }}{% if item % 2 == 0 %}{% continue %}{% endif %}{{ item }}{% endfor %}"; | ||
|
||
RenderResult rendered = jinjava.renderForResult(template, context); | ||
assertThat(rendered.getOutput()).isEqualTo("112334"); | ||
} | ||
|
||
@Test | ||
public void testContinueOutOfContext() { | ||
String template = "{% continue %}"; | ||
|
||
RenderResult rendered = jinjava.renderForResult(template, context); | ||
assertThat(rendered.getOutput()).isEqualTo(""); | ||
assertThat(rendered.getErrors()).hasSize(1); | ||
assertThat(rendered.getErrors().get(0).getSeverity()) | ||
.isEqualTo(TemplateError.ErrorType.FATAL); | ||
assertThat(rendered.getErrors().get(0).getMessage()) | ||
.contains("NotInLoopException: `continue` called while not in a for loop"); | ||
} | ||
} |