diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-09-08 13:00:36 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2018-09-08 20:20:26 +0200 |
commit | 6667c6d7430acc16b3683fbbacd263f1d00c8672 (patch) | |
tree | a238b6bf5a2e02da09e79eafa4784e7f51441144 /tpl/collections | |
parent | cfda13b36367465016f4458ab9924c948ed02b6f (diff) |
tpl/collections: Add group template func
This extends the page grouping in Hugo with a template function that allows for ad-hoc grouping.
A made-up example:
```
{{ $cool := where .Site.RegularPages "Params.cool" true | group "cool" }}
{{ $blue := where .Site.RegularPages "Params.blue" true | group "blue" }}
{{ $paginator := .Paginate (slice $cool $blue) }}
```
Closes #4865
Diffstat (limited to 'tpl/collections')
-rw-r--r-- | tpl/collections/collections.go | 25 | ||||
-rw-r--r-- | tpl/collections/collections_test.go | 41 | ||||
-rw-r--r-- | tpl/collections/init.go | 5 |
3 files changed, 71 insertions, 0 deletions
diff --git a/tpl/collections/collections.go b/tpl/collections/collections.go index 51bc5c796..2ccee43d4 100644 --- a/tpl/collections/collections.go +++ b/tpl/collections/collections.go @@ -23,6 +23,7 @@ import ( "strings" "time" + "github.com/gohugoio/hugo/common/collections" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/common/types" "github.com/gohugoio/hugo/deps" @@ -311,6 +312,30 @@ func (ns *Namespace) Intersect(l1, l2 interface{}) (interface{}, error) { } } +// Group groups a set of elements by the given key. +// This is currently only supported for Pages. +func (ns *Namespace) Group(key interface{}, items interface{}) (interface{}, error) { + if key == nil { + return nil, errors.New("nil is not a valid key to group by") + } + + tp := reflect.TypeOf(items) + switch tp.Kind() { + case reflect.Array, reflect.Slice: + tp = tp.Elem() + if tp.Kind() == reflect.Ptr { + tp = tp.Elem() + } + in := reflect.Zero(tp).Interface() + switch vv := in.(type) { + case collections.Grouper: + return vv.Group(key, items) + } + } + + return nil, fmt.Errorf("grouping not supported for type %T", items) +} + // IsSet returns whether a given array, channel, slice, or map has a key // defined. func (ns *Namespace) IsSet(a interface{}, key interface{}) (bool, error) { diff --git a/tpl/collections/collections_test.go b/tpl/collections/collections_test.go index c3e88f36f..cbcf819c7 100644 --- a/tpl/collections/collections_test.go +++ b/tpl/collections/collections_test.go @@ -75,6 +75,47 @@ func TestAfter(t *testing.T) { } } +type tstGrouper struct { +} + +type tstGroupers []*tstGrouper + +func (g tstGrouper) Group(key interface{}, items interface{}) (interface{}, error) { + ilen := reflect.ValueOf(items).Len() + return fmt.Sprintf("%v(%d)", key, ilen), nil +} + +func TestGroup(t *testing.T) { + t.Parallel() + + ns := New(&deps.Deps{}) + + for i, test := range []struct { + key interface{} + items interface{} + expect interface{} + }{ + {"a", []*tstGrouper{&tstGrouper{}, &tstGrouper{}}, "a(2)"}, + {"b", tstGroupers{&tstGrouper{}, &tstGrouper{}}, "b(2)"}, + {"a", []tstGrouper{tstGrouper{}, tstGrouper{}}, "a(2)"}, + {"a", []*tstGrouper{}, "a(0)"}, + {"a", []string{"a", "b"}, false}, + {nil, []*tstGrouper{&tstGrouper{}, &tstGrouper{}}, false}, + } { + errMsg := fmt.Sprintf("[%d] %v", i, test) + + result, err := ns.Group(test.key, test.items) + + if b, ok := test.expect.(bool); ok && !b { + require.Error(t, err, errMsg) + continue + } + + require.NoError(t, err, errMsg) + require.Equal(t, test.expect, result, errMsg) + } +} + func TestDelimit(t *testing.T) { t.Parallel() diff --git a/tpl/collections/init.go b/tpl/collections/init.go index b986b3b42..ad4f6f207 100644 --- a/tpl/collections/init.go +++ b/tpl/collections/init.go @@ -138,6 +138,11 @@ func init() { [][2]string{}, ) + ns.AddMethodMapping(ctx.Group, + []string{"group"}, + [][2]string{}, + ) + ns.AddMethodMapping(ctx.Seq, []string{"seq"}, [][2]string{ |