diff options
Diffstat (limited to 'tpl/tplimpl')
-rw-r--r-- | tpl/tplimpl/templateFuncster.go | 6 | ||||
-rw-r--r-- | tpl/tplimpl/template_funcs.go | 75 | ||||
-rw-r--r-- | tpl/tplimpl/template_funcs_test.go | 149 | ||||
-rw-r--r-- | tpl/tplimpl/template_test.go | 127 |
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 & The Sunshine Band <cathal@foo.bar>" | safeHTML}} -htmlUnescape 2: {{"Cathal Garvey &amp; The Sunshine Band &lt;cathal@foo.bar&gt;" | htmlUnescape | htmlUnescape | safeHTML}} -htmlUnescape 3: {{"Cathal Garvey &amp; The Sunshine Band &lt;cathal@foo.bar&gt;" | htmlUnescape | htmlUnescape }} -htmlUnescape 4: {{ htmlEscape "Cathal Garvey & The Sunshine Band <cathal@foo.bar>" | htmlUnescape | safeHTML }} -htmlUnescape 5: {{ htmlUnescape "Cathal Garvey & The Sunshine Band <cathal@foo.bar>" | 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 & The Sunshine Band <cathal@foo.bar> -htmlEscape 2: Cathal Garvey &amp; The Sunshine Band &lt;cathal@foo.bar&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 & The Sunshine Band <cathal@foo.bar> -htmlUnescape 4: Cathal Garvey & The Sunshine Band <cathal@foo.bar> -htmlUnescape 5: Cathal Garvey & The Sunshine Band <cathal@foo.bar> -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 -} |