Skip to content

Commit

Permalink
Implement cascading front matter
Browse files Browse the repository at this point in the history
  • Loading branch information
bep committed Aug 9, 2019
1 parent 7ff0a8e commit 53da54c
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 11 deletions.
4 changes: 2 additions & 2 deletions hugolib/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -520,7 +520,7 @@ func (p *pageState) addResources(r ...resource.Resource) {
p.resources = append(p.resources, r...)
}

func (p *pageState) mapContent(meta *pageMeta) error {
func (p *pageState) mapContent(bucket *pagesMapBucket, meta *pageMeta) error {

s := p.shortcodeState

Expand Down Expand Up @@ -563,7 +563,7 @@ Loop:
}
}

if err := meta.setMetadata(p, m); err != nil {
if err := meta.setMetadata(bucket, p, m); err != nil {
return err
}

Expand Down
3 changes: 3 additions & 0 deletions hugolib/page__common.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ type pageCommon struct {
// Laziliy initialized dependencies.
init *lazy.Init

metaInit sync.Once
metaInitFn func(bucket *pagesMapBucket) error

// All of these represents the common parts of a page.Page
maps.Scratcher
navigation.PageMenusProvider
Expand Down
17 changes: 16 additions & 1 deletion hugolib/page__meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ func (p *pageMeta) Weight() int {
return p.weight
}

func (pm *pageMeta) setMetadata(p *pageState, frontmatter map[string]interface{}) error {
func (pm *pageMeta) setMetadata(bucket *pagesMapBucket, p *pageState, frontmatter map[string]interface{}) error {
if frontmatter == nil {
return errors.New("missing frontmatter data")
}
Expand All @@ -316,6 +316,15 @@ func (pm *pageMeta) setMetadata(p *pageState, frontmatter map[string]interface{}
// Needed for case insensitive fetching of params values
maps.ToLower(frontmatter)

if bucket.cascade != nil {
// TODO(bep) cascade tolower
for k, v := range bucket.cascade {
if _, found := frontmatter[k]; !found {
frontmatter[k] = v
}
}
}

var mtime time.Time
if p.File().FileInfo() != nil {
mtime = p.File().FileInfo().ModTime()
Expand Down Expand Up @@ -370,6 +379,12 @@ func (pm *pageMeta) setMetadata(p *pageState, frontmatter map[string]interface{}
case "linktitle":
pm.linkTitle = cast.ToString(v)
pm.params[loki] = pm.linkTitle
case "cascade":
if p.IsNode() {
bucket.cascade = cast.ToStringMap(v)
} else {
pm.params[loki] = v
}
case "summary":
pm.summary = cast.ToString(v)
pm.params[loki] = pm.summary
Expand Down
14 changes: 9 additions & 5 deletions hugolib/page__new.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,16 @@ func newPageWithContent(f *fileInfo, s *Site, bundled bool, content resource.Ope

ps.shortcodeState = newShortcodeHandler(ps, ps.s, nil)

if err := ps.mapContent(metaProvider); err != nil {
return nil, ps.wrapError(err)
}
ps.metaInitFn = func(bucket *pagesMapBucket) error {
if err := ps.mapContent(bucket, metaProvider); err != nil {
return ps.wrapError(err)
}

if err := metaProvider.applyDefaultValues(); err != nil {
return nil, err
if err := metaProvider.applyDefaultValues(); err != nil {
return err
}

return nil
}

ps.init.Add(func() (interface{}, error) {
Expand Down
8 changes: 8 additions & 0 deletions hugolib/pagecollections.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ func (c *PageCollections) clearResourceCacheForPage(page *pageState) {
}

func (c *PageCollections) assemblePagesMap(s *Site) error {

c.pagesMap = newPagesMap(s)

rootSections := make(map[string]bool)
Expand Down Expand Up @@ -449,13 +450,19 @@ func (c *PageCollections) createWorkAllPages() error {
panic(fmt.Sprintf("[BUG] parent bucket not found for %q", s))
}
parentBucket = parentv.(*pagesMapBucket)
if bucket.cascade == nil {
// Fetch it from above
bucket.cascade = parentBucket.cascade
}

if !mainSectionsFound && strings.Count(s, "/") == 1 {
// Root section
rootBuckets = append(rootBuckets, bucket)
}
}

c.pagesMap.initPageMeta(bucket.owner, bucket)

if bucket.owner.IsHome() {
if resource.IsZeroDates(bucket.owner) {
// Calculate dates from the page tree.
Expand All @@ -477,6 +484,7 @@ func (c *PageCollections) createWorkAllPages() error {

tmp := bucket.pages[:0]
for _, x := range bucket.pages {
c.pagesMap.initPageMeta(x.(*pageState), bucket)
if c.pagesMap.s.shouldBuild(x) {
tmp = append(tmp, x)
}
Expand Down
12 changes: 12 additions & 0 deletions hugolib/pages_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ func (m *pagesMap) addPage(p *pageState) {
bucket.pages = append(bucket.pages, p)
}

func (m *pagesMap) initPageMeta(p *pageState, bucket *pagesMapBucket) {
p.metaInit.Do(func() {
if p.metaInitFn != nil {
// TODO(bep) cascade error etc.
p.metaInitFn(bucket)
}
})
}

func (m *pagesMap) withEveryPage(f func(p *pageState)) {
m.r.Walk(func(k string, v interface{}) bool {
b := v.(*pagesMapBucket)
Expand Down Expand Up @@ -312,6 +321,9 @@ type pagesMapBucket struct {
// Some additional metatadata attached to this node.
meta map[string]interface{}

// Cascading front matter.
cascade map[string]interface{}

owner *pageState // The branch node

// When disableKinds is enabled for this node.
Expand Down
103 changes: 103 additions & 0 deletions hugolib/pages_metadata_handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
// Copyright 2019 The Hugo Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package hugolib

import (
"bytes"
"fmt"
"testing"

"github.com/gohugoio/hugo/parser"
"github.com/gohugoio/hugo/parser/metadecoders"
)

func TestCascade(t *testing.T) {

weight := 0

p := func(m map[string]interface{}) string {
var yamlStr string

if len(m) > 0 {
var b bytes.Buffer

parser.InterfaceToConfig(m, metadecoders.YAML, &b)
yamlStr = b.String()
}

weight += 10

metaStr := "---\n" + yamlStr + fmt.Sprintf("\nweight: %d\n---", weight)

return metaStr

}

b := newTestSitesBuilder(t).WithConfigFile("toml", `
baseURL = "https://example.org"
disableKinds = ["taxonomy", "taxonomyTerm"]
`)
b.WithContent(
"_index.md", p(map[string]interface{}{
"title": "Home",
"cascade": map[string]interface{}{
"title": "Cascade Home",
"icon": "home.png",
},
}),
"p1.md", p(map[string]interface{}{
"title": "p1",
}),
"p2.md", p(map[string]interface{}{}),
"sect1/_index.md", p(map[string]interface{}{
"title": "Sect1",
"cascade": map[string]interface{}{
"title": "Cascade Sect1",
"icon": "sect1.png",
},
}),
"sect1/s1_2/_index.md", p(map[string]interface{}{
"title": "Sect1_2",
}),
"sect1/s1_2/p1.md", p(map[string]interface{}{
"title": "Sect1_2_p1",
}),
"sect2/_index.md", p(map[string]interface{}{
"title": "Sect2",
}),
"sect2/p1.md", p(map[string]interface{}{
"title": "Sect2_p1",
}),
"sect2/p2.md", p(map[string]interface{}{}),
)

b.WithTemplates("index.html", `
{{ range .Site.Pages }}
{{- .Weight }}|{{ .Kind }}|{{ .Path }}|{{ .Title }}|{{ .Params.icon }}|
{{ end }}
`)

b.Build(BuildCfg{})

b.AssertFileContent("public/index.html", `10|home|_index.md|Home||
20|page|p1.md|p1|home.png|
30|page|p2.md|Cascade Home|home.png|
40|section|sect1/_index.md|Sect1|home.png|
50|section|sect1/s1_2/_index.md|Sect1_2|sect1.png|
60|page|sect1/s1_2/p1.md|Sect1_2_p1|sect1.png|
70|section|sect2/_index.md|Sect2|home.png|
80|page|sect2/p1.md|Sect2_p1|home.png|
90|page|sect2/p2.md|Cascade Home|home.png|`)
}
10 changes: 7 additions & 3 deletions hugolib/testhelpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -649,9 +649,13 @@ func (s *sitesBuilder) AssertHome(matches ...string) {
func (s *sitesBuilder) AssertFileContent(filename string, matches ...string) {
s.T.Helper()
content := s.FileContent(filename)
for _, match := range matches {
if !strings.Contains(content, match) {
s.Fatalf("No match for %q in content for %s\n%s\n%q", match, filename, content, content)
for _, m := range matches {
lines := strings.Split(m, "\n")
for _, match := range lines {
match = strings.TrimSpace(match)
if !strings.Contains(content, match) {
s.Fatalf("No match for %q in content for %s\n%s\n%q", match, filename, content, content)
}
}
}
}
Expand Down

0 comments on commit 53da54c

Please sign in to comment.