package collections
import (
+ "context"
"fmt"
"html/template"
"math/rand"
// Delimit takes a given list l and returns a string delimited by sep.
// If last is passed to the function, it will be used as the final delimiter.
-func (ns *Namespace) Delimit(l, sep any, last ...any) (template.HTML, error) {
+func (ns *Namespace) Delimit(ctx context.Context, l, sep any, last ...any) (template.HTML, error) {
d, err := cast.ToStringE(sep)
if err != nil {
return "", err
var str string
switch lv.Kind() {
case reflect.Map:
- sortSeq, err := ns.Sort(l)
+ sortSeq, err := ns.Sort(ctx, l)
if err != nil {
return "", err
}
package collections
import (
+ "context"
"errors"
"fmt"
"html/template"
var err error
if test.last == nil {
- result, err = ns.Delimit(test.seq, test.delimiter)
+ result, err = ns.Delimit(context.Background(), test.seq, test.delimiter)
} else {
- result, err = ns.Delimit(test.seq, test.delimiter, test.last)
+ result, err = ns.Delimit(context.Background(), test.seq, test.delimiter, test.last)
}
c.Assert(err, qt.IsNil, errMsg)
}
}
+
+// Issue 11234.
+func TestWhereWithWordCount(t *testing.T) {
+ t.Parallel()
+
+ files := `
+-- config.toml --
+baseURL = 'http://example.com/'
+-- layouts/index.html --
+Home: {{ range where site.RegularPages "WordCount" "gt" 50 }}{{ .Title }}|{{ end }}
+-- layouts/shortcodes/lorem.html --
+{{ "ipsum " | strings.Repeat (.Get 0 | int) }}
+
+-- content/p1.md --
+---
+title: "p1"
+---
+{{< lorem 100 >}}
+-- content/p2.md --
+---
+title: "p2"
+---
+{{< lorem 20 >}}
+-- content/p3.md --
+---
+title: "p3"
+---
+{{< lorem 60 >}}
+ `
+
+ b := hugolib.NewIntegrationTestBuilder(
+ hugolib.IntegrationTestConfig{
+ T: t,
+ TxtarString: files,
+ },
+ ).Build()
+
+ b.AssertFileContent("public/index.html", `
+Home: p1|p3|
+`)
+}
package collections
import (
+ "context"
"errors"
"reflect"
"sort"
)
// Sort returns a sorted copy of the list l.
-func (ns *Namespace) Sort(l any, args ...any) (any, error) {
+func (ns *Namespace) Sort(ctx context.Context, l any, args ...any) (any, error) {
if l == nil {
return nil, errors.New("sequence must be provided")
}
return nil, errors.New("can't iterate over a nil value")
}
+ ctxv := reflect.ValueOf(ctx)
+
var sliceType reflect.Type
switch seqv.Kind() {
case reflect.Array, reflect.Slice:
v := p.Pairs[i].Value
var err error
for i, elemName := range path {
- v, err = evaluateSubElem(v, elemName)
+ v, err = evaluateSubElem(ctxv, v, elemName)
if err != nil {
return nil, err
}
v := p.Pairs[i].Value
var err error
for i, elemName := range path {
- v, err = evaluateSubElem(v, elemName)
+ v, err = evaluateSubElem(ctxv, v, elemName)
if err != nil {
return nil, err
}
package collections
import (
+ "context"
"fmt"
"reflect"
"testing"
var result any
var err error
if test.sortByField == nil {
- result, err = ns.Sort(test.seq)
+ result, err = ns.Sort(context.Background(), test.seq)
} else {
- result, err = ns.Sort(test.seq, test.sortByField, test.sortAsc)
+ result, err = ns.Sort(context.Background(), test.seq, test.sortByField, test.sortAsc)
}
if b, ok := test.expect.(bool); ok && !b {
package collections
import (
+ "context"
"errors"
"fmt"
"reflect"
)
// Where returns a filtered subset of collection c.
-func (ns *Namespace) Where(c, key any, args ...any) (any, error) {
+func (ns *Namespace) Where(ctx context.Context, c, key any, args ...any) (any, error) {
seqv, isNil := indirect(reflect.ValueOf(c))
if isNil {
return nil, errors.New("can't iterate over a nil value of type " + reflect.ValueOf(c).Type().String())
return nil, err
}
+ ctxv := reflect.ValueOf(ctx)
+
var path []string
kv := reflect.ValueOf(key)
if kv.Kind() == reflect.String {
switch seqv.Kind() {
case reflect.Array, reflect.Slice:
- return ns.checkWhereArray(seqv, kv, mv, path, op)
+ return ns.checkWhereArray(ctxv, seqv, kv, mv, path, op)
case reflect.Map:
- return ns.checkWhereMap(seqv, kv, mv, path, op)
+ return ns.checkWhereMap(ctxv, seqv, kv, mv, path, op)
default:
return nil, fmt.Errorf("can't iterate over %v", c)
}
return false, nil
}
-func evaluateSubElem(obj reflect.Value, elemName string) (reflect.Value, error) {
+func evaluateSubElem(ctx, obj reflect.Value, elemName string) (reflect.Value, error) {
if !obj.IsValid() {
return zero, errors.New("can't evaluate an invalid value")
}
index := hreflect.GetMethodIndexByName(objPtr.Type(), elemName)
if index != -1 {
+ var args []reflect.Value
mt := objPtr.Type().Method(index)
+ num := mt.Type.NumIn()
+ maxNumIn := 1
+ if num > 1 && mt.Type.In(1).Implements(hreflect.ContextInterface) {
+ args = []reflect.Value{ctx}
+ maxNumIn = 2
+ }
+
switch {
case mt.PkgPath != "":
return zero, fmt.Errorf("%s is an unexported method of type %s", elemName, typ)
- case mt.Type.NumIn() > 1:
- return zero, fmt.Errorf("%s is a method of type %s but requires more than 1 parameter", elemName, typ)
+ case mt.Type.NumIn() > maxNumIn:
+ return zero, fmt.Errorf("%s is a method of type %s but requires more than %d parameter", elemName, typ, maxNumIn)
case mt.Type.NumOut() == 0:
return zero, fmt.Errorf("%s is a method of type %s but returns no output", elemName, typ)
case mt.Type.NumOut() > 2:
case mt.Type.NumOut() == 2 && !mt.Type.Out(1).Implements(errorType):
return zero, fmt.Errorf("%s is a method of type %s returning two values but the second value is not an error type", elemName, typ)
}
- res := objPtr.Method(mt.Index).Call([]reflect.Value{})
+ res := objPtr.Method(mt.Index).Call(args)
if len(res) == 2 && !res[1].IsNil() {
return zero, fmt.Errorf("error at calling a method %s of type %s: %s", elemName, typ, res[1].Interface().(error))
}
// checkWhereArray handles the where-matching logic when the seqv value is an
// Array or Slice.
-func (ns *Namespace) checkWhereArray(seqv, kv, mv reflect.Value, path []string, op string) (any, error) {
+func (ns *Namespace) checkWhereArray(ctxv, seqv, kv, mv reflect.Value, path []string, op string) (any, error) {
rv := reflect.MakeSlice(seqv.Type(), 0, 0)
for i := 0; i < seqv.Len(); i++ {
vvv = rvv
for i, elemName := range path {
var err error
- vvv, err = evaluateSubElem(vvv, elemName)
+ vvv, err = evaluateSubElem(ctxv, vvv, elemName)
if err != nil {
continue
}
// checkWhereMap handles the where-matching logic when the seqv value is a Map.
-func (ns *Namespace) checkWhereMap(seqv, kv, mv reflect.Value, path []string, op string) (any, error) {
+func (ns *Namespace) checkWhereMap(ctxv, seqv, kv, mv reflect.Value, path []string, op string) (any, error) {
rv := reflect.MakeMap(seqv.Type())
keys := seqv.MapKeys()
for _, k := range keys {
elemv := seqv.MapIndex(k)
switch elemv.Kind() {
case reflect.Array, reflect.Slice:
- r, err := ns.checkWhereArray(elemv, kv, mv, path, op)
+ r, err := ns.checkWhereArray(ctxv, elemv, kv, mv, path, op)
if err != nil {
return nil, err
}
switch elemvv.Kind() {
case reflect.Array, reflect.Slice:
- r, err := ns.checkWhereArray(elemvv, kv, mv, path, op)
+ r, err := ns.checkWhereArray(ctxv, elemvv, kv, mv, path, op)
if err != nil {
return nil, err
}
package collections
import (
+ "context"
"fmt"
"html/template"
"reflect"
var err error
if len(test.op) > 0 {
- results, err = ns.Where(test.seq, test.key, test.op, test.match)
+ results, err = ns.Where(context.Background(), test.seq, test.key, test.op, test.match)
} else {
- results, err = ns.Where(test.seq, test.key, test.match)
+ results, err = ns.Where(context.Background(), test.seq, test.key, test.match)
}
if b, ok := test.expect.(bool); ok && !b {
if err == nil {
}
var err error
- _, err = ns.Where(map[string]int{"a": 1, "b": 2}, "a", []byte("="), 1)
+ _, err = ns.Where(context.Background(), map[string]int{"a": 1, "b": 2}, "a", []byte("="), 1)
if err == nil {
t.Errorf("Where called with none string op value didn't return an expected error")
}
- _, err = ns.Where(map[string]int{"a": 1, "b": 2}, "a", []byte("="), 1, 2)
+ _, err = ns.Where(context.Background(), map[string]int{"a": 1, "b": 2}, "a", []byte("="), 1, 2)
if err == nil {
t.Errorf("Where called with more than two variable arguments didn't return an expected error")
}
- _, err = ns.Where(map[string]int{"a": 1, "b": 2}, "a")
+ _, err = ns.Where(context.Background(), map[string]int{"a": 1, "b": 2}, "a")
if err == nil {
t.Errorf("Where called with no variable arguments didn't return an expected error")
}
{reflect.ValueOf(map[int]string{1: "foo", 2: "bar"}), "1", false},
{reflect.ValueOf([]string{"foo", "bar"}), "1", false},
} {
- result, err := evaluateSubElem(test.value, test.key)
+ result, err := evaluateSubElem(reflect.ValueOf(context.Background()), test.value, test.key)
if b, ok := test.expect.(bool); ok && !b {
if err == nil {
t.Errorf("[%d] evaluateSubElem didn't return an expected error", i)