Repository: PebbleTemplates/pebble
Pull request #283 (2017-07-04 15:10:31)

Handle null values passed to an include statement
Previously a null value was interpreted as 'not present in this context' and the parent context was queried. If the parent had a non-null value then the wrong value was used in the included template.
This PR provides a test case and the fix.


Patch:
diff --git a/src/main/java/com/mitchellbosecke/pebble/template/ScopeChain.java b/src/main/java/com/mitchellbosecke/pebble/template/ScopeChain.java
index 3e84ecee..4c6afba6 100644
--- a/src/main/java/com/mitchellbosecke/pebble/template/ScopeChain.java
+++ b/src/main/java/com/mitchellbosecke/pebble/template/ScopeChain.java
@@ -113,34 +113,34 @@ public class ScopeChain {
      * @return The value of the variable
      */
     public Object get(String key) {
-        Object result;
-
         /*
          * The majority of time, the requested variable will be in the first
          * scope so we do a quick lookup in that scope before attempting to
          * create an iterator, etc. This is solely for performance.
+         * null values must not be handled as "not present".
          */
         Scope scope = stack.getFirst();
-        result = scope.get(key);
-
-        if (result == null) {
+        if (scope.containsKey(key)) {
+            return scope.get(key);
+        }
 
-            Iterator<Scope> iterator = stack.iterator();
+        Iterator<Scope> iterator = stack.iterator();
+        // account for the first lookup we did
+        iterator.next();
 
-            // account for the first lookup we did
-            iterator.next();
+        while (iterator.hasNext()) {
+            scope = iterator.next();
 
-            while (result == null && iterator.hasNext()) {
-                scope = iterator.next();
+            if (scope.containsKey(key)) {
+                return scope.get(key);
+            }
 
-                result = scope.get(key);
-                if (scope.isLocal()) {
-                    break;
-                }
+            if (scope.isLocal()) {
+                return null;
             }
         }
 
-        return result;
+        return null;
     }
 
     /**


Test patch:
diff --git a/src/test/java/com/mitchellbosecke/pebble/CoreTagsTest.java b/src/test/java/com/mitchellbosecke/pebble/CoreTagsTest.java
index 2cc6e636..08a54d1f 100755
--- a/src/test/java/com/mitchellbosecke/pebble/CoreTagsTest.java
+++ b/src/test/java/com/mitchellbosecke/pebble/CoreTagsTest.java
@@ -863,6 +863,22 @@ public class CoreTagsTest extends AbstractTest {
         assertEquals("TWO" + LINE_SEPARATOR + "ONE" + LINE_SEPARATOR + "TWO" + LINE_SEPARATOR, writer.toString());
     }
 
+    /**
+     * Ensures that an include with a variable override works even if a null value is passed.
+     *
+     * @throws PebbleException
+     * @throws IOException
+     */
+    @Test
+    public void testIncludeOverridesVariable() throws PebbleException, IOException {
+        PebbleEngine pebble = new PebbleEngine.Builder().strictVariables(false).build();
+        PebbleTemplate template = pebble.getTemplate("templates/template.includeOverrideVariable1.peb");
+
+        Writer writer = new StringWriter();
+        template.evaluate(writer);
+        assertEquals("One: one (overridden)" + LINE_SEPARATOR + "Two: ", writer.toString());
+    }
+
     @Test
     public void testSet() throws PebbleException, IOException {
         PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
diff --git a/src/test/resources/templates/template.includeOverrideVariable1.peb b/src/test/resources/templates/template.includeOverrideVariable1.peb
new file mode 100644
index 00000000..e35821b6
--- /dev/null
+++ b/src/test/resources/templates/template.includeOverrideVariable1.peb
@@ -0,0 +1,4 @@
+{# used by CoreTagsTest.testIncludeOverridesVariable #}
+{% set one = 'one (base)' %}
+{% set two = 'two (base)' %}
+{% include "templates/template.includeOverrideVariable2.peb" with {"one": "one (overridden)", "two": null} %}
diff --git a/src/test/resources/templates/template.includeOverrideVariable2.peb b/src/test/resources/templates/template.includeOverrideVariable2.peb
new file mode 100644
index 00000000..9495d96c
--- /dev/null
+++ b/src/test/resources/templates/template.includeOverrideVariable2.peb
@@ -0,0 +1,4 @@
+{# used by CoreTagsTest.testIncludeOverridesVariable #}
+One: {{ one }}
+
+Two: {{ two }}
\ No newline at end of file