diff --git a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java index fbe2766b8..0107700fd 100644 --- a/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java +++ b/src/main/java/com/hubspot/jinjava/lib/filter/SelectAttrFilter.java @@ -4,6 +4,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.concurrent.ThreadLocalRandom; import com.hubspot.jinjava.doc.annotations.JinjavaDoc; import com.hubspot.jinjava.doc.annotations.JinjavaParam; @@ -13,7 +14,6 @@ import com.hubspot.jinjava.lib.exptest.ExpTest; import com.hubspot.jinjava.util.ForLoop; import com.hubspot.jinjava.util.ObjectIterator; -import com.hubspot.jinjava.util.Variable; @JinjavaDoc( value = "Filters a sequence of objects by applying a test to an attribute of an object and only selecting the ones with the test succeeding.", @@ -77,7 +77,22 @@ protected Object applyFilter(Object var, JinjavaInterpreter interpreter, Object[ while (loop.hasNext()) { Object val = loop.next(); - Object attrVal = new Variable(interpreter, String.format("%s.%s", "placeholder", attr)).resolve(val); + // push temporary variable to be resolved + String tempValue = generateTempVariable(); + String expression = generateTempVariable(tempValue, attr); + + // ensure this random value hasn't been seen before + while (interpreter.getContext().containsKey(tempValue) || interpreter.getContext().getResolvedExpressions().contains(expression)) { + tempValue = generateTempVariable(); + } + + interpreter.getContext().put(tempValue, val); + + Object attrVal = interpreter.resolveELExpression(expression, interpreter.getLineNumber()); + + // cleanup + interpreter.getContext().remove(tempValue); + if (acceptObjects == expTest.evaluate(attrVal, interpreter, expArgs)) { result.add(val); } @@ -85,4 +100,13 @@ protected Object applyFilter(Object var, JinjavaInterpreter interpreter, Object[ return result; } + + private String generateTempVariable() { + return "jj_temp_" + Math.abs(ThreadLocalRandom.current().nextInt()); + } + + private String generateTempVariable(String tempValue, String expression) { + return String.format("%s.%s", tempValue, expression).trim(); + } + } diff --git a/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java b/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java index a8727c242..46bc9d067 100644 --- a/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java +++ b/src/test/java/com/hubspot/jinjava/lib/filter/SelectAttrFilterTest.java @@ -3,10 +3,12 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.HashMap; +import java.util.List; import org.junit.Before; import org.junit.Test; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.hubspot.jinjava.Jinjava; @@ -18,9 +20,9 @@ public class SelectAttrFilterTest { public void setup() { jinjava = new Jinjava(); jinjava.getGlobalContext().put("users", Lists.newArrayList( - new User(0, false, "foo@bar.com", new Option(0, "option0")), - new User(1, true, "bar@bar.com", new Option(1, "option1")), - new User(2, false, null, new Option(2, "option2")))); + new User(0, false, "foo@bar.com", new Option(0, "option0"), ImmutableList.of(new Option(0, "option0"))), + new User(1, true, "bar@bar.com", new Option(1, "option1"), ImmutableList.of(new Option(1, "option1"))), + new User(2, false, null, new Option(2, "option2"), ImmutableList.of(new Option(2, "option2"))))); } @Test @@ -56,18 +58,30 @@ public void selectAttrWithNestedProperty() { .isEqualTo("[2]"); } + @Test + public void selectAttrWithNestedFilter() { + + assertThat(jinjava.render("{{ users|selectattr(\"optionList|map('id')\", 'containing', 1) }}", new HashMap())) + .isEqualTo("[1]"); + + assertThat(jinjava.render("{{ users|selectattr(\"optionList|map('name')\", 'containing', 'option2') }}", new HashMap())) + .isEqualTo("[2]"); + } + public static class User { private long num; private boolean isActive; private String email; private Option option; + private List