Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/gohugoio/hugo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tpl
diff options
context:
space:
mode:
Diffstat (limited to 'tpl')
-rw-r--r--tpl/template.go88
-rw-r--r--tpl/template_ast_transformers_test.go5
-rw-r--r--tpl/template_funcs.go57
-rw-r--r--tpl/template_funcs_test.go27
-rw-r--r--tpl/template_test.go34
5 files changed, 113 insertions, 98 deletions
diff --git a/tpl/template.go b/tpl/template.go
index db6a912ff..b26490f0c 100644
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -30,10 +30,8 @@ import (
"github.com/yosssi/ace"
)
-var localTemplates *template.Template
-
-// TODO(bep) globals get rid of the reset of the jww.ERR etc.
-var tmpl *GoHTMLTemplate
+// TODO(bep) globals get rid of the rest of the jww.ERR etc.
+//var tmpl *GoHTMLTemplate
// TODO(bep) an interface with hundreds of methods ... remove it.
// And unexport most of these methods.
@@ -45,13 +43,13 @@ type Template interface {
GetClone() *template.Template
LoadTemplates(absPath string)
LoadTemplatesWithPrefix(absPath, prefix string)
- MarkReady()
AddTemplate(name, tpl string) error
AddTemplateFileWithMaster(name, overlayFilename, masterFilename string) error
AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error
AddInternalTemplate(prefix, name, tpl string) error
AddInternalShortcode(name, tpl string) error
PrintErrors()
+ Funcs(funcMap template.FuncMap)
}
type templateErr struct {
@@ -60,7 +58,8 @@ type templateErr struct {
}
type GoHTMLTemplate struct {
- template.Template
+ *template.Template
+
clone *template.Template
// a separate storage for the overlays created from cloned master templates.
@@ -69,41 +68,54 @@ type GoHTMLTemplate struct {
errors []*templateErr
+ funcster *templateFuncster
+
// TODO(bep) globals template
log *jww.Notepad
}
-// InitializeT resets the internal template state to its initial state
-func InitializeT(logger *jww.Notepad) *GoHTMLTemplate {
- tmpl = New(logger)
- return tmpl
-}
-
// New returns a new Hugo Template System
// with all the additional features, templates & functions
-func New(logger *jww.Notepad) *GoHTMLTemplate {
- var templates = &GoHTMLTemplate{
- Template: *template.New(""),
+func New(logger *jww.Notepad, withTemplate ...func(templ Template) error) *GoHTMLTemplate {
+ tmpl := &GoHTMLTemplate{
+ Template: template.New(""),
overlays: make(map[string]*template.Template),
errors: make([]*templateErr, 0),
log: logger,
}
- localTemplates = &templates.Template
+ tmpl.funcster = newTemplateFuncster(tmpl)
// The URL funcs in the funcMap is somewhat language dependent,
// so we need to wait until the language and site config is loaded.
- initFuncMap()
+ // TODO(bep) globals
+ tmpl.funcster.initFuncMap()
- for k, v := range funcMap {
+ // TODO(bep) globals
+ for k, v := range tmpl.funcster.funcMap {
amber.FuncMap[k] = v
}
- templates.Funcs(funcMap)
- templates.LoadEmbedded()
- return templates
+
+ tmpl.LoadEmbedded()
+
+ for _, wt := range withTemplate {
+ err := wt(tmpl)
+ if err != nil {
+ tmpl.errors = append(tmpl.errors, &templateErr{"init", err})
+ }
+
+ }
+
+ tmpl.markReady()
+
+ return tmpl
+}
+
+func (t *GoHTMLTemplate) Funcs(funcMap template.FuncMap) {
+ t.Template.Funcs(funcMap)
}
-func partial(name string, contextList ...interface{}) template.HTML {
+func (t *GoHTMLTemplate) partial(name string, contextList ...interface{}) template.HTML {
if strings.HasPrefix("partials/", name) {
name = name[8:]
}
@@ -114,16 +126,16 @@ func partial(name string, contextList ...interface{}) template.HTML {
} else {
context = contextList[0]
}
- return ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name)
+ return t.ExecuteTemplateToHTML(context, "partials/"+name, "theme/partials/"+name)
}
-func executeTemplate(context interface{}, w io.Writer, layouts ...string) {
+func (t *GoHTMLTemplate) executeTemplate(context interface{}, w io.Writer, layouts ...string) {
var worked bool
for _, layout := range layouts {
- templ := Lookup(layout)
+ templ := t.Lookup(layout)
if templ == nil {
layout += ".html"
- templ = Lookup(layout)
+ templ = t.Lookup(layout)
}
if templ != nil {
@@ -136,28 +148,20 @@ func executeTemplate(context interface{}, w io.Writer, layouts ...string) {
}
}
if !worked {
- tmpl.log.ERROR.Println("Unable to render", layouts)
- tmpl.log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
+ t.log.ERROR.Println("Unable to render", layouts)
+ t.log.ERROR.Println("Expecting to find a template in either the theme/layouts or /layouts in one of the following relative locations", layouts)
}
}
-func ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML {
+func (t *GoHTMLTemplate) ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML {
b := bp.GetBuffer()
defer bp.PutBuffer(b)
- executeTemplate(context, b, layouts...)
+ t.executeTemplate(context, b, layouts...)
return template.HTML(b.String())
}
-func Lookup(name string) *template.Template {
- return tmpl.Lookup(name)
-}
-
func (t *GoHTMLTemplate) Lookup(name string) *template.Template {
- if templ := localTemplates.Lookup(name); templ != nil {
- return templ
- }
-
if t.overlays != nil {
if templ, ok := t.overlays[name]; ok {
return templ
@@ -183,9 +187,9 @@ func (t *GoHTMLTemplate) LoadEmbedded() {
t.EmbedTemplates()
}
-// MarkReady marks the template as "ready for execution". No changes allowed
+// markReady marks the template as "ready for execution". No changes allowed
// after this is set.
-func (t *GoHTMLTemplate) MarkReady() {
+func (t *GoHTMLTemplate) markReady() {
if t.clone == nil {
t.clone = template.Must(t.Template.Clone())
}
@@ -522,7 +526,7 @@ func (t *GoHTMLTemplate) LoadTemplates(absPath string) {
}
func (t *GoHTMLTemplate) PrintErrors() {
- for _, e := range t.errors {
- t.log.ERROR.Println(e.err)
+ for i, e := range t.errors {
+ t.log.ERROR.Println(i, ":", e.err)
}
}
diff --git a/tpl/template_ast_transformers_test.go b/tpl/template_ast_transformers_test.go
index c78c521c9..8ffb1cab1 100644
--- a/tpl/template_ast_transformers_test.go
+++ b/tpl/template_ast_transformers_test.go
@@ -18,7 +18,6 @@ import (
"html/template"
- jww "github.com/spf13/jwalterweatherman"
"github.com/stretchr/testify/require"
)
@@ -265,7 +264,3 @@ P2: {{ .Params.LOWER }}
require.Contains(t, result, "P1: P1L")
require.Contains(t, result, "P2: P1L")
}
-
-func init() {
- jww.SetStdoutThreshold(jww.LevelCritical)
-}
diff --git a/tpl/template_funcs.go b/tpl/template_funcs.go
index 596902fcd..6c8a9957e 100644
--- a/tpl/template_funcs.go
+++ b/tpl/template_funcs.go
@@ -54,9 +54,19 @@ import (
_ "image/png"
)
-var (
- funcMap template.FuncMap
-)
+// Some of the template funcs are'nt entirely stateless.
+type templateFuncster struct {
+ t *GoHTMLTemplate
+ funcMap template.FuncMap
+ cachedPartials partialCache
+}
+
+func newTemplateFuncster(t *GoHTMLTemplate) *templateFuncster {
+ return &templateFuncster{
+ t: t,
+ cachedPartials: partialCache{p: make(map[string]template.HTML)},
+ }
+}
// eq returns the boolean truth of arg1 == arg2.
func eq(x, y interface{}) bool {
@@ -1003,7 +1013,7 @@ func where(seq, key interface{}, args ...interface{}) (interface{}, error) {
}
// apply takes a map, array, or slice and returns a new slice with the function fname applied over it.
-func apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) {
+func (tf *templateFuncster) apply(seq interface{}, fname string, args ...interface{}) (interface{}, error) {
if seq == nil {
return make([]interface{}, 0), nil
}
@@ -1018,7 +1028,7 @@ func apply(seq interface{}, fname string, args ...interface{}) (interface{}, err
return nil, errors.New("can't iterate over a nil value")
}
- fn, found := funcMap[fname]
+ fn, found := tf.funcMap[fname]
if !found {
return nil, errors.New("can't find function " + fname)
}
@@ -1518,41 +1528,39 @@ type partialCache struct {
// Get retrieves partial output from the cache based upon the partial name.
// If the partial is not found in the cache, the partial is rendered and added
// to the cache.
-func (c *partialCache) Get(key, name string, context interface{}) (p template.HTML) {
+func (tf *templateFuncster) Get(key, name string, context interface{}) (p template.HTML) {
var ok bool
- c.RLock()
- p, ok = c.p[key]
- c.RUnlock()
+ tf.cachedPartials.RLock()
+ p, ok = tf.cachedPartials.p[key]
+ tf.cachedPartials.RUnlock()
if ok {
return p
}
- c.Lock()
- if p, ok = c.p[key]; !ok {
- p = partial(name, context)
- c.p[key] = p
+ tf.cachedPartials.Lock()
+ if p, ok = tf.cachedPartials.p[key]; !ok {
+ p = tf.t.partial(name, context)
+ tf.cachedPartials.p[key] = p
}
- c.Unlock()
+ tf.cachedPartials.Unlock()
return p
}
-var cachedPartials = partialCache{p: make(map[string]template.HTML)}
-
// partialCached executes and caches partial templates. An optional variant
// string parameter (a string slice actually, but be only use a variadic
// argument to make it optional) can be passed so that a given partial can have
// multiple uses. The cache is created with name+variant as the key.
-func partialCached(name string, context interface{}, variant ...string) template.HTML {
+func (tf *templateFuncster) partialCached(name string, context interface{}, variant ...string) template.HTML {
key := name
if len(variant) > 0 {
for i := 0; i < len(variant); i++ {
key += variant[i]
}
}
- return cachedPartials.Get(key, name, context)
+ return tf.Get(key, name, context)
}
// regexpCache represents a cache of regexp objects protected by a mutex.
@@ -2090,8 +2098,8 @@ func getenv(key interface{}) (string, error) {
return os.Getenv(skey), nil
}
-func initFuncMap() {
- funcMap = template.FuncMap{
+func (tf *templateFuncster) initFuncMap() {
+ funcMap := template.FuncMap{
"absURL": absURL,
"absLangURL": func(i interface{}) (template.HTML, error) {
s, err := cast.ToStringE(i)
@@ -2102,7 +2110,7 @@ func initFuncMap() {
},
"add": func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '+') },
"after": after,
- "apply": apply,
+ "apply": tf.apply,
"base64Decode": base64Decode,
"base64Encode": base64Encode,
"chomp": chomp,
@@ -2147,8 +2155,8 @@ func initFuncMap() {
"mul": func(a, b interface{}) (interface{}, error) { return helpers.DoArithmetic(a, b, '*') },
"ne": ne,
"now": func() time.Time { return time.Now() },
- "partial": partial,
- "partialCached": partialCached,
+ "partial": tf.t.partial,
+ "partialCached": tf.partialCached,
"plainify": plainify,
"pluralize": pluralize,
"querify": querify,
@@ -2195,4 +2203,7 @@ func initFuncMap() {
"i18n": i18nTranslate,
"T": i18nTranslate,
}
+
+ tf.funcMap = funcMap
+ tf.t.Funcs(funcMap)
}
diff --git a/tpl/template_funcs_test.go b/tpl/template_funcs_test.go
index c3fbb6ad8..6bbbf0146 100644
--- a/tpl/template_funcs_test.go
+++ b/tpl/template_funcs_test.go
@@ -1960,40 +1960,43 @@ func TestMarkdownify(t *testing.T) {
}
func TestApply(t *testing.T) {
+
+ f := newTestFuncster()
+
strings := []interface{}{"a\n", "b\n"}
noStringers := []interface{}{tstNoStringer{}, tstNoStringer{}}
- chomped, _ := apply(strings, "chomp", ".")
+ chomped, _ := f.apply(strings, "chomp", ".")
assert.Equal(t, []interface{}{template.HTML("a"), template.HTML("b")}, chomped)
- chomped, _ = apply(strings, "chomp", "c\n")
+ chomped, _ = f.apply(strings, "chomp", "c\n")
assert.Equal(t, []interface{}{template.HTML("c"), template.HTML("c")}, chomped)
- chomped, _ = apply(nil, "chomp", ".")
+ chomped, _ = f.apply(nil, "chomp", ".")
assert.Equal(t, []interface{}{}, chomped)
- _, err := apply(strings, "apply", ".")
+ _, err := f.apply(strings, "apply", ".")
if err == nil {
t.Errorf("apply with apply should fail")
}
var nilErr *error
- _, err = apply(nilErr, "chomp", ".")
+ _, err = f.apply(nilErr, "chomp", ".")
if err == nil {
t.Errorf("apply with nil in seq should fail")
}
- _, err = apply(strings, "dobedobedo", ".")
+ _, err = f.apply(strings, "dobedobedo", ".")
if err == nil {
t.Errorf("apply with unknown func should fail")
}
- _, err = apply(noStringers, "chomp", ".")
+ _, err = f.apply(noStringers, "chomp", ".")
if err == nil {
t.Errorf("apply when func fails should fail")
}
- _, err = apply(tstNoStringer{}, "chomp", ".")
+ _, err = f.apply(tstNoStringer{}, "chomp", ".")
if err == nil {
t.Errorf("apply with non-sequence should fail")
}
@@ -2780,7 +2783,6 @@ func TestPartialCached(t *testing.T) {
data.Params = map[string]interface{}{"langCode": "en"}
tstInitTemplates()
- InitializeT(logger)
for i, tc := range testCases {
var tmp string
if tc.variant != "" {
@@ -2831,7 +2833,6 @@ func TestPartialCached(t *testing.T) {
}
func BenchmarkPartial(b *testing.B) {
- InitializeT(logger)
tmpl, err := New(logger).New("testroot").Parse(`{{ partial "bench1" . }}`)
if err != nil {
b.Fatalf("unable to create new html template: %s", err)
@@ -2851,7 +2852,6 @@ func BenchmarkPartial(b *testing.B) {
}
func BenchmarkPartialCached(b *testing.B) {
- InitializeT(logger)
tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . }}`)
if err != nil {
b.Fatalf("unable to create new html template: %s", err)
@@ -2871,7 +2871,6 @@ func BenchmarkPartialCached(b *testing.B) {
}
func BenchmarkPartialCachedVariants(b *testing.B) {
- InitializeT(logger)
tmpl, err := New(logger).New("testroot").Parse(`{{ partialCached "bench1" . "header" }}`)
if err != nil {
b.Fatalf("unable to create new html template: %s", err)
@@ -2889,3 +2888,7 @@ func BenchmarkPartialCachedVariants(b *testing.B) {
buf.Reset()
}
}
+
+func newTestFuncster() *templateFuncster {
+ return New(logger).funcster
+}
diff --git a/tpl/template_test.go b/tpl/template_test.go
index 2f4946598..cf691858b 100644
--- a/tpl/template_test.go
+++ b/tpl/template_test.go
@@ -55,8 +55,6 @@ html lang=en
for _, root := range []string{"", os.TempDir()} {
- templ := New(logger)
-
basePath := this.basePath
innerPath := this.innerPath
@@ -70,17 +68,20 @@ html lang=en
d := "DATA"
- err := templ.AddAceTemplate("mytemplate.ace", basePath, innerPath,
- []byte(this.baseContent), []byte(this.innerContent))
+ templ := New(logger, func(templ Template) error {
+ return templ.AddAceTemplate("mytemplate.ace", basePath, innerPath,
+ []byte(this.baseContent), []byte(this.innerContent))
- if err != nil && this.expectErr == 0 {
- t.Errorf("Test %d with root '%s' errored: %s", i, root, err)
- } else if err == nil && this.expectErr == 1 {
+ })
+
+ if len(templ.errors) > 0 && this.expectErr == 0 {
+ t.Errorf("Test %d with root '%s' errored: %v", i, root, templ.errors)
+ } else if len(templ.errors) == 0 && this.expectErr == 1 {
t.Errorf("#1 Test %d with root '%s' should have errored", i, root)
}
var buff bytes.Buffer
- err = templ.ExecuteTemplate(&buff, "mytemplate.html", d)
+ err := templ.ExecuteTemplate(&buff, "mytemplate.html", d)
if err != nil && this.expectErr == 0 {
t.Errorf("Test %d with root '%s' errored: %s", i, root, err)
@@ -245,7 +246,6 @@ func TestTplGoFuzzReports(t *testing.T) {
// Issue #1095
{"{{apply .C \"urlize\" " +
"\".\"}}", 2}} {
- templ := New(logger)
d := &Data{
A: 42,
@@ -258,15 +258,17 @@ func TestTplGoFuzzReports(t *testing.T) {
H: "a,b,c,d,e,f",
}
- err := templ.AddTemplate("fuzz", this.data)
+ templ := New(logger, func(templ Template) error {
+ return templ.AddTemplate("fuzz", this.data)
- if err != nil && this.expectErr == 0 {
- t.Fatalf("Test %d errored: %s", i, err)
- } else if err == nil && this.expectErr == 1 {
- t.Fatalf("#1 Test %d should have errored", i)
- }
+ })
- err = templ.ExecuteTemplate(ioutil.Discard, "fuzz", d)
+ if len(templ.errors) > 0 && this.expectErr == 0 {
+ t.Errorf("Test %d errored: %v", i, templ.errors)
+ } else if len(templ.errors) == 0 && this.expectErr == 1 {
+ t.Errorf("#1 Test %d should have errored", i)
+ }
+ err := templ.ExecuteTemplate(ioutil.Discard, "fuzz", d)
if err != nil && this.expectErr == 0 {
t.Fatalf("Test %d errored: %s", i, err)