1
0
mirror of https://github.com/danog/gojekyll.git synced 2024-11-27 03:24:40 +01:00

Switch from acstech/liquid -> osteele/liquid

This commit is contained in:
Oliver Steele 2017-06-28 11:25:58 -04:00
parent 60ac92497e
commit c41a7b975c
7 changed files with 97 additions and 214 deletions

View File

@ -30,3 +30,6 @@ type RemoteEngine interface {
// IncludeTagHandler resolves the filename in a Liquid include tag into the expanded content // IncludeTagHandler resolves the filename in a Liquid include tag into the expanded content
// of the included file. // of the included file.
type IncludeTagHandler func(string, io.Writer, map[string]interface{}) error type IncludeTagHandler func(string, io.Writer, map[string]interface{}) error
// A LinkTagHandler given an include tag file name returns a URL.
type LinkTagHandler func(string) (string, bool)

View File

@ -1,51 +0,0 @@
package liquid
import (
"fmt"
"io"
"strings"
"github.com/acstech/liquid"
"github.com/acstech/liquid/core"
)
func init() {
liquid.Tags["link"] = linkFactory
}
// A LinkTagHandler given an include tag file name returns a URL.
type LinkTagHandler func(string) (string, bool)
var currentLinkHandler LinkTagHandler
// SetLinkHandler sets the function that resolves an include tag file name to a URL.
func SetLinkHandler(h LinkTagHandler) {
currentLinkHandler = h
}
// Link tag data, for passing information from the factory to Execute
type linkData struct {
standaloneTag
filename string
}
// linkFactory creates a link tag
func linkFactory(p *core.Parser, config *core.Configuration) (core.Tag, error) {
start := p.Position
p.SkipPastTag()
end := p.Position - 2
filename := strings.TrimSpace(string(p.Data[start:end]))
return &linkData{standaloneTag{"link"}, filename}, nil
}
// Execute is required by the Liquid tag interface
func (l *linkData) Execute(writer io.Writer, data map[string]interface{}) core.ExecuteState {
url, ok := currentLinkHandler(l.filename)
if !ok {
panic(fmt.Errorf("link tag: %s not found", l.filename))
}
if _, err := writer.Write([]byte(url)); err != nil {
panic(err)
}
return core.Normal
}

View File

@ -3,23 +3,15 @@ package liquid
import ( import (
"encoding/json" "encoding/json"
"encoding/xml" "encoding/xml"
"fmt"
"reflect" "reflect"
"strings" "strings"
"time" "time"
"github.com/acstech/liquid/core" "github.com/osteele/liquid/expressions"
) )
func init() { func (e *LocalWrapperEngine) addJekyllFilters() {
core.RegisterFilter("array_to_sentence_string", arrayToSentenceStringFactory) e.engine.DefineFilter("array_to_sentence_string", arrayToSentenceStringFilter)
core.RegisterFilter("date_to_rfc822", dateToRFC822Factory)
core.RegisterFilter("jsonify", jsonifyFactory)
core.RegisterFilter("xml_escape", xmlEscapeFactory)
core.RegisterFilter("where_exp", whereExpFactory)
}
func AddStandardFilters(e *LocalWrapperEngine) {
e.engine.DefineFilter("date_to_rfc822", func(date time.Time) interface{} { e.engine.DefineFilter("date_to_rfc822", func(date time.Time) interface{} {
return date.Format(time.RFC822) return date.Format(time.RFC822)
// Out: Mon, 07 Nov 2008 13:07:54 -0800 // Out: Mon, 07 Nov 2008 13:07:54 -0800
@ -28,111 +20,81 @@ func AddStandardFilters(e *LocalWrapperEngine) {
return date.Format("02 Jan 2005") return date.Format("02 Jan 2005")
// Out: 07 Nov 2008 // Out: 07 Nov 2008
}) })
} e.engine.DefineFilter("jsonify", func(value interface{}) interface{} {
s, err := json.Marshal(value)
func arrayToSentenceStringFactory(parameters []core.Value) core.Filter {
conj := "and "
if len(parameters) > 0 {
conj = fmt.Sprint(parameters[0]) + " "
}
return func(input interface{}, data map[string]interface{}) interface{} {
rt := reflect.ValueOf(input)
switch rt.Kind() {
case reflect.Array, reflect.Slice:
ar := make([]string, rt.Len())
for i := 0; i < rt.Len(); i++ {
ar[i] = fmt.Sprint(rt.Index(i))
if i == rt.Len()-1 {
ar[i] = conj + ar[i]
}
}
return strings.Join(ar, ", ")
}
return nil
}
}
func dateToRFC822Factory(parameters []core.Value) core.Filter {
if len(parameters) != 0 {
panic("The date_to_rfc822 filter doesn't accept parameters")
}
return func(input interface{}, data map[string]interface{}) interface{} {
date := input.(time.Time) // TODO if a string, try parsing it
return date.Format(time.RFC822)
}
}
func jsonifyFactory(parameters []core.Value) core.Filter {
if len(parameters) != 0 {
panic("The jsonify filter doesn't accept parameters")
}
return func(input interface{}, data map[string]interface{}) interface{} {
s, err := json.Marshal(input)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return s return s
} })
e.engine.DefineFilter("where_exp", whereExpFilter)
e.engine.DefineFilter("xml_escape", xml.Marshal)
} }
func xmlEscapeFactory(parameters []core.Value) core.Filter { func arrayToSentenceStringFilter(value []string, conjunction interface{}) interface{} {
if len(parameters) != 0 { conj, ok := conjunction.(string)
panic("The xml_escape filter doesn't accept parameters") if !ok {
conj = "and "
} }
return func(input interface{}, data map[string]interface{}) interface{} { rt := reflect.ValueOf(value)
s, err := xml.Marshal(input) ar := make([]string, rt.Len())
// TODO can't handle maps for i, v := range value {
// eval https://github.com/clbanning/mxj ar[i] = v
// adapt https://stackoverflow.com/questions/30928770/marshall-map-to-xml-in-go if i == rt.Len()-1 {
if err != nil { ar[i] = conj + v
panic(err)
} }
return s
} }
return strings.Join(ar, ", ")
} }
// WhereExpFactory implements the Jekyll `where_exp` filter // func xmlEscapeFilter(value interface{}) interface{} {
func whereExpFactory(parameters []core.Value) core.Filter { // data, err := xml.Marshal(value)
if len(parameters) != 2 { // // TODO can't handle maps
panic("The were_exp filter requires two parameters") // // eval https://github.com/clbanning/mxj
} // // adapt https://stackoverflow.com/questions/30928770/marshall-map-to-xml-in-go
return (&whereExpFilter{parameters[0], parameters[1]}).run // if err != nil {
} // panic(err)
// }
// return data
// }
type whereExpFilter struct { func whereExpFilter(in []interface{}, name string, expr expressions.Closure) (interface{}, error) {
varName core.Value rt := reflect.ValueOf(in)
expr core.Value
}
func (f *whereExpFilter) run(input interface{}, data map[string]interface{}) interface{} {
rt := reflect.ValueOf(input)
switch rt.Kind() { switch rt.Kind() {
case reflect.Slice: case reflect.Array, reflect.Slice:
case reflect.Array:
default: default:
return input return in, nil
}
varName := f.varName.Resolve(data).(string)
expr := f.expr.Resolve(data).(string)
p := core.NewParser([]byte(expr + "%}"))
condition, err := p.ReadConditionGroup()
// TODO assert we're at the end of the string
if err != nil {
panic(err)
}
result := []interface{}{}
d := make(map[string]interface{})
for k, v := range data {
d[k] = v
} }
out := []interface{}{}
for i := 0; i < rt.Len(); i++ { for i := 0; i < rt.Len(); i++ {
item := rt.Index(i).Interface() item := rt.Index(i).Interface()
d[varName] = item value, err := expr.Bind(name, item).Evaluate()
if condition.IsTrue(d) { if err != nil {
result = append(result, item) return nil, err
}
if value != nil && value != false {
out = append(out, item)
} }
} }
return result return out, nil
}
func whereFilter(in []interface{}, key string, value interface{}) interface{} {
rt := reflect.ValueOf(in)
switch rt.Kind() {
case reflect.Array, reflect.Slice:
default:
return in
}
out := []interface{}{}
for i := 0; i < rt.Len(); i++ {
item := rt.Index(i)
if item.Kind() == reflect.Map && item.Type().Key().Kind() == reflect.String {
attr := item.MapIndex(reflect.ValueOf(key))
if attr != reflect.Zero(attr.Type()) && (value == nil || attr.Interface() == value) {
out = append(out, item.Interface())
}
}
}
return out
} }

