ref: 3400aff2588cbf9dd4629c05537d16b019d0fdf5
parent: fdfa4a5fe62232f65f1dd8d6fe0c500374228788
author: Gareth Watts <gareth@omnipotent.net>
date: Thu Oct 22 08:14:14 EDT 2020
Allow cascade _target to work with non toml fm
The TOML lib unmarshals slices of string maps to []map[string]interface{}
whereas YAML and JSON decode to []interface{}
The existing tests only check for TOML working correctly, and _target
with cascade did not work at all for frontmatter defined in other formats.
Add a function to normalize those slices
Fixes #7874
--- a/common/maps/maps.go
+++ b/common/maps/maps.go
@@ -14,6 +14,7 @@
package maps
import (
+ "fmt"
"strings"
"github.com/gobwas/glob"
@@ -62,6 +63,23 @@
func ToStringMap(in interface{}) map[string]interface{} {m, _ := ToStringMapE(in)
return m
+}
+
+func ToSliceStringMap(in interface{}) ([]map[string]interface{}, error) {+ switch v := in.(type) {+ case []map[string]interface{}:+ return v, nil
+ case []interface{}:+ var s []map[string]interface{}+ for _, entry := range v {+ if vv, ok := entry.(map[string]interface{}); ok {+ s = append(s, vv)
+ }
+ }
+ return s, nil
+ default:
+ return nil, fmt.Errorf("unable to cast %#v of type %T to []map[string]interface{}", in, in)+ }
}
type keyRename struct {--- a/common/maps/maps_test.go
+++ b/common/maps/maps_test.go
@@ -75,6 +75,39 @@
}
}
+func TestToSliceStringMap(t *testing.T) {+ c := qt.New(t)
+
+ tests := []struct {+ input interface{}+ expected []map[string]interface{}+ }{+ {+ input: []map[string]interface{}{+ {"abc": 123},+ },
+ expected: []map[string]interface{}{+ {"abc": 123},+ },
+ }, {+ input: []interface{}{+ map[string]interface{}{+ "def": 456,
+ },
+ },
+ expected: []map[string]interface{}{+ {"def": 456},+ },
+ },
+ }
+
+ for _, test := range tests {+ v, err := ToSliceStringMap(test.input)
+ c.Assert(err, qt.IsNil)
+ c.Assert(v, qt.DeepEquals, test.expected)
+ }
+}
+
func TestRenameKeys(t *testing.T) {c := qt.New(t)
--- a/hugolib/cascade_test.go
+++ b/hugolib/cascade_test.go
@@ -459,4 +459,58 @@
})
+ c.Run("slice with yaml _target", func(c *qt.C) {+ b := newBuilder(c)
+
+ b.WithContent("_index.md", `---+title: "Home"
+cascade:
+- p1: p1
+ _target:
+ path: "**p1**"
+- p2: p2
+ _target:
+ kind: "section"
+---
+`)
+
+ b.Build(BuildCfg{})+
+ b.AssertFileContent("public/index.html", `+P1|p1:p1|p2:|
+S1|p1:|p2:p2|
+`)
+
+ })
+
+ c.Run("slice with json _target", func(c *qt.C) {+ b := newBuilder(c)
+
+ b.WithContent("_index.md", `{+"title": "Home",
+"cascade": [
+ {+ "p1": "p1",
+ "_target": {+ "path": "**p1**"
+ }
+ },{+ "p2": "p2",
+ "_target": {+ "kind": "section"
+ }
+ }
+]
+}
+`)
+
+ b.Build(BuildCfg{})+
+ b.AssertFileContent("public/index.html", `+ P1|p1:p1|p2:|
+ S1|p1:|p2:p2|
+ `)
+
+ })
+
}
--- a/hugolib/page__meta.go
+++ b/hugolib/page__meta.go
@@ -342,8 +342,7 @@
if p.bucket != nil {// Check for any cascade define on itself.
if cv, found := frontmatter["cascade"]; found {- switch v := cv.(type) {- case []map[string]interface{}:+ if v, err := maps.ToSliceStringMap(cv); err == nil {p.bucket.cascade = make(map[page.PageMatcher]maps.Params)
for _, vv := range v {@@ -367,12 +366,12 @@
}
}
- default:
+ } else { p.bucket.cascade = map[page.PageMatcher]maps.Params{ page.PageMatcher{}: maps.ToStringMap(cv),}
- }
+ }
}
}
} else {--
⑨