mirror of
https://github.com/gohugoio/hugo.git
synced 2025-09-01 22:42:45 +02:00
Fix Params case handling in the index, sort and where func
This means that you can now do: ``` {{ range where .Site.Pages "Params.MYPARAM" "foo" }} ```
This commit is contained in:
@@ -22,6 +22,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/common/loggers"
|
||||
"github.com/gohugoio/hugo/config"
|
||||
@@ -891,6 +893,15 @@ type TstX struct {
|
||||
unexported string
|
||||
}
|
||||
|
||||
type TstParams struct {
|
||||
params maps.Params
|
||||
}
|
||||
|
||||
func (x TstParams) Params() maps.Params {
|
||||
return x.params
|
||||
|
||||
}
|
||||
|
||||
type TstXIHolder struct {
|
||||
XI TstXI
|
||||
}
|
||||
|
@@ -17,6 +17,10 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/spf13/cast"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
)
|
||||
|
||||
// Index returns the result of indexing its first argument by the following
|
||||
@@ -34,6 +38,11 @@ func (ns *Namespace) Index(item interface{}, args ...interface{}) (interface{},
|
||||
return nil, errors.New("index of untyped nil")
|
||||
}
|
||||
|
||||
lowerm, ok := item.(maps.Params)
|
||||
if ok {
|
||||
return lowerm.Get(cast.ToStringSlice(args)...), nil
|
||||
}
|
||||
|
||||
var indices []interface{}
|
||||
|
||||
if len(args) == 1 {
|
||||
@@ -79,6 +88,7 @@ func (ns *Namespace) Index(item interface{}, args ...interface{}) (interface{},
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if x := v.MapIndex(index); x.IsValid() {
|
||||
v = x
|
||||
} else {
|
||||
|
@@ -17,6 +17,8 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
)
|
||||
@@ -42,7 +44,8 @@ func TestIndex(t *testing.T) {
|
||||
{[]map[string]map[string]string{{"a": {"b": "c"}}}, []interface{}{0, "a", "b"}, "c", false},
|
||||
{map[string]map[string]interface{}{"a": {"b": []string{"c", "d"}}}, []interface{}{"a", "b", 1}, "d", false},
|
||||
{map[string]map[string]string{"a": {"b": "c"}}, []interface{}{[]string{"a", "b"}}, "c", false},
|
||||
|
||||
{maps.Params{"a": "av"}, []interface{}{"A"}, "av", false},
|
||||
{maps.Params{"a": map[string]interface{}{"b": "bv"}}, []interface{}{"A", "B"}, "bv", false},
|
||||
// errors
|
||||
{nil, nil, nil, true},
|
||||
{[]int{0, 1}, []interface{}{"1"}, nil, true},
|
||||
|
@@ -19,6 +19,7 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
"github.com/gohugoio/hugo/tpl/compare"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
@@ -75,11 +76,19 @@ func (ns *Namespace) Sort(seq interface{}, args ...interface{}) (interface{}, er
|
||||
} else {
|
||||
v := p.Pairs[i].Value
|
||||
var err error
|
||||
for _, elemName := range path {
|
||||
for i, elemName := range path {
|
||||
v, err = evaluateSubElem(v, elemName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !v.IsValid() {
|
||||
continue
|
||||
}
|
||||
// Special handling of lower cased maps.
|
||||
if params, ok := v.Interface().(maps.Params); ok {
|
||||
v = reflect.ValueOf(params.Get(path[i+1:]...))
|
||||
break
|
||||
}
|
||||
}
|
||||
p.Pairs[i].Key = v
|
||||
}
|
||||
@@ -89,6 +98,7 @@ func (ns *Namespace) Sort(seq interface{}, args ...interface{}) (interface{}, er
|
||||
keys := seqv.MapKeys()
|
||||
for i := 0; i < seqv.Len(); i++ {
|
||||
p.Pairs[i].Value = seqv.MapIndex(keys[i])
|
||||
|
||||
if sortByField == "" {
|
||||
p.Pairs[i].Key = keys[i]
|
||||
} else if sortByField == "value" {
|
||||
@@ -96,11 +106,19 @@ func (ns *Namespace) Sort(seq interface{}, args ...interface{}) (interface{}, er
|
||||
} else {
|
||||
v := p.Pairs[i].Value
|
||||
var err error
|
||||
for _, elemName := range path {
|
||||
for i, elemName := range path {
|
||||
v, err = evaluateSubElem(v, elemName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !v.IsValid() {
|
||||
continue
|
||||
}
|
||||
// Special handling of lower cased maps.
|
||||
if params, ok := v.Interface().(maps.Params); ok {
|
||||
v = reflect.ValueOf(params.Get(path[i+1:]...))
|
||||
break
|
||||
}
|
||||
}
|
||||
p.Pairs[i].Key = v
|
||||
}
|
||||
@@ -135,6 +153,7 @@ func (p pairList) Less(i, j int) bool {
|
||||
// can only call Interface() on valid reflect Values
|
||||
return sortComp.Lt(iv.Interface(), jv.Interface())
|
||||
}
|
||||
|
||||
// if j is invalid, test i against i's zero value
|
||||
return sortComp.Lt(iv.Interface(), reflect.Zero(iv.Type()))
|
||||
}
|
||||
|
@@ -18,6 +18,8 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
)
|
||||
|
||||
@@ -100,6 +102,20 @@ func TestSort(t *testing.T) {
|
||||
"asc",
|
||||
[]*TstX{{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"}, {A: "g", B: "h"}, {A: "i", B: "j"}},
|
||||
},
|
||||
// Lower case Params, slice
|
||||
{
|
||||
[]TstParams{{params: maps.Params{"color": "indigo"}}, {params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}},
|
||||
".Params.COLOR",
|
||||
"asc",
|
||||
[]TstParams{{params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}, {params: maps.Params{"color": "indigo"}}},
|
||||
},
|
||||
// Lower case Params, map
|
||||
{
|
||||
map[string]TstParams{"1": {params: maps.Params{"color": "indigo"}}, "2": {params: maps.Params{"color": "blue"}}, "3": {params: maps.Params{"color": "green"}}},
|
||||
".Params.CoLoR",
|
||||
"asc",
|
||||
[]TstParams{{params: maps.Params{"color": "blue"}}, {params: maps.Params{"color": "green"}}, {params: maps.Params{"color": "indigo"}}},
|
||||
},
|
||||
// test map sorting by struct's method
|
||||
{
|
||||
map[string]TstX{"1": {A: "i", B: "j"}, "2": {A: "e", B: "f"}, "3": {A: "c", B: "d"}, "4": {A: "g", B: "h"}, "5": {A: "a", B: "b"}},
|
||||
|
@@ -18,6 +18,8 @@ import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
)
|
||||
|
||||
// Where returns a filtered subset of a given data type.
|
||||
@@ -277,6 +279,7 @@ func evaluateSubElem(obj reflect.Value, elemName string) (reflect.Value, error)
|
||||
if !obj.IsValid() {
|
||||
return zero, errors.New("can't evaluate an invalid value")
|
||||
}
|
||||
|
||||
typ := obj.Type()
|
||||
obj, isNil := indirect(obj)
|
||||
|
||||
@@ -295,6 +298,7 @@ func evaluateSubElem(obj reflect.Value, elemName string) (reflect.Value, error)
|
||||
if objPtr.Kind() != reflect.Interface && objPtr.CanAddr() {
|
||||
objPtr = objPtr.Addr()
|
||||
}
|
||||
|
||||
mt, ok := objPtr.Type().MethodByName(elemName)
|
||||
if ok {
|
||||
switch {
|
||||
@@ -368,16 +372,22 @@ func parseWhereArgs(args ...interface{}) (mv reflect.Value, op string, err error
|
||||
// Array or Slice.
|
||||
func (ns *Namespace) checkWhereArray(seqv, kv, mv reflect.Value, path []string, op string) (interface{}, error) {
|
||||
rv := reflect.MakeSlice(seqv.Type(), 0, 0)
|
||||
|
||||
for i := 0; i < seqv.Len(); i++ {
|
||||
var vvv reflect.Value
|
||||
rvv := seqv.Index(i)
|
||||
|
||||
if kv.Kind() == reflect.String {
|
||||
vvv = rvv
|
||||
for _, elemName := range path {
|
||||
var err error
|
||||
vvv, err = evaluateSubElem(vvv, elemName)
|
||||
if err != nil {
|
||||
continue
|
||||
if params, ok := rvv.Interface().(maps.Params); ok {
|
||||
vvv = reflect.ValueOf(params.Get(path...))
|
||||
} else {
|
||||
vvv = rvv
|
||||
for _, elemName := range path {
|
||||
var err error
|
||||
vvv, err = evaluateSubElem(vvv, elemName)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@@ -16,9 +16,12 @@ package collections
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gohugoio/hugo/common/maps"
|
||||
|
||||
"github.com/gohugoio/hugo/deps"
|
||||
)
|
||||
|
||||
@@ -162,6 +165,37 @@ func TestWhere(t *testing.T) {
|
||||
{1: "a", 2: "m"},
|
||||
},
|
||||
},
|
||||
{
|
||||
seq: []maps.Params{
|
||||
{"a": "a1", "b": "b1"}, {"a": "a2", "b": "b2"},
|
||||
},
|
||||
key: "B", match: "b2",
|
||||
expect: []maps.Params{
|
||||
maps.Params{"a": "a2", "b": "b2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
seq: []maps.Params{
|
||||
maps.Params{
|
||||
"a": map[string]interface{}{
|
||||
"b": "b1",
|
||||
},
|
||||
},
|
||||
maps.Params{
|
||||
"a": map[string]interface{}{
|
||||
"b": "b2",
|
||||
},
|
||||
},
|
||||
},
|
||||
key: "A.B", match: "b2",
|
||||
expect: []maps.Params{
|
||||
maps.Params{
|
||||
"a": map[string]interface{}{
|
||||
"b": "b2",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
seq: []*TstX{
|
||||
{A: "a", B: "b"}, {A: "c", B: "d"}, {A: "e", B: "f"},
|
||||
@@ -557,11 +591,24 @@ func TestWhere(t *testing.T) {
|
||||
"zap": []interface{}{map[interface{}]interface{}{"a": 5, "b": 6}},
|
||||
},
|
||||
},
|
||||
{
|
||||
seq: map[string]interface{}{
|
||||
"foo": []interface{}{maps.Params{"a": 1, "b": 2}},
|
||||
"bar": []interface{}{maps.Params{"a": 3, "b": 4}},
|
||||
"zap": []interface{}{maps.Params{"a": 5, "b": 6}},
|
||||
},
|
||||
key: "B", op: ">", match: 3,
|
||||
expect: map[string]interface{}{
|
||||
"bar": []interface{}{maps.Params{"a": 3, "b": 4}},
|
||||
"zap": []interface{}{maps.Params{"a": 5, "b": 6}},
|
||||
},
|
||||
},
|
||||
} {
|
||||
|
||||
testVariants := createTestVariants(test)
|
||||
for j, test := range testVariants {
|
||||
name := fmt.Sprintf("[%d/%d] %T %s %s", i, j, test.seq, test.op, test.key)
|
||||
name := fmt.Sprintf("%d/%d %T %s %s", i, j, test.seq, test.op, test.key)
|
||||
name = strings.ReplaceAll(name, "[]", "slice-of-")
|
||||
t.Run(name, func(t *testing.T) {
|
||||
var results interface{}
|
||||
var err error
|
||||
|
Reference in New Issue
Block a user