mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
Allow for return partials with falsy arguments (#9298)
Partials with returns values are parsed, then inserted into a partial return wrapper via wrapInPartialReturnWrapper in order to assign the return value via *contextWrapper.Set. The predefined wrapper template for partials inserts a partial's nodes into a "with" template action in order to set dot to a *contextWrapper within the partial. However, because "with" is skipped if its argument is falsy, partials with falsy arguments were not being evaluated. This replaces the "with" action in the partial wrapper with a "range" action that isn't skipped if .Arg is falsy. Fixes #7528
This commit is contained in:
parent
8ee6de6d96
commit
5758c370ea
3 changed files with 34 additions and 21 deletions
|
@ -456,22 +456,34 @@ complex: 80: 80
|
||||||
`,
|
`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
c.Run("Zero argument", func(c *qt.C) {
|
// Issue 7528
|
||||||
b := newBuilder(c)
|
func TestPartialWithZeroedArgs(t *testing.T) {
|
||||||
|
|
||||||
b.WithTemplatesAdded(
|
b := newTestSitesBuilder(t)
|
||||||
"index.html", `
|
b.WithTemplatesAdded("index.html",
|
||||||
Test Partials With Return Values:
|
`
|
||||||
|
X{{ partial "retval" dict }}X
|
||||||
|
X{{ partial "retval" slice }}X
|
||||||
|
X{{ partial "retval" "" }}X
|
||||||
|
X{{ partial "retval" false }}X
|
||||||
|
X{{ partial "retval" 0 }}X
|
||||||
|
{{ define "partials/retval" }}
|
||||||
|
{{ return 123 }}
|
||||||
|
{{ end }}`)
|
||||||
|
|
||||||
add42: fail: {{ partial "add42.tpl" 0 }}
|
b.WithContentAdded("p.md", ``)
|
||||||
|
b.Build(BuildCfg{})
|
||||||
|
b.AssertFileContent("public/index.html",
|
||||||
|
`
|
||||||
|
X123X
|
||||||
|
X123X
|
||||||
|
X123X
|
||||||
|
X123X
|
||||||
|
X123X
|
||||||
|
`)
|
||||||
|
|
||||||
`,
|
|
||||||
)
|
|
||||||
|
|
||||||
e := b.CreateSites().BuildE(BuildCfg{})
|
|
||||||
b.Assert(e, qt.Not(qt.IsNil))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPartialCached(t *testing.T) {
|
func TestPartialCached(t *testing.T) {
|
||||||
|
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/hreflect"
|
|
||||||
texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
|
texttemplate "github.com/gohugoio/hugo/tpl/internal/go_templates/texttemplate"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/helpers"
|
"github.com/gohugoio/hugo/helpers"
|
||||||
|
@ -121,10 +120,6 @@ func (ns *Namespace) Include(name string, contextList ...interface{}) (interface
|
||||||
var w io.Writer
|
var w io.Writer
|
||||||
|
|
||||||
if info.HasReturn {
|
if info.HasReturn {
|
||||||
if !hreflect.IsTruthful(context) {
|
|
||||||
// TODO(bep) we need to fix this, but it is non-trivial.
|
|
||||||
return nil, errors.New("partial that returns a value needs a non-zero argument.")
|
|
||||||
}
|
|
||||||
// Wrap the context sent to the template to capture the return value.
|
// Wrap the context sent to the template to capture the return value.
|
||||||
// Note that the template is rewritten to make sure that the dot (".")
|
// Note that the template is rewritten to make sure that the dot (".")
|
||||||
// and the $ variable points to Arg.
|
// and the $ variable points to Arg.
|
||||||
|
|
|
@ -112,7 +112,11 @@ func getParseTree(templ tpl.Template) *parse.Tree {
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
partialReturnWrapperTempl = `{{ $_hugo_dot := $ }}{{ $ := .Arg }}{{ with .Arg }}{{ $_hugo_dot.Set ("PLACEHOLDER") }}{{ end }}`
|
// We parse this template and modify the nodes in order to assign
|
||||||
|
// the return value of a partial to a contextWrapper via Set. We use
|
||||||
|
// "range" over a one-element slice so we can shift dot to the
|
||||||
|
// partial's argument, Arg, while allowing Arg to be falsy.
|
||||||
|
partialReturnWrapperTempl = `{{ $_hugo_dot := $ }}{{ $ := .Arg }}{{ range (slice .Arg) }}{{ $_hugo_dot.Set ("PLACEHOLDER") }}{{ end }}`
|
||||||
)
|
)
|
||||||
|
|
||||||
var partialReturnWrapper *parse.ListNode
|
var partialReturnWrapper *parse.ListNode
|
||||||
|
@ -125,16 +129,18 @@ func init() {
|
||||||
partialReturnWrapper = templ.Tree.Root
|
partialReturnWrapper = templ.Tree.Root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// wrapInPartialReturnWrapper copies and modifies the parsed nodes of a
|
||||||
|
// predefined partial return wrapper to insert those of a user-defined partial.
|
||||||
func (c *templateContext) wrapInPartialReturnWrapper(n *parse.ListNode) *parse.ListNode {
|
func (c *templateContext) wrapInPartialReturnWrapper(n *parse.ListNode) *parse.ListNode {
|
||||||
wrapper := partialReturnWrapper.CopyList()
|
wrapper := partialReturnWrapper.CopyList()
|
||||||
withNode := wrapper.Nodes[2].(*parse.WithNode)
|
rangeNode := wrapper.Nodes[2].(*parse.RangeNode)
|
||||||
retn := withNode.List.Nodes[0]
|
retn := rangeNode.List.Nodes[0]
|
||||||
setCmd := retn.(*parse.ActionNode).Pipe.Cmds[0]
|
setCmd := retn.(*parse.ActionNode).Pipe.Cmds[0]
|
||||||
setPipe := setCmd.Args[1].(*parse.PipeNode)
|
setPipe := setCmd.Args[1].(*parse.PipeNode)
|
||||||
// Replace PLACEHOLDER with the real return value.
|
// Replace PLACEHOLDER with the real return value.
|
||||||
// Note that this is a PipeNode, so it will be wrapped in parens.
|
// Note that this is a PipeNode, so it will be wrapped in parens.
|
||||||
setPipe.Cmds = []*parse.CommandNode{c.returnNode}
|
setPipe.Cmds = []*parse.CommandNode{c.returnNode}
|
||||||
withNode.List.Nodes = append(n.Nodes, retn)
|
rangeNode.List.Nodes = append(n.Nodes, retn)
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue