Skip to content

Commit

Permalink
Move filters to own package
Browse files Browse the repository at this point in the history
  • Loading branch information
osteele committed Jun 27, 2017
1 parent 83503a1 commit 4189f03
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 122 deletions.
2 changes: 1 addition & 1 deletion chunks/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func (ctx Context) RenderASTSequence(w io.Writer, seq []ASTNode) error {
// Render evaluates an AST node and writes the result to an io.Writer.
func (n *ASTControlTag) Render(w io.Writer, ctx Context) error {
cd, ok := FindControlDefinition(n.Tag)
if !ok {
if !ok || cd.action == nil {
return fmt.Errorf("unimplemented tag: %s", n.Tag)
}
f := cd.action(*n)
Expand Down
73 changes: 0 additions & 73 deletions chunks/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,65 +22,6 @@ var renderTests = []struct{ in, expected string }{
{"{{ar[1]}}", "second"},
}

var filterTests = []struct{ in, expected string }{
// Jekyll extensions
{`{{ obj | inspect }}`, `{"a":1}`},

// filters
// {{ product_price | default: 2.99 }}

// list filters
// {{ site.pages | map: 'category' | compact | join "," %}
// {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}{{ my_array.first }}
{`{{"John, Paul, George, Ringo" | split: ", " | join: " and "}}`, "John and Paul and George and Ringo"},
{`{{ animals | sort | join: ", " }}`, "Sally Snake, giraffe, octopus, zebra"},
{`{{ sort_prop | sort: "weight" | inspect }}`, `[{"weight":null},{"weight":1},{"weight":3},{"weight":5}]`},

// last, map, slice, sort_natural, reverse, size, uniq

// string filters
// {{ "/my/fancy/url" | append: ".html" }}
// {% assign filename = "/index.html" %}{{ "website.com" | append: filename }}

// {{ "title" | capitalize }}
// {{ "my great title" | capitalize }}

// {{ "Parker Moore" | downcase }}

// {{ "Have you read 'James & the Giant Peach'?" | escape }}
// {{ "1 < 2 & 3" | escape_once }}
// {{ "1 &lt; 2 &amp; 3" | escape_once }}

// lstrip, newline_to_br, prepend, remove, remove_first, replace, replace_first
// rstrip, split, strip, strip_html, strip_newlines, truncate, truncatewords, upcase
// url_decode, url_encode

// number filters
// {{ -17 | abs }}
// {{ 4 | abs }}
// {{ "-19.86" | abs }}

// {{ 1.2 | ceil }}
// {{ 2.0 | ceil }}
// {{ 183.357 | ceil }}
// {{ "3.5" | ceil }}

// {{ 16 | divided_by: 4 }}
// {{ 5 | divided_by: 3 }}
// {{ 20 | divided_by: 7.0 }}

// {{ 1.2 | floor }}
// {{ 2.0 | floor }}
// {{ 183.357 | floor }}
// minus, modulo, plus, round,times

// date filters
// {{ article.published_at | date: "%a, %b %d, %y" }}
// {{ article.published_at | date: "%Y" }}
// {{ "March 14, 2016" | date: "%b %d, %y" }}
// {{ "now" | date: "%Y-%m-%d %H:%M" }
}

var renderTestContext = Context{map[string]interface{}{
"x": 123,
"obj": map[string]interface{}{
Expand Down Expand Up @@ -135,17 +76,3 @@ func TestRender(t *testing.T) {
})
}
}

func TestFilters(t *testing.T) {
for i, test := range filterTests {
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
tokens := Scan(test.in, "")
ast, err := Parse(tokens)
require.NoErrorf(t, err, test.in)
buf := new(bytes.Buffer)
err = ast.Render(buf, renderTestContext)
require.NoErrorf(t, err, test.in)
require.Equalf(t, test.expected, buf.String(), test.in)
})
}
}
8 changes: 8 additions & 0 deletions engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,16 @@ import (
"io"

"github.com/osteele/liquid/chunks"
"github.com/osteele/liquid/filters"
"github.com/osteele/liquid/tags"
)

// TODO move the filters and tags from globals to the engine
func init() {
tags.DefineStandardTags()
filters.DefineStandardFilters()
}

// Engine parses template source into renderable text.
//
// In the future, it will be configured with additional tags, filters, and the {%include%} search path.
Expand Down
47 changes: 0 additions & 47 deletions expressions/filters.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package expressions

import (
"encoding/json"
"fmt"
"reflect"
"strings"

"github.com/osteele/liquid/errors"
"github.com/osteele/liquid/generics"
Expand All @@ -16,53 +14,8 @@ func (e InterpreterError) Error() string { return string(e) }

type valueFn func(Context) interface{}

func joinFilter(in []interface{}, sep interface{}) interface{} {
a := make([]string, len(in))
s := ", "
if sep != nil {
s = fmt.Sprint(sep)
}
for i, x := range in {
a[i] = fmt.Sprint(x)
}
return strings.Join(a, s)
}

func sortFilter(in []interface{}, key interface{}) []interface{} {
out := make([]interface{}, len(in))
for i, v := range in {
out[i] = v
}
if key == nil {
generics.Sort(out)
} else {
generics.SortByProperty(out, key.(string))
}
return out
}

func splitFilter(in, sep string) interface{} {
return strings.Split(in, sep)
}

var filters = map[string]interface{}{}

func init() {
DefineStandardFilters()
}

func DefineStandardFilters() {
// lists
DefineFilter("join", joinFilter)
DefineFilter("sort", sortFilter)

// strings
DefineFilter("split", splitFilter)

// Jekyll
DefineFilter("inspect", json.Marshal)
}

func DefineFilter(name string, fn interface{}) {
rf := reflect.ValueOf(fn)
switch {
Expand Down
110 changes: 110 additions & 0 deletions filters/filter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package filters

import (
"fmt"
"testing"

"github.com/osteele/liquid/expressions"
"github.com/stretchr/testify/require"
)

func init() {
DefineStandardFilters()
}

var filterTests = []struct{ in, expected string }{
// Jekyll extensions
{`obj | inspect`, `{"a":1}`},

// filters
// product_price | default: 2.99 }}

// list filters
// site.pages | map: 'category' | compact | join "," %}
// {% assign my_array = "apples, oranges, peaches, plums" | split: ", " %}my_array.first }}
{`"John, Paul, George, Ringo" | split: ", " | join: " and "`, "John and Paul and George and Ringo"},
{`animals | sort | join: ", "`, "Sally Snake, giraffe, octopus, zebra"},
{`sort_prop | sort: "weight" | inspect`, `[{"weight":null},{"weight":1},{"weight":3},{"weight":5}]`},

// last, map, slice, sort_natural, reverse, size, uniq

// string filters
// "/my/fancy/url" | append: ".html"
// {% assign filename = "/index.html" %}"website.com" | append: filename

// "title" | capitalize
// "my great title" | capitalize

// "Parker Moore" | downcase

// "Have you read 'James & the Giant Peach'?" | escape
// "1 < 2 & 3" | escape_once
// "1 &lt; 2 &amp; 3" | escape_once

// lstrip, newline_to_br, prepend, remove, remove_first, replace, replace_first
// rstrip, split, strip, strip_html, strip_newlines, truncate, truncatewords, upcase
// url_decode, url_encode

// number filters
// -17 | abs
// 4 | abs
// "-19.86" | abs

// 1.2 | ceil
// 2.0 | ceil
// 183.357 | ceil
// "3.5" | ceil

// 16 | divided_by: 4
// 5 | divided_by: 3
// 20 | divided_by: 7.0

// 1.2 | floor
// 2.0 | floor
// 183.357 | floor
// minus, modulo, plus, round,times

// date filters
// article.published_at | date: "%a, %b %d, %y"
// article.published_at | date: "%Y"
// "March 14, 2016" | date: "%b %d, %y"
// "now" | date: "%Y-%m-%d %H:%M" }
}

var filterTestContext = expressions.NewContext(map[string]interface{}{
"x": 123,
"obj": map[string]interface{}{
"a": 1,
},
"animals": []string{"zebra", "octopus", "giraffe", "Sally Snake"},
"pages": []map[string]interface{}{
{"category": "business"},
{"category": "celebrities"},
{},
{"category": "lifestyle"},
{"category": "sports"},
{},
{"category": "technology"},
},
"sort_prop": []map[string]interface{}{
{"weight": 1},
{"weight": 5},
{"weight": 3},
{"weight": nil},
},
"ar": []string{"first", "second", "third"},
"page": map[string]interface{}{
"title": "Introduction",
},
})

func TestFilters(t *testing.T) {
for i, test := range filterTests {
t.Run(fmt.Sprintf("%02d", i), func(t *testing.T) {
val, err := expressions.EvaluateExpr(test.in, filterTestContext)
require.NoErrorf(t, err, test.in)
actual := fmt.Sprintf("%s", val)
require.Equalf(t, test.expected, actual, test.in)
})
}
}
53 changes: 53 additions & 0 deletions filters/filters.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// The filters package defines the standard Liquid filters.
package filters

import (
"encoding/json"
"fmt"
"strings"

"github.com/osteele/liquid/expressions"
"github.com/osteele/liquid/generics"
)

// DefineStandardFilters defines the standard Liquid filters.
func DefineStandardFilters() {
// lists
expressions.DefineFilter("join", joinFilter)
expressions.DefineFilter("sort", sortFilter)

// strings
expressions.DefineFilter("split", splitFilter)

// Jekyll
expressions.DefineFilter("inspect", json.Marshal)
}

func joinFilter(in []interface{}, sep interface{}) interface{} {
a := make([]string, len(in))
s := ", "
if sep != nil {
s = fmt.Sprint(sep)
}
for i, x := range in {
a[i] = fmt.Sprint(x)
}
return strings.Join(a, s)
}

func sortFilter(in []interface{}, key interface{}) []interface{} {
out := make([]interface{}, len(in))
for i, v := range in {
out[i] = v
}
if key == nil {
generics.Sort(out)
} else {
generics.SortByProperty(out, key.(string))
}
return out
}

func splitFilter(in, sep string) interface{} {
return strings.Split(in, sep)
}
3 changes: 2 additions & 1 deletion tags/tags.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// The tags package defines the standard Liquid tags.
package tags

import (
Expand All @@ -6,6 +7,7 @@ import (
"github.com/osteele/liquid/chunks"
)

// DefineStandardTags defines the standard Liquid tags.
func DefineStandardTags() {
loopTags := []string{"break", "continue", "cycle"}
chunks.DefineControlTag("comment")
Expand All @@ -17,7 +19,6 @@ func DefineStandardTags() {
chunks.DefineControlTag("capture")
}


func ifTagAction(polarity bool) func(chunks.ASTControlTag) func(io.Writer, chunks.Context) error {
return func(node chunks.ASTControlTag) func(io.Writer, chunks.Context) error {
expr, err := chunks.MakeExpressionValueFn(node.Args)
Expand Down

0 comments on commit 4189f03

Please sign in to comment.