View File

@ -1,12 +1,10 @@
package liquid package liquid
import ( import (
"bytes"
"strings" "strings"
"testing" "testing"
"time" "time"
"github.com/acstech/liquid"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -38,12 +36,11 @@ func TestFilters(t *testing.T) {
} }
} }
func requireTemplateRender(t *testing.T, tmpl string, data map[string]interface{}, expected string) { func requireTemplateRender(t *testing.T, tmpl string, scope map[string]interface{}, expected string) {
template, err := liquid.ParseString(tmpl, nil) engine := NewLocalWrapperEngine()
data, err := engine.ParseAndRender([]byte(tmpl), scope)
require.NoError(t, err) require.NoError(t, err)
writer := new(bytes.Buffer) require.Equal(t, expected, strings.TrimSpace(string(data)))
template.Render(writer, data)
require.Equal(t, expected, strings.TrimSpace(writer.String()))
} }
// func TestXMLEscapeFilter(t *testing.T) { // func TestXMLEscapeFilter(t *testing.T) {

View File

@ -1,11 +1,7 @@
package liquid package liquid
import ( import (
"fmt"
"io"
"github.com/osteele/liquid" "github.com/osteele/liquid"
"github.com/osteele/liquid/chunks"
) )
// LocalWrapperEngine is a wrapper around osteele/liquid. // LocalWrapperEngine is a wrapper around osteele/liquid.
@ -18,22 +14,8 @@ type LocalWrapperEngine struct {
// NewLocalWrapperEngine creates a LocalEngine. // NewLocalWrapperEngine creates a LocalEngine.
func NewLocalWrapperEngine() LocalEngine { func NewLocalWrapperEngine() LocalEngine {
e := &LocalWrapperEngine{engine: liquid.NewEngine()} e := &LocalWrapperEngine{engine: liquid.NewEngine()}
AddStandardFilters(e) e.addJekyllFilters()
e.engine.DefineTag("link", func(filename string) (func(io.Writer, chunks.Context) error, error) { e.addJekyllTags()
return func(w io.Writer, _ chunks.Context) error {
url, found := e.linkHandler(filename)
if !found {
return fmt.Errorf("missing link filename: %s", filename)
}
_, err := w.Write([]byte(url))
return err
}, nil
})
e.engine.DefineTag("include", func(filename string) (func(io.Writer, chunks.Context) error, error) {
return func(w io.Writer, ctx chunks.Context) error {
return e.includeTagHandler(filename, w, ctx.GetVariableMap())
}, nil
})
return e return e
} }
@ -49,10 +31,7 @@ func (e *LocalWrapperEngine) IncludeHandler(h IncludeTagHandler) {
// Parse is a wrapper for liquid.Parse. // Parse is a wrapper for liquid.Parse.
func (e *LocalWrapperEngine) Parse(source []byte) (Template, error) { func (e *LocalWrapperEngine) Parse(source []byte) (Template, error) {
// fmt.Println("parse", string(source)) return e.engine.ParseTemplate(source)
t, err := e.engine.ParseTemplate(source)
// return &localTemplate{t}, err
return t, err
} }
// ParseAndRender parses and then renders the template. // ParseAndRender parses and then renders the template.
@ -61,6 +40,5 @@ func (e *LocalWrapperEngine) ParseAndRender(source []byte, scope map[string]inte
if err != nil { if err != nil {
return nil, err return nil, err
} }
// fmt.Println("render", t)
return t.Render(scope) return t.Render(scope)
} }

View File

@ -1,32 +0,0 @@
package liquid
import "github.com/acstech/liquid/core"
type standaloneTag struct {
name string
}
// AddCode is required by the Liquid tag interface
func (l *standaloneTag) AddCode(code core.Code) {
panic("AddCode should not have been called on a standalone tag")
}
// AddSibling is required by the Liquid tag interface
func (l *standaloneTag) AddSibling(tag core.Tag) error {
panic("AddSibling should not have been called on a standalone tag")
}
// LastSibling is required by the Liquid tag interface
func (l *standaloneTag) LastSibling() core.Tag {
return nil
}
// Name is required by the Liquid tag interface
func (l *standaloneTag) Name() string {
return l.name
}
// Type is required by the Liquid tag interface
func (l *standaloneTag) Type() core.TagType {
return core.StandaloneTag
}

26
liquid/tags.go Normal file
View File

@ -0,0 +1,26 @@
package liquid
import (
"fmt"
"io"
"github.com/osteele/liquid/chunks"
)
func (e *LocalWrapperEngine) addJekyllTags() {
e.engine.DefineTag("link", func(filename string) (func(io.Writer, chunks.Context) error, error) {
return func(w io.Writer, _ chunks.Context) error {
url, found := e.linkHandler(filename)
if !found {
return fmt.Errorf("missing link filename: %s", filename)
}
_, err := w.Write([]byte(url))
return err
}, nil
})
e.engine.DefineTag("include", func(filename string) (func(io.Writer, chunks.Context) error, error) {
return func(w io.Writer, ctx chunks.Context) error {
return e.includeTagHandler(filename, w, ctx.GetVariableMap())
}, nil
})
}