ref: c335efdd064b2c15aa52146f59ec267a2385d169
parent: 82cc1ac0f8860de6ca097235d23babb42c69c720
author: Ariejan de Vroom <ariejan@ariejan.net>
date: Wed Jun 10 19:49:55 EDT 2015
Add `after` template function Where `first` will return the first N items of a rangeable list, `after` will return all items after the Nth item. This allows the user to do something with the first N items and something different with the remaining items after N.
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -177,7 +177,7 @@
}
var start, length int
- toInt := func (v interface{}, message string) (int, error) {+ toInt := func(v interface{}, message string) (int, error) { switch i := v.(type) {case int:
return i, nil
@@ -388,6 +388,42 @@
return seqv.Slice(0, limitv).Interface(), nil
}
+// After is exposed to templates, to iterate over all the items after N in a
+// rangeable list. It's meant to accompany First
+func After(limit interface{}, seq interface{}) (interface{}, error) {+
+ if limit == nil || seq == nil {+ return nil, errors.New("both limit and seq must be provided")+ }
+
+ limitv, err := cast.ToIntE(limit)
+
+ if err != nil {+ return nil, err
+ }
+
+ if limitv < 1 {+ return nil, errors.New("can't return negative/empty count of items from sequence")+ }
+
+ seqv := reflect.ValueOf(seq)
+ seqv, isNil := indirect(seqv)
+ if isNil {+ return nil, errors.New("can't iterate over a nil value")+ }
+
+ switch seqv.Kind() {+ case reflect.Array, reflect.Slice, reflect.String:
+ // okay
+ default:
+ return nil, errors.New("can't iterate over " + reflect.ValueOf(seq).Type().String())+ }
+ if limitv >= seqv.Len() {+ return nil, errors.New("no items left")+ }
+ return seqv.Slice(limitv, seqv.Len()).Interface(), nil
+}
+
var (
zero reflect.Value
errorType = reflect.TypeOf((*error)(nil)).Elem()
@@ -1252,6 +1288,7 @@
"relURL": func(a string) template.HTML { return template.HTML(helpers.RelURL(a)) },"markdownify": Markdownify,
"first": First,
+ "after": After,
"where": Where,
"delimit": Delimit,
"sort": Sort,
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -4,7 +4,6 @@
"bytes"
"errors"
"fmt"
- "github.com/stretchr/testify/assert"
"html/template"
"path"
"reflect"
@@ -11,6 +10,8 @@
"runtime"
"testing"
"time"
+
+ "github.com/stretchr/testify/assert"
)
type tstNoStringer struct {@@ -236,6 +237,40 @@
{1, t, false}, } {results, err := First(this.count, this.sequence)
+ if b, ok := this.expect.(bool); ok && !b {+ if err == nil {+ t.Errorf("[%d] First didn't return an expected error", i)+ }
+ } else {+ if err != nil {+ t.Errorf("[%d] failed: %s", i, err)+ continue
+ }
+ if !reflect.DeepEqual(results, this.expect) {+ t.Errorf("[%d] First %d items, got %v but expected %v", i, this.count, results, this.expect)+ }
+ }
+ }
+}
+
+func TestAfter(t *testing.T) {+ for i, this := range []struct {+ count interface{}+ sequence interface{}+ expect interface{}+ }{+ {int(2), []string{"a", "b", "c", "d"}, []string{"c", "d"}},+ {int32(3), []string{"a", "b"}, false},+ {int64(2), []int{100, 200, 300}, []int{300}},+ {100, []int{100, 200}, false},+ {"1", []int{100, 200, 300}, []int{200, 300}},+ {int64(-1), []int{100, 200, 300}, false},+ {"noint", []int{100, 200, 300}, false},+ {1, nil, false},+ {nil, []int{100}, false},+ {1, t, false},+ } {+ results, err := After(this.count, this.sequence)
if b, ok := this.expect.(bool); ok && !b { if err == nil { t.Errorf("[%d] First didn't return an expected error", i)--
⑨