shithub: hugo

Download patch

ref: c335efdd064b2c15aa52146f59ec267a2385d169
parent: 82cc1ac0f8860de6ca097235d23babb42c69c720
author: Ariejan de Vroom <[email protected]>
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)