Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ protected Triple<String, String, String> getPrefixTokenAndSuffix(
interpreter,
DeferredToken
.builderFromImage(joiner.toString(), tagNode.getMaster())
.addUsedDeferredWords(getNamespaceRootsForDottedSet(variables, interpreter))
.addSetDeferredWords(Stream.of(variables))
.build()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public Triple<String, String, String> getPrefixTokenAndSuffix(
DeferredToken
.builderFromImage(joiner.toString(), tagNode.getMaster())
.addUsedDeferredWords(eagerExecutionResult.getResult().getDeferredWords())
.addUsedDeferredWords(getNamespaceRootsForDottedSet(variables, interpreter))
.addSetDeferredWords(Arrays.stream(variables).map(String::trim))
.build()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.hubspot.jinjava.interpret.MetaContextVariables;
import com.hubspot.jinjava.lib.tag.SetTag;
import com.hubspot.jinjava.loader.RelativePathResolver;
import com.hubspot.jinjava.objects.Namespace;
import com.hubspot.jinjava.tree.TagNode;
import com.hubspot.jinjava.util.EagerReconstructionUtils;
import com.hubspot.jinjava.util.PrefixToPreserveState;
Expand All @@ -26,6 +27,19 @@ protected EagerSetTagStrategy(SetTag setTag) {
this.setTag = setTag;
}

protected static Stream<String> getNamespaceRootsForDottedSet(
String[] variables,
JinjavaInterpreter interpreter
) {
return Arrays
.stream(variables)
.map(String::trim)
.filter(variable -> variable.contains("."))
.map(variable -> variable.substring(0, variable.indexOf('.')))
.distinct()
.filter(root -> interpreter.getContext().get(root) instanceof Namespace);
}

public String run(TagNode tagNode, JinjavaInterpreter interpreter) {
int eqPos = tagNode.getHelpers().indexOf('=');
String[] variables;
Expand Down
36 changes: 36 additions & 0 deletions src/test/java/com/hubspot/jinjava/EagerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,42 @@ public void itHandlesDeferredInNamespaceSecondPass() {
);
}

@Test
public void itReconstructsNamespaceForSetTagsAcrossScope() {
expectedTemplateInterpreter.assertExpectedOutput(
"reconstructs-namespace-for-set-tags-across-scope/test"
);
}

@Test
public void itReconstructsNamespaceForSetTagsAcrossScopeSecondPass() {
localContext.put("deferred", "resolved");
expectedTemplateInterpreter.assertExpectedOutput(
"reconstructs-namespace-for-set-tags-across-scope/test.expected"
);
expectedTemplateInterpreter.assertExpectedNonEagerOutput(
"reconstructs-namespace-for-set-tags-across-scope/test.expected"
);
}

@Test
public void itReconstructsNamespaceForSetTagsInForLoop() {
expectedTemplateInterpreter.assertExpectedOutput(
"reconstructs-namespace-for-set-tags-in-for-loop/test"
);
}

@Test
public void itReconstructsNamespaceForSetTagsInForLoopSecondPass() {
localContext.put("deferred", 2);
expectedTemplateInterpreter.assertExpectedOutput(
"reconstructs-namespace-for-set-tags-in-for-loop/test.expected"
);
expectedTemplateInterpreter.assertExpectedNonEagerOutput(
"reconstructs-namespace-for-set-tags-in-for-loop/test.expected"
);
}

@Test
public void itHandlesClashingNameInMacro() {
expectedTemplateInterpreter.assertExpectedOutput(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import com.hubspot.jinjava.interpret.TemplateError.ErrorReason;
import com.hubspot.jinjava.lib.tag.SetTagTest;
import com.hubspot.jinjava.mode.EagerExecutionMode;
import com.hubspot.jinjava.objects.Namespace;
import com.hubspot.jinjava.tree.parse.TagToken;
import java.util.HashMap;
import java.util.Optional;
import java.util.stream.Collectors;
import org.junit.After;
Expand Down Expand Up @@ -98,6 +100,63 @@ public void itHandlesMultipleVars() {
.containsExactlyInAnyOrder("deferred", "range");
}

@Test
public void itDefersNamespaceRootAsUsedWordForDottedSet() {
context.put("ns", new Namespace());
context.setDeferredExecutionMode(true);
interpreter.render("{% set ns.enabled = deferred %}");
Optional<DeferredToken> maybeDeferredToken = context
.getDeferredTokens()
.stream()
.filter(e -> ((TagToken) e.getToken()).getTagName().equals(tag.getName()))
.findAny();
assertThat(maybeDeferredToken).isPresent();
assertThat(maybeDeferredToken.get().getSetDeferredWords())
.containsExactly("ns.enabled");
assertThat(maybeDeferredToken.get().getUsedDeferredWords()).contains("ns");
}

@Test
public void itDefersNamespaceRootAsUsedWordForDottedBlockSet() {
context.put("ns", new Namespace());
context.setDeferredExecutionMode(true);
interpreter.render("{% set ns.enabled %}{{ deferred }}{% endset %}");
Optional<DeferredToken> maybeDeferredToken = context
.getDeferredTokens()
.stream()
.filter(e -> e.getToken() instanceof TagToken)
.filter(e -> ((TagToken) e.getToken()).getTagName().equals(tag.getName()))
.findAny();
assertThat(maybeDeferredToken).isPresent();
assertThat(maybeDeferredToken.get().getSetDeferredWords())
.containsExactly("ns.enabled");
assertThat(maybeDeferredToken.get().getUsedDeferredWords()).contains("ns");
}

@Test
public void itDoesNotDeferNonNamespaceRootAsUsedWordForDottedSet() {
context.put("someMap", new HashMap<String, Object>());
context.setDeferredExecutionMode(true);
interpreter.render("{% set someMap.field = deferred %}");
Optional<DeferredToken> maybeDeferredToken = context
.getDeferredTokens()
.stream()
.filter(e -> ((TagToken) e.getToken()).getTagName().equals(tag.getName()))
.findAny();
assertThat(maybeDeferredToken).isPresent();
assertThat(maybeDeferredToken.get().getUsedDeferredWords()).doesNotContain("someMap");
}

@Test
public void itDoesNotThrowWhenNamespaceHoldsUnserializableValue() {
Namespace ns = new Namespace();
ns.put("obj", new Object());
context.put("ns", ns);
context.setDeferredExecutionMode(true);
interpreter.render("{% set ns.enabled = deferred %}");
assertThat(interpreter.getErrors()).isEmpty();
}

@Test
public void itLimitsLength() {
StringBuilder tooLong = new StringBuilder();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% set ns = namespace({'enabled': false} ) %}\
{% set __macro_setit_1985651457_temp_variable_0__ %}\
{% set ns.enabled = deferred %}\
{% endset %}\
{{ __macro_setit_1985651457_temp_variable_0__ }}
{{ ns.enabled }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{%- set ns = namespace(enabled=false) %}
{%- macro setit() %}{% set ns.enabled = deferred %}{% endmacro %}
{{- setit() -}}
{{ ns.enabled }}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
true
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{% set ns = namespace({'found': false} ) %}\
{% for __ignored__ in [0] %}\
{% if 1 == deferred %}\
{% set ns.found = true %}\
{% endif %}\
{% if 2 == deferred %}\
{% set ns.found = true %}\
{% endif %}\
{% if 3 == deferred %}\
{% set ns.found = true %}\
{% endif %}\
{% endfor %}
{{ ns.found }}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{%- set ns = namespace(found=false) %}
{%- for item in [1, 2, 3] %}
{%- if item == deferred %}{% set ns.found = true %}{% endif %}
{%- endfor %}
{{ ns.found }}
Loading