mirror of
https://github.com/danog/gojekyll.git
synced 2025-01-23 00:01:14 +01:00
More filters
This commit is contained in:
parent
8ed24f0eb7
commit
899875e6f5
@ -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`
|
||||
|
@ -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 {
|
||||
|
@ -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 < 2 & 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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user