summaryrefslogtreecommitdiffstats
path: root/tpl/tplimpl
diff options
context:
space:
mode:
Diffstat (limited to 'tpl/tplimpl')
-rw-r--r--tpl/tplimpl/templateFuncster.go6
-rw-r--r--tpl/tplimpl/template_funcs.go75
-rw-r--r--tpl/tplimpl/template_funcs_test.go149
-rw-r--r--tpl/tplimpl/template_test.go127
4 files changed, 13 insertions, 344 deletions
diff --git a/tpl/tplimpl/templateFuncster.go b/tpl/tplimpl/templateFuncster.go
index c37d7f937..656cd89d3 100644
--- a/tpl/tplimpl/templateFuncster.go
+++ b/tpl/tplimpl/templateFuncster.go
@@ -25,16 +25,14 @@ import (
// Some of the template funcs are'nt entirely stateless.
type templateFuncster struct {
- funcMap template.FuncMap
- cachedPartials partialCache
+ funcMap template.FuncMap
*deps.Deps
}
func newTemplateFuncster(deps *deps.Deps) *templateFuncster {
return &templateFuncster{
- Deps: deps,
- cachedPartials: partialCache{p: make(map[string]interface{})},
+ Deps: deps,
}
}
diff --git a/tpl/tplimpl/template_funcs.go b/tpl/tplimpl/template_funcs.go
index d1a2dd73a..089463a1e 100644
--- a/tpl/tplimpl/template_funcs.go
+++ b/tpl/tplimpl/template_funcs.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The Hugo Authors. All rights reserved.
+// Copyright 2017-present The Hugo Authors. All rights reserved.
//
// Portions Copyright The Go Authors.
@@ -16,24 +16,24 @@
package tplimpl
import (
- "fmt"
"html/template"
- "sync"
- "github.com/spf13/cast"
"github.com/spf13/hugo/tpl/internal"
// Init the namespaces
+ _ "github.com/spf13/hugo/tpl/cast"
_ "github.com/spf13/hugo/tpl/collections"
_ "github.com/spf13/hugo/tpl/compare"
_ "github.com/spf13/hugo/tpl/crypto"
_ "github.com/spf13/hugo/tpl/data"
_ "github.com/spf13/hugo/tpl/encoding"
+ _ "github.com/spf13/hugo/tpl/fmt"
_ "github.com/spf13/hugo/tpl/images"
_ "github.com/spf13/hugo/tpl/inflect"
_ "github.com/spf13/hugo/tpl/lang"
_ "github.com/spf13/hugo/tpl/math"
_ "github.com/spf13/hugo/tpl/os"
+ _ "github.com/spf13/hugo/tpl/partials"
_ "github.com/spf13/hugo/tpl/safe"
_ "github.com/spf13/hugo/tpl/strings"
_ "github.com/spf13/hugo/tpl/time"
@@ -41,74 +41,15 @@ import (
_ "github.com/spf13/hugo/tpl/urls"
)
-// 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 (t *templateFuncster) Get(key, name string, context interface{}) (p interface{}, err error) {
- var ok bool
-
- t.cachedPartials.RLock()
- p, ok = t.cachedPartials.p[key]
- t.cachedPartials.RUnlock()
-
- if ok {
- return
- }
-
- t.cachedPartials.Lock()
- if p, ok = t.cachedPartials.p[key]; !ok {
- t.cachedPartials.Unlock()
- p, err = t.partial(name, context)
-
- t.cachedPartials.Lock()
- t.cachedPartials.p[key] = p
-
- }
- t.cachedPartials.Unlock()
-
- return
-}
-
-// partialCache represents a cache of partials protected by a mutex.
-type partialCache struct {
- sync.RWMutex
- p map[string]interface{}
-}
-
-// 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 (t *templateFuncster) partialCached(name string, context interface{}, variant ...string) (interface{}, error) {
- key := name
- if len(variant) > 0 {
- for i := 0; i < len(variant); i++ {
- key += variant[i]
- }
- }
- return t.Get(key, name, context)
-}
-
func (t *templateFuncster) initFuncMap() {
- funcMap := template.FuncMap{
- // Namespaces
- //"time": t.time.Namespace,
- "int": func(v interface{}) (int, error) { return cast.ToIntE(v) },
- "partial": t.partial,
- "partialCached": t.partialCached,
- "print": fmt.Sprint,
- "printf": fmt.Sprintf,
- "println": fmt.Sprintln,
- "string": func(v interface{}) (string, error) { return cast.ToStringE(v) },
- "urlize": t.PathSpec.URLize,
- }
+ funcMap := template.FuncMap{}
// Merge the namespace funcs
for _, nsf := range internal.TemplateFuncsNamespaceRegistry {
ns := nsf(t.Deps)
- // TODO(bep) namespace ns.Context is a dummy func just to make this work.
- // Consider if we can add this context to the rendering context in an easy
- // way to make this cleaner. Maybe.
+ if _, exists := funcMap[ns.Name]; exists {
+ panic(ns.Name + " is a duplicate template func")
+ }
funcMap[ns.Name] = ns.Context
for k, v := range ns.Aliases {
funcMap[k] = v
diff --git a/tpl/tplimpl/template_funcs_test.go b/tpl/tplimpl/template_funcs_test.go
index 186da511b..b0a6e87b7 100644
--- a/tpl/tplimpl/template_funcs_test.go
+++ b/tpl/tplimpl/template_funcs_test.go
@@ -18,7 +18,6 @@ import (
"fmt"
"path/filepath"
"reflect"
- "strings"
"testing"
"io/ioutil"
@@ -92,6 +91,7 @@ func TestTemplateFuncsExamples(t *testing.T) {
in, expected := example[0], example[1]
d.WithTemplate = func(templ tpl.TemplateHandler) error {
require.NoError(t, templ.AddTemplate("test", in))
+ require.NoError(t, templ.AddTemplate("partials/header.html", "<title>Hugo Rocks!</title>"))
return nil
}
require.NoError(t, d.LoadResources())
@@ -106,134 +106,8 @@ func TestTemplateFuncsExamples(t *testing.T) {
}
-func TestFuncsInTemplate(t *testing.T) {
- t.Parallel()
-
- workingDir := "/home/hugo"
-
- v := viper.New()
-
- v.Set("workingDir", workingDir)
- v.Set("multilingual", true)
-
- fs := hugofs.NewMem(v)
-
- afero.WriteFile(fs.Source, filepath.Join(workingDir, "README.txt"), []byte("Hugo Rocks!"), 0755)
-
- // Add the examples from the docs: As a smoke test and to make sure the examples work.
- // TODO(bep): docs: fix title example
- // TODO(bep) namespace remove when done
- in :=
- `
-crypto.MD5: {{ crypto.MD5 "Hello world, gophers!" }}
-dateFormat: {{ dateFormat "Monday, Jan 2, 2006" "2015-01-21" }}
-htmlEscape 1: {{ htmlEscape "Cathal Garvey & The Sunshine Band <cathal@foo.bar>" | safeHTML}}
-htmlEscape 2: {{ htmlEscape "Cathal Garvey & The Sunshine Band <cathal@foo.bar>"}}
-htmlUnescape 1: {{htmlUnescape "Cathal Garvey &amp; The Sunshine Band &lt;cathal@foo.bar&gt;" | safeHTML}}
-htmlUnescape 2: {{"Cathal Garvey &amp;amp; The Sunshine Band &amp;lt;cathal@foo.bar&amp;gt;" | htmlUnescape | htmlUnescape | safeHTML}}
-htmlUnescape 3: {{"Cathal Garvey &amp;amp; The Sunshine Band &amp;lt;cathal@foo.bar&amp;gt;" | htmlUnescape | htmlUnescape }}
-htmlUnescape 4: {{ htmlEscape "Cathal Garvey & The Sunshine Band <cathal@foo.bar>" | htmlUnescape | safeHTML }}
-htmlUnescape 5: {{ htmlUnescape "Cathal Garvey &amp; The Sunshine Band &lt;cathal@foo.bar&gt;" | htmlEscape | safeHTML }}
-print: {{ print "works!" }}
-printf: {{ printf "%s!" "works" }}
-println: {{ println "works!" -}}
-strings.TrimPrefix: {{ strings.TrimPrefix "Goodbye,, world!" "Goodbye," }}
-urlize: {{ "Bat Man" | urlize }}
-`
-
- expected := `
-crypto.MD5: b3029f756f98f79e7f1b7f1d1f0dd53b
-dateFormat: Wednesday, Jan 21, 2015
-htmlEscape 1: Cathal Garvey &amp; The Sunshine Band &lt;cathal@foo.bar&gt;
-htmlEscape 2: Cathal Garvey &amp;amp; The Sunshine Band &amp;lt;cathal@foo.bar&amp;gt;
-htmlUnescape 1: Cathal Garvey & The Sunshine Band <cathal@foo.bar>
-htmlUnescape 2: Cathal Garvey & The Sunshine Band <cathal@foo.bar>
-htmlUnescape 3: Cathal Garvey &amp; The Sunshine Band &lt;cathal@foo.bar&gt;
-htmlUnescape 4: Cathal Garvey & The Sunshine Band <cathal@foo.bar>
-htmlUnescape 5: Cathal Garvey &amp; The Sunshine Band &lt;cathal@foo.bar&gt;
-print: works!
-printf: works!
-println: works!
-strings.TrimPrefix: , world!
-urlize: bat-man
-`
-
- var b bytes.Buffer
-
- var data struct {
- Title string
- Section string
- Params map[string]interface{}
- }
-
- data.Title = "**BatMan**"
- data.Section = "blog"
- data.Params = map[string]interface{}{"langCode": "en"}
-
- v.Set("baseURL", "http://mysite.com/hugo/")
- v.Set("CurrentContentLanguage", helpers.NewLanguage("en", v))
-
- config := newDepsConfig(v)
- config.WithTemplate = func(templ tpl.TemplateHandler) error {
- if err := templ.AddTemplate("test", in); err != nil {
- t.Fatal("Got error on parse", err)
- }
- return nil
- }
- config.Fs = fs
-
- d, err := deps.New(config)
- if err != nil {
- t.Fatal(err)
- }
-
- if err := d.LoadResources(); err != nil {
- t.Fatal(err)
- }
-
- err = d.Tmpl.Lookup("test").Execute(&b, &data)
-
- if err != nil {
- t.Fatal("Got error on execute", err)
- }
-
- if b.String() != expected {
- sl1 := strings.Split(b.String(), "\n")
- sl2 := strings.Split(expected, "\n")
- t.Errorf("Diff:\n%q", helpers.DiffStringSlices(sl1, sl2))
- }
-}
-
-func TestDefault(t *testing.T) {
- t.Parallel()
- for i, this := range []struct {
- input interface{}
- tpl string
- expected string
- ok bool
- }{
- {map[string]string{"foo": "bar"}, `{{ index . "foo" | default "nope" }}`, `bar`, true},
- {map[string]string{"foo": "pop"}, `{{ index . "bar" | default "nada" }}`, `nada`, true},
- {map[string]string{"foo": "cat"}, `{{ default "nope" .foo }}`, `cat`, true},
- {map[string]string{"foo": "dog"}, `{{ default "nope" .foo "extra" }}`, ``, false},
- {map[string]interface{}{"images": []string{}}, `{{ default "default.jpg" (index .images 0) }}`, `default.jpg`, true},
- } {
-
- tmpl := newTestTemplate(t, "test", this.tpl)
-
- buf := new(bytes.Buffer)
- err := tmpl.Execute(buf, this.input)
- if (err == nil) != this.ok {
- t.Errorf("[%d] execute template returned unexpected error: %s", i, err)
- continue
- }
-
- if buf.String() != this.expected {
- t.Errorf("[%d] execute template got %v, but expected %v", i, buf.String(), this.expected)
- }
- }
-}
-
+// TODO(bep) it would be dandy to put this one into the partials package, but
+// we have some package cycle issues to solve first.
func TestPartialCached(t *testing.T) {
t.Parallel()
testCases := []struct {
@@ -388,20 +262,3 @@ func newTestFuncsterWithViper(v *viper.Viper) *templateFuncster {
return d.Tmpl.(*templateHandler).html.funcster
}
-
-func newTestTemplate(t *testing.T, name, template string) tpl.Template {
- config := newDepsConfig(viper.New())
- config.WithTemplate = func(templ tpl.TemplateHandler) error {
- err := templ.AddTemplate(name, template)
- if err != nil {
- return err
- }
- return nil
- }
-
- de, err := deps.New(config)
- require.NoError(t, err)
- require.NoError(t, de.LoadResources())
-
- return de.Tmpl.Lookup(name)
-}
diff --git a/tpl/tplimpl/template_test.go b/tpl/tplimpl/template_test.go
deleted file mode 100644
index 998915a46..000000000
--- a/tpl/tplimpl/template_test.go
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2016 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 tplimpl
-
-import (
- "errors"
- "io/ioutil"
- "testing"
-
- "github.com/spf13/hugo/deps"
-
- "github.com/spf13/hugo/tpl"
- "github.com/spf13/viper"
- "github.com/stretchr/testify/require"
-)
-
-// Test for bugs discovered by https://github.com/dvyukov/go-fuzz
-func TestTplGoFuzzReports(t *testing.T) {
- t.Parallel()
-
- // The following test case(s) also fail
- // See https://github.com/golang/go/issues/10634
- //{"{{ seq 433937734937734969526500969526500 }}", 2}}
-
- for i, this := range []struct {
- data string
- expectErr int
- }{
- // Issue #1089
- //{"{{apply .C \"first\" }}", 2},
- // Issue #1090
- {"{{ slicestr \"000000\" 10}}", 2},
- // Issue #1091
- //{"{{apply .C \"first\" 0 0 0}}", 2},
- {"{{seq 3e80}}", 2},
- // Issue #1095
- {"{{apply .C \"urlize\" " +
- "\".\"}}", 2}} {
-
- d := &Data{
- A: 42,
- B: "foo",
- C: []int{1, 2, 3},
- D: map[int]string{1: "foo", 2: "bar"},
- E: Data1{42, "foo"},
- F: []string{"a", "b", "c"},
- G: []string{"a", "b", "c", "d", "e"},
- H: "a,b,c,d,e,f",
- }
-
- config := newDepsConfig(viper.New())
-
- config.WithTemplate = func(templ tpl.TemplateHandler) error {
- return templ.AddTemplate("fuzz", this.data)
- }
-
- de, err := deps.New(config)
- require.NoError(t, err)
- require.NoError(t, de.LoadResources())
-
- templ := de.Tmpl.(*templateHandler)
-
- 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)
- }
-
- tt := de.Tmpl.Lookup("fuzz")
- require.NotNil(t, tt)
- err = tt.Execute(ioutil.Discard, d)
-
- if err != nil && this.expectErr == 0 {
- t.Fatalf("Test %d errored: %s", i, err)
- } else if err == nil && this.expectErr == 2 {
- t.Fatalf("#2 Test %d should have errored", i)
- }
-
- }
-}
-
-type Data struct {
- A int
- B string
- C []int
- D map[int]string
- E Data1
- F []string
- G []string
- H string
-}
-
-type Data1 struct {
- A int
- B string
-}
-
-func (Data1) Q() string {
- return "foo"
-}
-
-func (Data1) W() (string, error) {
- return "foo", nil
-}
-
-func (Data1) E() (string, error) {
- return "foo", errors.New("Data.E error")
-}
-
-func (Data1) R(v int) (string, error) {
- return "foo", nil
-}
-
-func (Data1) T(s string) (string, error) {
- return s, nil
-}