ref: 0a2e5424ab5b684e88eebe87efc88c55e11e9789
parent: 627d016cc95d355f54e9565c44bfef419e0c7044
author: Ariejan de Vroom <[email protected]>
date: Wed Jun 10 20:11:47 EDT 2015
Add `last` template function `last` allows the user to select the last X items of and array.
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -388,6 +388,42 @@
return seqv.Slice(0, limitv).Interface(), nil
}
+// Last is exposed to templates, to iterate over the last N items in a
+// rangeable list.
+func Last(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() {
+ limitv = seqv.Len()
+ }
+ return seqv.Slice(seqv.Len()-limitv, seqv.Len()).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(index interface{}, seq interface{}) (interface{}, error) {
@@ -1288,6 +1324,7 @@
"relURL": func(a string) template.HTML { return template.HTML(helpers.RelURL(a)) },
"markdownify": Markdownify,
"first": First,
+ "last": Last,
"after": After,
"where": Where,
"delimit": Delimit,
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -253,6 +253,40 @@
}
}
+func TestLast(t *testing.T) {
+ for i, this := range []struct {
+ count interface{}
+ sequence interface{}
+ expect interface{}
+ }{
+ {int(2), []string{"a", "b", "c"}, []string{"b", "c"}},
+ {int32(3), []string{"a", "b"}, []string{"a", "b"}},
+ {int64(2), []int{100, 200, 300}, []int{200, 300}},
+ {100, []int{100, 200}, []int{100, 200}},
+ {"1", []int{100, 200, 300}, []int{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 := Last(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{}