Skip to content

Commit

Permalink
Add site.Store and hugo.Store and Shortcode.Store
Browse files Browse the repository at this point in the history
Closes #13021
  • Loading branch information
bep committed Nov 13, 2024
1 parent 3477d9f commit a7df536
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 13 deletions.
10 changes: 10 additions & 0 deletions common/hugo/hugo.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/gohugoio/hugo/common/hcontext"
"github.com/gohugoio/hugo/common/hexec"
"github.com/gohugoio/hugo/common/loggers"
"github.com/gohugoio/hugo/common/maps"
"github.com/gohugoio/hugo/hugofs/files"

"github.com/spf13/afero"
Expand All @@ -55,6 +56,8 @@ var (
vendorInfo string
)

var _ maps.StoreProvider = (*HugoInfo)(nil)

// HugoInfo contains information about the current Hugo environment
type HugoInfo struct {
CommitHash string
Expand All @@ -72,6 +75,8 @@ type HugoInfo struct {
conf ConfigProvider
deps []*Dependency

store *maps.Scratch

// Context gives access to some of the context scoped variables.
Context Context
}
Expand Down Expand Up @@ -116,6 +121,10 @@ func (i HugoInfo) Deps() []*Dependency {
return i.deps
}

func (i HugoInfo) Store() *maps.Scratch {
return i.store
}

// Deprecated: Use hugo.IsMultihost instead.
func (i HugoInfo) IsMultiHost() bool {
Deprecate("hugo.IsMultiHost", "Use hugo.IsMultihost instead.", "v0.124.0")
Expand Down Expand Up @@ -185,6 +194,7 @@ func NewInfo(conf ConfigProvider, deps []*Dependency) HugoInfo {
Environment: conf.Environment(),
conf: conf,
deps: deps,
store: maps.NewScratch(),
GoVersion: goVersion,
}
}
Expand Down
8 changes: 7 additions & 1 deletion common/maps/scratch.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,13 @@ import (
"github.com/gohugoio/hugo/common/math"
)

// Scratch is a writable context used for stateful operations in Page/Node rendering.
type StoreProvider interface {
// Store returns a Scratch that can be used to store temporary state.
// Store is not reset on server rebuilds.
Store() *Scratch
}

// Scratch is a writable context used for stateful build operations
type Scratch struct {
values map[string]any
mu sync.RWMutex
Expand Down
49 changes: 49 additions & 0 deletions hugolib/page_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1893,3 +1893,52 @@ func TestRenderWithoutArgument(t *testing.T) {

b.Assert(err, qt.IsNotNil)
}

// Issue #13021
func TestAllStores(t *testing.T) {
t.Parallel()

files := `
-- hugo.toml --
disableKinds = ["taxonomy", "term", "page", "section"]
disableLiveReload = true
-- content/_index.md --
---
title: "Home"
---
{{< s >}}
-- layouts/shortcodes/s.html --
{{ if not (.Store.Get "Shortcode") }}{{ .Store.Set "Shortcode" (printf "sh-%s" $.Page.Title) }}{{ end }}
Shortcode: {{ .Store.Get "Shortcode" }}|
-- layouts/index.html --
{{ .Content }}
{{ if not (.Store.Get "Page") }}{{ .Store.Set "Page" (printf "p-%s" $.Title) }}{{ end }}
{{ if not (hugo.Store.Get "Hugo") }}{{ hugo.Store.Set "Hugo" (printf "h-%s" $.Title) }}{{ end }}
{{ if not (site.Store.Get "Site") }}{{ site.Store.Set "Site" (printf "s-%s" $.Title) }}{{ end }}
Page: {{ .Store.Get "Page" }}|
Hugo: {{ hugo.Store.Get "Hugo" }}|
Site: {{ site.Store.Get "Site" }}|
`

b := TestRunning(t, files)

b.AssertFileContent("public/index.html",
`
Shortcode: sh-Home|
Page: p-Home|
Site: s-Home|
Hugo: h-Home|
`,
)

b.EditFileReplaceAll("content/_index.md", "Home", "Homer").Build()

b.AssertFileContent("public/index.html",
`
Shortcode: sh-Homer|
Page: p-Homer|
Site: s-Home|
Hugo: h-Home|
`,
)
}
34 changes: 25 additions & 9 deletions hugolib/shortcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,10 @@ import (
)

var (
_ urls.RefLinker = (*ShortcodeWithPage)(nil)
_ types.Unwrapper = (*ShortcodeWithPage)(nil)
_ text.Positioner = (*ShortcodeWithPage)(nil)
_ urls.RefLinker = (*ShortcodeWithPage)(nil)
_ types.Unwrapper = (*ShortcodeWithPage)(nil)
_ text.Positioner = (*ShortcodeWithPage)(nil)
_ maps.StoreProvider = (*ShortcodeWithPage)(nil)
)

// ShortcodeWithPage is the "." context in a shortcode template.
Expand All @@ -72,7 +73,7 @@ type ShortcodeWithPage struct {
posOffset int
pos text.Position

scratch *maps.Scratch
store *maps.Scratch
}

// InnerDeindent returns the (potentially de-indented) inner content of the shortcode.
Expand Down Expand Up @@ -124,13 +125,19 @@ func (scp *ShortcodeWithPage) RelRef(args map[string]any) (string, error) {
return scp.Page.RelRefFrom(args, scp)
}

// Store returns this shortcode's Store.
func (scp *ShortcodeWithPage) Store() *maps.Scratch {
if scp.store == nil {
scp.store = maps.NewScratch()
}
return scp.store
}

// Scratch returns a scratch-pad scoped for this shortcode. This can be used
// as a temporary storage for variables, counters etc.
// Deprecated: Use Store instead. Note that from the templates this should be considered a "soft deprecation".
func (scp *ShortcodeWithPage) Scratch() *maps.Scratch {
if scp.scratch == nil {
scp.scratch = maps.NewScratch()
}
return scp.scratch
return scp.Store()
}

// Get is a convenience method to look up shortcode parameters by its key.
Expand Down Expand Up @@ -399,7 +406,16 @@ func doRenderShortcode(
hasVariants = hasVariants || more
}

data := &ShortcodeWithPage{Ordinal: sc.ordinal, posOffset: sc.pos, indentation: sc.indentation, Params: sc.params, Page: newPageForShortcode(p), Parent: parent, Name: sc.name}
data := &ShortcodeWithPage{
Ordinal: sc.ordinal,
posOffset: sc.pos,
indentation: sc.indentation,
Params: sc.params,
Page: newPageForShortcode(p),
Parent: parent,
Name: sc.name,
}

if sc.params != nil {
data.IsNamedParams = reflect.TypeOf(sc.params).Kind() == reflect.Map
}
Expand Down
6 changes: 6 additions & 0 deletions hugolib/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ type Site struct {
language *langs.Language
languagei int
pageMap *pageMap
store *maps.Scratch

// The owning container.
h *HugoSites
Expand Down Expand Up @@ -248,6 +249,7 @@ func NewHugoSites(cfg deps.DepsCfg) (*HugoSites, error) {
language: language,
languagei: i,
frontmatterHandler: frontmatterHandler,
store: maps.NewScratch(),
}

if i == 0 {
Expand Down Expand Up @@ -614,6 +616,10 @@ func (s *Site) AllRegularPages() page.Pages {
return s.h.RegularPages()
}

func (s *Site) Store() *maps.Scratch {
return s.store
}

func (s *Site) CheckReady() {
if s.state != siteStateReady {
panic("this method cannot be called before the site is fully initialized")
Expand Down
4 changes: 1 addition & 3 deletions resources/page/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,9 +331,7 @@ type PageWithoutContent interface {
// Deprecated: From Hugo v0.138.0 this is just an alias for Store.
Scratch() *maps.Scratch

// Store returns a Scratch that can be used to store temporary state.
// In contrast to Scratch(), this Scratch is not reset on server rebuilds.
Store() *maps.Scratch
maps.StoreProvider

RelatedKeywordsProvider

Expand Down
10 changes: 10 additions & 0 deletions resources/page/site.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ type Site interface {
// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
RSSLink() template.URL

maps.StoreProvider

// For internal use only.
// This will panic if the site is not fully initialized.
// This is typically used to inform the user in the content adapter templates,
Expand Down Expand Up @@ -327,6 +329,10 @@ func (s *siteWrapper) RSSLink() template.URL {
return s.s.RSSLink()
}

func (s *siteWrapper) Store() *maps.Scratch {
return s.s.Store()
}

// For internal use only.
func (s *siteWrapper) ForEeachIdentityByName(name string, f func(identity.Identity) bool) {
s.s.(identity.ForEeachIdentityByNameProvider).ForEeachIdentityByName(name, f)
Expand Down Expand Up @@ -491,6 +497,10 @@ func (s testSite) RSSLink() template.URL {
return ""
}

func (s testSite) Store() *maps.Scratch {
return maps.NewScratch()
}

func (s testSite) CheckReady() {
}

Expand Down

2 comments on commit a7df536

@irkode
Copy link

@irkode irkode commented on a7df536 Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That sounds promising,

sadly no docs upbout - guess will coming, or is this just a technical thing?
site.Store sounds like a global exchange place maybe solving the $noop - page must be rendered thing
hugo.Store even exchange data between sites?

@bep
Copy link
Member Author

@bep bep commented on a7df536 Nov 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs is coming (see gohugoio/hugoDocs#2771), but it's fairly simple:

  • The difference is scope, shortcode, page, site, hugo -- the latter being the global scope.
  • These will only be reset once they're re-created, e.g. site.Store and hugo.Store will only be recreated on full rebuilds (config changes).

Please sign in to comment.
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy