summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--hugolib/page.go14
-rw-r--r--hugolib/page_test.go179
-rw-r--r--resources/page/page_lazy_contentprovider.go101
3 files changed, 294 insertions, 0 deletions
diff --git a/hugolib/page.go b/hugolib/page.go
index d2d962044..509a083e8 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -940,6 +940,20 @@ func (p *pageState) shiftToOutputFormat(isRenderingSite bool, idx int) error {
panic(fmt.Sprintf("pageOutput is nil for output idx %d", idx))
}
+ // We attempt to assign pageContentOutputs while preparing each site
+ // for rendering and before rendering each site. This lets us share
+ // content between page outputs to conserve resources. But if a template
+ // unexpectedly calls a method of a ContentProvider that is not yet
+ // initialized, we assign a LazyContentProvider that performs the
+ // initialization just in time.
+ p.pageOutput.ContentProvider = page.NewLazyContentProvider(func() (page.ContentProvider, error) {
+ cp, err := newPageContentOutput(p, p.pageOutput)
+ if err != nil {
+ return nil, err
+ }
+ return cp, nil
+ })
+
// Reset any built paginator. This will trigger when re-rendering pages in
// server mode.
if isRenderingSite && p.pageOutput.paginator != nil && p.pageOutput.paginator.current != nil {
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index 7d55787c8..fc01bbf25 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -36,6 +36,7 @@ import (
"github.com/gohugoio/hugo/resources/page"
"github.com/gohugoio/hugo/resources/resource"
"github.com/spf13/afero"
+ "github.com/spf13/jwalterweatherman"
qt "github.com/frankban/quicktest"
"github.com/gohugoio/hugo/deps"
@@ -767,6 +768,184 @@ Here is the last report for commits in the year 2016. It covers hrev50718-hrev50
`)
}
+// Issue 8919
+func TestContentProviderWithCustomOutputFormat(t *testing.T) {
+ b := newTestSitesBuilder(t)
+ b.WithLogger(loggers.NewBasicLoggerForWriter(jwalterweatherman.LevelDebug, os.Stderr))
+ b.WithConfigFile("toml", `baseURL = 'http://example.org/'
+title = 'My New Hugo Site'
+
+timeout = 600000 # ten minutes in case we want to pause and debug
+
+defaultContentLanguage = "en"
+
+[languages]
+ [languages.en]
+ title = "Repro"
+ languageName = "English"
+ contentDir = "content/en"
+
+ [languages.zh_CN]
+ title = "Repro"
+ languageName = "简体中文"
+ contentDir = "content/zh_CN"
+
+[outputFormats]
+ [outputFormats.metadata]
+ baseName = "metadata"
+ mediaType = "text/html"
+ isPlainText = true
+ notAlternative = true
+
+[outputs]
+ home = ["HTML", "metadata"]`)
+
+ b.WithTemplates("home.metadata.html", `<h2>Translations metadata</h2>
+<ul>
+{{ $p := .Page }}
+{{ range $p.Translations}}
+<li>Title: {{ .Title }}, {{ .Summary }}</li>
+<li>Content: {{ .Content }}</li>
+<li>Plain: {{ .Plain }}</li>
+<li>PlainWords: {{ .PlainWords }}</li>
+<li>Summary: {{ .Summary }}</li>
+<li>Truncated: {{ .Truncated }}</li>
+<li>FuzzyWordCount: {{ .FuzzyWordCount }}</li>
+<li>ReadingTime: {{ .ReadingTime }}</li>
+<li>Len: {{ .Len }}</li>
+{{ end }}
+</ul>`)
+
+ b.WithTemplates("_default/baseof.html", `<html>
+
+<body>
+ {{ block "main" . }}{{ end }}
+</body>
+
+</html>`)
+
+ b.WithTemplates("_default/home.html", `{{ define "main" }}
+<h2>Translations</h2>
+<ul>
+{{ $p := .Page }}
+{{ range $p.Translations}}
+<li>Title: {{ .Title }}, {{ .Summary }}</li>
+<li>Content: {{ .Content }}</li>
+<li>Plain: {{ .Plain }}</li>
+<li>PlainWords: {{ .PlainWords }}</li>
+<li>Summary: {{ .Summary }}</li>
+<li>Truncated: {{ .Truncated }}</li>
+<li>FuzzyWordCount: {{ .FuzzyWordCount }}</li>
+<li>ReadingTime: {{ .ReadingTime }}</li>
+<li>Len: {{ .Len }}</li>
+{{ end }}
+</ul>
+{{ end }}`)
+
+ b.WithContent("en/_index.md", `---
+title: Title (en)
+summary: Summary (en)
+---
+
+Here is some content.
+`)
+
+ b.WithContent("zh_CN/_index.md", `---
+title: Title (zh)
+summary: Summary (zh)
+---
+
+这是一些内容
+`)
+
+ b.Build(BuildCfg{})
+
+ b.AssertFileContent("public/index.html", `<html>
+
+<body>
+
+<h2>Translations</h2>
+<ul>
+
+
+<li>Title: Title (zh), Summary (zh)</li>
+<li>Content: <p>这是一些内容</p>
+</li>
+<li>Plain: 这是一些内容
+</li>
+<li>PlainWords: [这是一些内容]</li>
+<li>Summary: Summary (zh)</li>
+<li>Truncated: false</li>
+<li>FuzzyWordCount: 100</li>
+<li>ReadingTime: 1</li>
+<li>Len: 26</li>
+
+</ul>
+
+</body>
+
+</html>`)
+ b.AssertFileContent("public/metadata.html", `<h2>Translations metadata</h2>
+<ul>
+
+
+<li>Title: Title (zh), Summary (zh)</li>
+<li>Content: <p>这是一些内容</p>
+</li>
+<li>Plain: 这是一些内容
+</li>
+<li>PlainWords: [这是一些内容]</li>
+<li>Summary: Summary (zh)</li>
+<li>Truncated: false</li>
+<li>FuzzyWordCount: 100</li>
+<li>ReadingTime: 1</li>
+<li>Len: 26</li>
+
+</ul>`)
+ b.AssertFileContent("public/zh_cn/index.html", `<html>
+
+<body>
+
+<h2>Translations</h2>
+<ul>
+
+
+<li>Title: Title (en), Summary (en)</li>
+<li>Content: <p>Here is some content.</p>
+</li>
+<li>Plain: Here is some content.
+</li>
+<li>PlainWords: [Here is some content.]</li>
+<li>Summary: Summary (en)</li>
+<li>Truncated: false</li>
+<li>FuzzyWordCount: 100</li>
+<li>ReadingTime: 1</li>
+<li>Len: 29</li>
+
+</ul>
+
+</body>
+
+</html>`)
+ b.AssertFileContent("public/zh_cn/metadata.html", `<h2>Translations metadata</h2>
+<ul>
+
+
+<li>Title: Title (en), Summary (en)</li>
+<li>Content: <p>Here is some content.</p>
+</li>
+<li>Plain: Here is some content.
+</li>
+<li>PlainWords: [Here is some content.]</li>
+<li>Summary: Summary (en)</li>
+<li>Truncated: false</li>
+<li>FuzzyWordCount: 100</li>
+<li>ReadingTime: 1</li>
+<li>Len: 29</li>
+
+</ul>`)
+}
+
func TestPageWithDate(t *testing.T) {
t.Parallel()
cfg, fs := newTestCfg()
diff --git a/resources/page/page_lazy_contentprovider.go b/resources/page/page_lazy_contentprovider.go
new file mode 100644
index 000000000..d8d92d7cd
--- /dev/null
+++ b/resources/page/page_lazy_contentprovider.go
@@ -0,0 +1,101 @@
+// 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 page
+
+import (
+ "html/template"
+
+ "github.com/gohugoio/hugo/lazy"
+)
+
+// LazyContentProvider initializes itself when read. Each method of the
+// ContentProvider interface initializes a content provider and shares it
+// with other methods.
+//
+// Used in cases where we cannot guarantee whether the content provider
+// will be needed. Must create via NewLazyContentProvider.
+type LazyContentProvider struct {
+ init *lazy.Init
+ cp ContentProvider
+}
+
+// NewLazyContentProvider returns a LazyContentProvider initialized with
+// function f. The resulting LazyContentProvider calls f in order to
+// retrieve a ContentProvider
+func NewLazyContentProvider(f func() (ContentProvider, error)) *LazyContentProvider {
+ lcp := LazyContentProvider{
+ init: lazy.New(),
+ cp: NopPage,
+ }
+ lcp.init.Add(func() (interface{}, error) {
+ cp, err := f()
+ if err != nil {
+ return nil, err
+ }
+ lcp.cp = cp
+ return nil, nil
+ })
+ return &lcp
+}
+
+func (lcp *LazyContentProvider) Content() (interface{}, error) {
+ lcp.init.Do()
+ return lcp.cp.Content()
+}
+
+func (lcp *LazyContentProvider) Plain() string {
+ lcp.init.Do()
+ return lcp.cp.Plain()
+}
+
+func (lcp *LazyContentProvider) PlainWords() []string {
+ lcp.init.Do()
+ return lcp.cp.PlainWords()
+}
+
+func (lcp *LazyContentProvider) Summary() template.HTML {
+ lcp.init.Do()
+ return lcp.cp.Summary()
+
+}
+
+func (lcp *LazyContentProvider) Truncated() bool {
+ lcp.init.Do()
+ return lcp.cp.Truncated()
+
+}
+
+func (lcp *LazyContentProvider) FuzzyWordCount() int {
+ lcp.init.Do()
+ return lcp.cp.FuzzyWordCount()
+
+}
+
+func (lcp *LazyContentProvider) WordCount() int {
+ lcp.init.Do()
+ return lcp.cp.WordCount()
+
+}
+
+func (lcp *LazyContentProvider) ReadingTime() int {
+ lcp.init.Do()
+ return lcp.cp.ReadingTime()
+
+}
+
+func (lcp *LazyContentProvider) Len() int {
+ lcp.init.Do()
+ return lcp.cp.Len()
+
+}