Repository: PebbleTemplates/pebble
Pull request #424 (2019-01-16 03:33:49)

Add split filter (#421)


Patch:
diff --git a/docs/src/orchid/resources/changelog/v3_0_8.md b/docs/src/orchid/resources/changelog/v3_0_8.md
new file mode 100644
index 00000000..69cbf0b0
--- /dev/null
+++ b/docs/src/orchid/resources/changelog/v3_0_8.md
@@ -0,0 +1,5 @@
+---
+version: '3.0.8'
+---
+
+- Add split filter (#421) 
diff --git a/docs/src/orchid/resources/data/twig-compatibility/filters.yml b/docs/src/orchid/resources/data/twig-compatibility/filters.yml
index 740ebd0d..31662133 100644
--- a/docs/src/orchid/resources/data/twig-compatibility/filters.yml
+++ b/docs/src/orchid/resources/data/twig-compatibility/filters.yml
@@ -93,7 +93,8 @@
   page: 'sort'
 - name: 'split'
   twigLink: 'https://twig.symfony.com/doc/2.x/filters/split.html'
-  support: 'none'
+  support: 'full'
+  page: 'split'
 - name: 'striptags'
   twigLink: 'https://twig.symfony.com/doc/2.x/filters/striptags.html'
   support: 'none'
diff --git a/docs/src/orchid/resources/wiki/filter/split.md b/docs/src/orchid/resources/wiki/filter/split.md
new file mode 100644
index 00000000..2055f311
--- /dev/null
+++ b/docs/src/orchid/resources/wiki/filter/split.md
@@ -0,0 +1,20 @@
+# `split`
+The `split` filter splits a string by the given delimiter and returns a list of strings.
+```twig
+{% set foo = "one,two,three" | split(',') %}
+{# foo contains ['one', 'two', 'three'] #}
+```
+
+You can also pass a limit argument:
+- If `limit` is positive, then the pattern will be applied at most n - 1 times, the array's length will be no greater than n, and the array's last entry will contain all input beyond the last matched delimiter;
+- If `limit` is negative, then the pattern will be applied as many times as possible and the array can have any length;
+- If `limit` is zero, then the pattern will be applied as many times as possible, the array can have any length, and trailing empty strings will be discarded;
+
+```twig
+{% set foo = "one,two,three,four,five" | split(',', 3) %}
+{# foo contains ['one', 'two', 'three,four,five'] #}
+```
+
+## Arguments
+- delimiter: The delimiter
+- limit: The limit argument
\ No newline at end of file
diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java b/pebble/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java
index bf089cd6..2e620500 100644
--- a/pebble/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java
+++ b/pebble/src/main/java/com/mitchellbosecke/pebble/extension/core/CoreExtension.java
@@ -54,7 +54,6 @@ import com.mitchellbosecke.pebble.tokenParser.MacroTokenParser;
 import com.mitchellbosecke.pebble.tokenParser.ParallelTokenParser;
 import com.mitchellbosecke.pebble.tokenParser.SetTokenParser;
 import com.mitchellbosecke.pebble.tokenParser.TokenParser;
-
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -143,6 +142,7 @@ public class CoreExtension extends AbstractExtension {
     filters.put("length", new LengthFilter());
     filters.put(ReplaceFilter.FILTER_NAME, new ReplaceFilter());
     filters.put(MergeFilter.FILTER_NAME, new MergeFilter());
+    filters.put(SplitFilter.FILTER_NAME, new SplitFilter());
     return filters;
   }
 
diff --git a/pebble/src/main/java/com/mitchellbosecke/pebble/extension/core/SplitFilter.java b/pebble/src/main/java/com/mitchellbosecke/pebble/extension/core/SplitFilter.java
new file mode 100644
index 00000000..4f45bbfd
--- /dev/null
+++ b/pebble/src/main/java/com/mitchellbosecke/pebble/extension/core/SplitFilter.java
@@ -0,0 +1,48 @@
+package com.mitchellbosecke.pebble.extension.core;
+
+import com.mitchellbosecke.pebble.error.PebbleException;
+import com.mitchellbosecke.pebble.extension.Filter;
+import com.mitchellbosecke.pebble.template.EvaluationContext;
+import com.mitchellbosecke.pebble.template.PebbleTemplate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class SplitFilter implements Filter {
+
+  public static final String FILTER_NAME = "split";
+  private static final String ARGUMENT_NAME_DELIMITER = "delimiter";
+  private static final String ARGUMENT_NAME_LIMIT = "limit";
+
+  private final List<String> argumentNames = new ArrayList<>();
+
+  public SplitFilter() {
+    this.argumentNames.add(ARGUMENT_NAME_DELIMITER);
+    this.argumentNames.add(ARGUMENT_NAME_LIMIT);
+  }
+
+  @Override
+  public Object apply(Object input, Map<String, Object> args, PebbleTemplate self,
+      EvaluationContext context, int lineNumber) throws PebbleException {
+    if (input == null) {
+      return null;
+    }
+
+    String delimiter = (String) args.get(ARGUMENT_NAME_DELIMITER);
+    Number limit = (Number) args.get(ARGUMENT_NAME_LIMIT);
+    if (delimiter == null) {
+      throw new PebbleException(null, "missing delimiter parameter in split filter", lineNumber,
+          self.getName());
+    }
+
+    if (limit == null) {
+      return ((String) input).split(delimiter);
+    }
+    return ((String) input).split(delimiter, limit.intValue());
+  }
+
+  @Override
+  public List<String> getArgumentNames() {
+    return this.argumentNames;
+  }
+}


Test patch:
diff --git a/pebble/src/test/java/com/mitchellbosecke/pebble/SplitFilterTest.java b/pebble/src/test/java/com/mitchellbosecke/pebble/SplitFilterTest.java
new file mode 100644
index 00000000..12ebeb7f
--- /dev/null
+++ b/pebble/src/test/java/com/mitchellbosecke/pebble/SplitFilterTest.java
@@ -0,0 +1,120 @@
+package com.mitchellbosecke.pebble;
+
+import static org.junit.Assert.assertEquals;
+
+import com.mitchellbosecke.pebble.error.PebbleException;
+import com.mitchellbosecke.pebble.loader.StringLoader;
+import com.mitchellbosecke.pebble.template.PebbleTemplate;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.Test;
+
+public class SplitFilterTest {
+
+  @Test
+  public void whenSplit_givenInputNull_thenReturnNull() throws IOException {
+    PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
+        .strictVariables(false).build();
+
+    PebbleTemplate template = pebble.getTemplate("{% set foo = null | split(',') %}\n"
+        + "{{ foo }}");
+
+    Map<String, Object> context = new HashMap<>();
+
+    Writer writer = new StringWriter();
+    template.evaluate(writer, context);
+
+    assertEquals("", writer.toString());
+  }
+
+  @Test(expected = PebbleException.class)
+  public void whenSplit_givenNoDelimiter_thenThrowPebbleException() throws IOException {
+    PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
+        .strictVariables(false).build();
+
+    PebbleTemplate template = pebble.getTemplate("{{ \"one,two,three\" | split }}");
+
+    Map<String, Object> context = new HashMap<>();
+
+    Writer writer = new StringWriter();
+    template.evaluate(writer, context);
+  }
+
+  @Test
+  public void whenSplit_givenInputWithDelimiter_thenSplit() throws IOException {
+    PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
+        .strictVariables(false).build();
+
+    PebbleTemplate template = pebble.getTemplate("{% set foo = \"one,two,three\" | split(',') %}"
+        + "{% for var in foo %}"
+        + "{{ var }}"
+        + "{% endfor %}");
+
+    Map<String, Object> context = new HashMap<>();
+
+    Writer writer = new StringWriter();
+    template.evaluate(writer, context);
+
+    assertEquals("onetwothree", writer.toString());
+  }
+
+  @Test
+  public void whenSplit_givenInputWithDelimiterAndPositiveLimit_thenSplit() throws IOException {
+    PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
+        .strictVariables(false).build();
+
+    PebbleTemplate template = pebble
+        .getTemplate("{% set foo = \"one,two,three,four,five\" | split(',',3) %}"
+            + "{% for var in foo %}"
+            + "{{ var }}"
+            + "{% endfor %}");
+
+    Map<String, Object> context = new HashMap<>();
+
+    Writer writer = new StringWriter();
+    template.evaluate(writer, context);
+
+    assertEquals("onetwothree,four,five", writer.toString());
+  }
+
+  @Test
+  public void whenSplit_givenInputWithDelimiterAndNegativeLimit_thenSplit() throws IOException {
+    PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
+        .strictVariables(false).build();
+
+    PebbleTemplate template = pebble
+        .getTemplate("{% set foo = \"one,two,three,four,five\" | split(',',-1) %}"
+            + "{% for var in foo %}"
+            + "{{ var }}"
+            + "{% endfor %}");
+
+    Map<String, Object> context = new HashMap<>();
+
+    Writer writer = new StringWriter();
+    template.evaluate(writer, context);
+
+    assertEquals("onetwothreefourfive", writer.toString());
+  }
+
+  @Test
+  public void whenSplit_givenInputWithDelimiterAndZeroLimit_thenSplit() throws IOException {
+    PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader())
+        .strictVariables(false).build();
+
+    PebbleTemplate template = pebble
+        .getTemplate("{% set foo = \"one,two,three,four,five\" | split(',',0) %}"
+            + "{% for var in foo %}"
+            + "{{ var }}"
+            + "{% endfor %}");
+
+    Map<String, Object> context = new HashMap<>();
+
+    Writer writer = new StringWriter();
+    template.evaluate(writer, context);
+
+    assertEquals("onetwothreefourfive", writer.toString());
+  }
+}