1
0
mirror of https://github.com/danog/gojekyll.git synced 2025-01-23 00:01:14 +01:00

More filters

This commit is contained in:
Oliver Steele 2017-06-30 22:38:27 -04:00
parent 8ed24f0eb7
commit 899875e6f5
3 changed files with 61 additions and 10 deletions

View File

@ -32,8 +32,7 @@ This is currently 5-30x faster than Jekyll, depending on the use of `{% highligh
- [ ] Customization
- [x] Templates
- [ ] Jekyll filters
- [ ] array filters: `group_by_exp` `sample` `pop` `shift`
- [ ] string filters: `cgi_escape` `uri_escape` `scssify` `smartify` `slugify` `normalize_whitespace`
- [ ] `group_by_exp` `pop` `shift` `cgi_escape` `uri_escape` `scssify` `smartify`
- [x] everything else
- [ ] Jekyll tags
- [x] `include`

View File

@ -5,6 +5,7 @@ import (
"encoding/json"
"encoding/xml"
"fmt"
"math/rand"
"reflect"
"regexp"
"strings"
@ -31,6 +32,13 @@ func AddJekyllFilters(engine liquid.Engine, config config.Config) {
return out
})
engine.DefineFilter("group_by", groupByFilter)
engine.DefineFilter("group_by_exp", unimplementedFilter("group_by_exp"))
engine.DefineFilter("sample", func(array []interface{}) interface{} {
if len(array) == 0 {
return nil
}
return array[rand.Intn(len(array))]
})
// sort overrides the Liquid filter with one that takes parameters
engine.DefineFilter("sort", sortFilter)
engine.DefineFilter("where", whereFilter) // TODO test case
@ -40,6 +48,8 @@ func AddJekyllFilters(engine liquid.Engine, config config.Config) {
engine.DefineFilter("push", func(array []interface{}, item interface{}) interface{} {
return append(array, generics.MustConvertItem(item, array))
})
engine.DefineFilter("pop", unimplementedFilter("pop"))
engine.DefineFilter("shift", unimplementedFilter("shift"))
engine.DefineFilter("unshift", func(array []interface{}, item interface{}) interface{} {
return append([]interface{}{generics.MustConvertItem(item, array)}, array...)
})
@ -71,10 +81,25 @@ func AddJekyllFilters(engine liquid.Engine, config config.Config) {
})
engine.DefineFilter("jsonify", json.Marshal)
engine.DefineFilter("markdownify", blackfriday.MarkdownCommon)
// engine.DefineFilter("normalize_whitespace", func(s string) string {
// wsPattern := regexp.MustCompile(`(?s:[\s\n]+)`)
// return wsPattern.ReplaceAllString(s, " ")
// })
engine.DefineFilter("normalize_whitespace", func(s string) string {
// s = strings.Replace(s, "n", "N", -1)
wsPattern := regexp.MustCompile(`(?s:[\s\n]+)`)
return wsPattern.ReplaceAllString(s, " ")
})
engine.DefineFilter("slugify", func(s, mode string) string {
if mode == "" {
mode = "default"
}
p := map[string]string{
"raw": `\s+`,
"default": `[^[:alnum:]]+`,
"pretty": `[^[:alnum:]\._~!$&'()+,;=@]+`,
}[mode]
if p != "" {
s = regexp.MustCompile(p).ReplaceAllString(s, "-")
}
return strings.ToLower(s)
})
engine.DefineFilter("to_integer", func(n int) int { return n })
engine.DefineFilter("number_of_words", func(s string) int {
wordPattern := regexp.MustCompile(`\w+`) // TODO what's the Jekyll spec for a word?
@ -94,6 +119,10 @@ func AddJekyllFilters(engine liquid.Engine, config config.Config) {
// }
// return strings.Join(parts, "?")
// })
engine.DefineFilter("cgi_escape", unimplementedFilter("cgi_escape"))
engine.DefineFilter("uri_escape", unimplementedFilter("uri_escape"))
engine.DefineFilter("scssify", unimplementedFilter("scssify"))
engine.DefineFilter("smartify", unimplementedFilter("smartify"))
engine.DefineFilter("xml_escape", func(s string) string {
// TODO can't handle maps
// eval https://github.com/clbanning/mxj
@ -106,6 +135,17 @@ func AddJekyllFilters(engine liquid.Engine, config config.Config) {
})
}
func unimplementedFilter(name string) func(value interface{}) interface{} {
warned := false
return func(value interface{}) interface{} {
if !warned {
fmt.Println("warning: unimplemented filter:", name)
warned = true
}
return value
}
}
func arrayToSentenceStringFilter(array []string, conjunction interface{}) string {
conj, ok := conjunction.(string)
if !ok {

View File

@ -2,6 +2,7 @@ package filters
import (
"fmt"
"math/rand"
"strings"
"testing"
"time"
@ -22,7 +23,10 @@ var filterTests = []struct{ in, expected string }{
// arrays
// TODO group_by group_by_exp sample pop shift
// pop and shift are challenging because they require lvalues
{`{{ ar | array_to_sentence_string }}`, "first, second, and third"},
{`{{ array | array_to_sentence_string }}`, "first, second, and third"},
{`{{ array | sample }}`, "third"},
{`{{ site.members | group_by: "graduation_year" | map: "name" | sort | join }}`, "2013, 2014, 2015"},
// TODO what is the default for nil first?
{`{{ animals | sort | join: ", " }}`, "Sally Snake, giraffe, octopus, zebra"},
@ -34,7 +38,6 @@ var filterTests = []struct{ in, expected string }{
{`{{ site.members | where_exp: "item", "item.graduation_year == 2014" | map: "name" }}`, "Alan"},
{`{{ site.members | where_exp: "item", "item.graduation_year < 2014" | map: "name" }}`, "Alonzo"},
{`{{ site.members | where_exp: "item", "item.name contains 'Al'" | map: "name" | join }}`, "Alonzo, Alan"},
{`{{ site.members | group_by: "graduation_year" | map: "name" | sort | join }}`, "2013, 2014, 2015"},
{`{{ page.tags | push: 'Spokane' | join }}`, "Seattle, Tacoma, Spokane"},
// {`{{ page.tags | pop }}`, "Seattle"},
@ -49,18 +52,25 @@ var filterTests = []struct{ in, expected string }{
{`{{ obj | jsonify }}`, `{"a":[1,2,3,4]}`},
{`{{ site.pages | map: "name" | join }}`, "a, b, c, d"},
{`{{ site.pages | filter: "weight" | map: "name" | join }}`, "a, c, d"},
// {"{{ \"a \n b\" | normalize_whitespace }}", "a b"},
{"{{ ws | normalize_whitespace }}", "a b c"},
{`{{ "123" | to_integer | type }}`, "int"},
{`{{ false | to_integer }}`, "0"},
{`{{ true | to_integer }}`, "1"},
{`{{ "here are some words" | number_of_words}}`, "4"},
{`{{ "The _config.yml file" | slugify }}`, "the-config-yml-file"},
{`{{ "The _config.yml file" | slugify: 'none' }}`, "the _config.yml file"},
{`{{ "The _config.yml file" | slugify: 'raw' }}`, "the-_config.yml-file"},
{`{{ "The _config.yml file" | slugify: 'default' }}`, "the-config-yml-file"},
{`{{ "The _config.yml file" | slugify: 'pretty' }}`, "the-_config.yml-file"},
{`{{ "1 < 2 & 3" | xml_escape }}`, "1 &lt; 2 &amp; 3"},
// {`{{ "http://foo.com/?q=foo, \bar?" | uri_escape }}`, "http://foo.com/?q=foo,%20%5Cbar?"},
}
var filterTestScope = map[string]interface{}{
"animals": []string{"zebra", "octopus", "giraffe", "Sally Snake"},
"ar": []string{"first", "second", "third"},
"array": []string{"first", "second", "third"},
"obj": map[string]interface{}{
"a": []int{1, 2, 3, 4},
},
@ -82,6 +92,7 @@ var filterTestScope = map[string]interface{}{
},
},
"time": timeMustParse("2008-11-07T13:07:54Z"),
"ws": "a b\n\t c",
}
func timeMustParse(s string) time.Time {
@ -93,6 +104,7 @@ func timeMustParse(s string) time.Time {
}
func TestFilters(t *testing.T) {
rand.Seed(1)
for i, test := range filterTests {
t.Run(fmt.Sprintf("%02d", i+1), func(t *testing.T) {
requireTemplateRender(t, test.in, filterTestScope, test.expected)