shithub: hugo

Download patch

ref: 973393c99e3e6927e1c8ef27b0cd8f22eae3f4e4
parent: 9896cd0030da75bf6acab14dad0a7f78472ceff9
author: Bjørn Erik Pedersen <[email protected]>
date: Wed Mar 9 09:05:31 EST 2016

Create template clone for late template execution

Fixing some breaking blogs on Go 1.6

Fixes #1879

--- a/hugolib/handler_test.go
+++ b/hugolib/handler_test.go
@@ -51,12 +51,11 @@
 	}
 
 	s.initializeSiteInfo()
-	// From site_test.go
-	templatePrep(s)
 
-	must(s.addTemplate("_default/single.html", "{{.Content}}"))
-	must(s.addTemplate("head", "<head><script src=\"script.js\"></script></head>"))
-	must(s.addTemplate("head_abs", "<head><script src=\"/script.js\"></script></head>"))
+	s.prepTemplates(
+		"_default/single.html", "{{.Content}}",
+		"head", "<head><script src=\"script.js\"></script></head>",
+		"head_abs", "<head><script src=\"/script.js\"></script></head>")
 
 	// From site_test.go
 	createAndRenderPages(t, s)
--- a/hugolib/robotstxt_test.go
+++ b/hugolib/robotstxt_test.go
@@ -31,8 +31,7 @@
 
 	s.initializeSiteInfo()
 
-	s.prepTemplates()
-	s.addTemplate("robots.txt", ROBOTSTXT_TEMPLATE)
+	s.prepTemplates("robots.txt", ROBOTSTXT_TEMPLATE)
 
 	if err := s.CreatePages(); err != nil {
 		t.Fatalf("Unable to create pages: %s", err)
--- a/hugolib/rss_test.go
+++ b/hugolib/rss_test.go
@@ -58,10 +58,7 @@
 		Source: &source.InMemorySource{ByteSource: WEIGHTED_SOURCES},
 	}
 	s.initializeSiteInfo()
-	s.prepTemplates()
-
-	//  Add an rss.xml template to invoke the rss build.
-	s.addTemplate("rss.xml", RSS_TEMPLATE)
+	s.prepTemplates("rss.xml", RSS_TEMPLATE)
 
 	if err := s.CreatePages(); err != nil {
 		t.Fatalf("Unable to create pages: %s", err)
--- a/hugolib/shortcode_test.go
+++ b/hugolib/shortcode_test.go
@@ -458,12 +458,16 @@
 	}
 
 	s.initializeSiteInfo()
-	templatePrep(s)
+
+	s.loadTemplates()
+
+	s.Tmpl.AddTemplate("_default/single.html", "{{.Content}}")
+
 	s.Tmpl.AddInternalShortcode("b.html", `b`)
 	s.Tmpl.AddInternalShortcode("c.html", `c`)
 	s.Tmpl.AddInternalShortcode("d.html", `d`)
 
-	must(s.addTemplate("_default/single.html", "{{.Content}}"))
+	s.Tmpl.MarkReady()
 
 	createAndRenderPages(t, s)
 
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -50,6 +50,9 @@
 
 var _ = transform.AbsURL
 
+// used to indicate if run as a test.
+var testMode bool
+
 var DefaultTimer *nitro.B
 
 var distinctErrorLogger = helpers.NewDistinctErrorLogger()
@@ -594,7 +597,7 @@
 	return s.ShowPlan(os.Stdout)
 }
 
-func (s *Site) prepTemplates() {
+func (s *Site) loadTemplates() {
 	s.Tmpl = tpl.InitializeT()
 	s.Tmpl.LoadTemplates(s.absLayoutDir())
 	if s.hasTheme() {
@@ -602,8 +605,18 @@
 	}
 }
 
-func (s *Site) addTemplate(name, data string) error {
-	return s.Tmpl.AddTemplate(name, data)
+func (s *Site) prepTemplates(additionalNameValues ...string) error {
+	s.loadTemplates()
+
+	for i := 0; i < len(additionalNameValues); i += 2 {
+		err := s.Tmpl.AddTemplate(additionalNameValues[i], additionalNameValues[i+1])
+		if err != nil {
+			return err
+		}
+	}
+	s.Tmpl.MarkReady()
+
+	return nil
 }
 
 func (s *Site) loadData(sources []source.Input) (err error) {
@@ -1386,7 +1399,7 @@
 		var layouts []string
 		if !p.IsRenderable() {
 			self := "__" + p.TargetPath()
-			_, err := s.Tmpl.New(self).Parse(string(p.Content))
+			_, err := s.Tmpl.GetClone().New(self).Parse(string(p.Content))
 			if err != nil {
 				results <- err
 				continue
@@ -2024,8 +2037,11 @@
 	if err := s.renderThing(d, layout, w); err != nil {
 		// Behavior here should be dependent on if running in server or watch mode.
 		distinctErrorLogger.Printf("Error while rendering %s: %v", name, err)
-		if !s.Running() {
+		if !s.Running() && !testMode {
+			// TODO(bep) check if this can be propagated
 			os.Exit(-1)
+		} else if testMode {
+			return err
 		}
 	}
 
@@ -2043,10 +2059,11 @@
 
 func (s *Site) renderThing(d interface{}, layout string, w io.Writer) error {
 	// If the template doesn't exist, then return, but leave the Writer open
-	if s.Tmpl.Lookup(layout) == nil {
-		return fmt.Errorf("Layout not found: %s", layout)
+	if templ := s.Tmpl.Lookup(layout); templ != nil {
+		return templ.Execute(w, d)
 	}
-	return s.Tmpl.ExecuteTemplate(w, layout, d)
+	return fmt.Errorf("Layout not found: %s", layout)
+
 }
 
 func (s *Site) NewXMLBuffer() *bytes.Buffer {
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -30,7 +30,6 @@
 	"github.com/spf13/hugo/hugofs"
 	"github.com/spf13/hugo/source"
 	"github.com/spf13/hugo/target"
-	"github.com/spf13/hugo/tpl"
 	"github.com/spf13/viper"
 	"github.com/stretchr/testify/assert"
 )
@@ -65,6 +64,10 @@
 `
 )
 
+func init() {
+	testMode = true
+}
+
 // Issue #1797
 func TestReadPagesFromSourceWithEmptySource(t *testing.T) {
 	viper.Reset()
@@ -110,14 +113,6 @@
 	}
 }
 
-func templatePrep(s *Site) {
-	s.Tmpl = tpl.New()
-	s.Tmpl.LoadTemplates(s.absLayoutDir())
-	if s.hasTheme() {
-		s.Tmpl.LoadTemplatesWithPrefix(s.absThemeDir()+"/layouts", "theme")
-	}
-}
-
 func pageMust(p *Page, err error) *Page {
 	if err != nil {
 		panic(err)
@@ -129,7 +124,7 @@
 	p, _ := NewPageFrom(strings.NewReader(PAGE_SIMPLE_TITLE), "content/a/file.md")
 	p.Convert()
 	s := new(Site)
-	templatePrep(s)
+	s.prepTemplates()
 	err := s.renderThing(p, "foobar", nil)
 	if err == nil {
 		t.Errorf("Expected err to be returned when missing the template.")
@@ -138,8 +133,7 @@
 
 func TestAddInvalidTemplate(t *testing.T) {
 	s := new(Site)
-	templatePrep(s)
-	err := s.addTemplate("missing", TEMPLATE_MISSING_FUNC)
+	err := s.prepTemplates("missing", TEMPLATE_MISSING_FUNC)
 	if err == nil {
 		t.Fatalf("Expecting the template to return an error")
 	}
@@ -182,7 +176,6 @@
 	for i, test := range tests {
 
 		s := new(Site)
-		templatePrep(s)
 
 		p, err := NewPageFrom(strings.NewReader(test.content), "content/a/file.md")
 		p.Convert()
@@ -190,7 +183,9 @@
 			t.Fatalf("Error parsing buffer: %s", err)
 		}
 		templateName := fmt.Sprintf("foobar%d", i)
-		err = s.addTemplate(templateName, test.template)
+
+		s.prepTemplates(templateName, test.template)
+
 		if err != nil {
 			t.Fatalf("Unable to add template: %s", err)
 		}
@@ -230,7 +225,6 @@
 	for i, test := range tests {
 
 		s := &Site{}
-		templatePrep(s)
 
 		p, err := NewPageFrom(strings.NewReader(PAGE_SIMPLE_TITLE), "content/a/file.md")
 		if err != nil {
@@ -237,11 +231,9 @@
 			t.Fatalf("Error parsing buffer: %s", err)
 		}
 		templateName := fmt.Sprintf("default%d", i)
-		err = s.addTemplate(templateName, test.template)
-		if err != nil {
-			t.Fatalf("Unable to add template: %s", err)
-		}
 
+		s.prepTemplates(templateName, test.template)
+
 		var err2 error
 
 		if test.missing {
@@ -387,9 +379,8 @@
 	}
 
 	s.initializeSiteInfo()
-	templatePrep(s)
 
-	must(s.addTemplate("_default/single.html", "{{.Content}}"))
+	s.prepTemplates("_default/single.html", "{{.Content}}")
 
 	createAndRenderPages(t, s)
 
@@ -454,16 +445,14 @@
 	}
 
 	s.initializeSiteInfo()
-	templatePrep(s)
 
-	must(s.addTemplate("index.html", "Home Sweet {{ if.IsHome  }}Home{{ end }}."))
-	must(s.addTemplate("_default/single.html", "{{.Content}}{{ if.IsHome  }}This is not home!{{ end }}"))
-	must(s.addTemplate("404.html", "Page Not Found.{{ if.IsHome  }}This is not home!{{ end }}"))
+	s.prepTemplates(
+		"index.html", "Home Sweet {{ if.IsHome  }}Home{{ end }}.",
+		"_default/single.html", "{{.Content}}{{ if.IsHome  }}This is not home!{{ end }}",
+		"404.html", "Page Not Found.{{ if.IsHome  }}This is not home!{{ end }}",
+		"rss.xml", "<root>RSS</root>",
+		"sitemap.xml", "<root>SITEMAP</root>")
 
-	// make sure the XML files also end up with ugly urls
-	must(s.addTemplate("rss.xml", "<root>RSS</root>"))
-	must(s.addTemplate("sitemap.xml", "<root>SITEMAP</root>"))
-
 	createAndRenderPages(t, s)
 	s.RenderHomePage()
 	s.RenderSitemap()
@@ -549,11 +538,10 @@
 	}
 
 	s.initializeSiteInfo()
-	templatePrep(s)
+	s.prepTemplates(
+		"_default/single.html", "{{.Content}}",
+		"_default/list.html", "{{ .Title }}")
 
-	must(s.addTemplate("_default/single.html", "{{.Content}}"))
-	must(s.addTemplate("_default/list.html", "{{ .Title }}"))
-
 	createAndRenderPages(t, s)
 	s.RenderSectionLists()
 
@@ -614,11 +602,11 @@
 	}
 
 	s.initializeSiteInfo()
-	templatePrep(s)
 
-	must(s.addTemplate("_default/single.html", "{{.Content}}"))
-	must(s.addTemplate("head", "<head><script src=\"script.js\"></script></head>"))
-	must(s.addTemplate("head_abs", "<head><script src=\"/script.js\"></script></head>"))
+	s.prepTemplates(
+		"_default/single.html", "{{.Content}}",
+		"head", "<head><script src=\"script.js\"></script></head>",
+		"head_abs", "<head><script src=\"/script.js\"></script></head>")
 
 	createAndRenderPages(t, s)
 
@@ -670,8 +658,8 @@
 		}
 		t.Logf("Rendering with BaseURL %q and CanonifyURLs set %v", viper.GetString("baseURL"), canonify)
 		s.initializeSiteInfo()
-		templatePrep(s)
-		must(s.addTemplate("blue/single.html", TEMPLATE_WITH_URL_ABS))
+
+		s.prepTemplates("blue/single.html", TEMPLATE_WITH_URL_ABS)
 
 		if err := s.CreatePages(); err != nil {
 			t.Fatalf("Unable to create pages: %s", err)
--- a/hugolib/site_url_test.go
+++ b/hugolib/site_url_test.go
@@ -103,8 +103,7 @@
 		Source: &source.InMemorySource{ByteSource: urlFakeSource},
 	}
 	s.initializeSiteInfo()
-	s.prepTemplates()
-	must(s.addTemplate("indexes/blue.html", INDEX_TEMPLATE))
+	s.prepTemplates("indexes/blue.html", INDEX_TEMPLATE)
 
 	if err := s.CreatePages(); err != nil {
 		t.Errorf("Unable to create pages: %s", err)
--- a/hugolib/siteinfo_test.go
+++ b/hugolib/siteinfo_test.go
@@ -33,8 +33,9 @@
 	if s.Info.Params["MyGlobalParam"] != "FOOBAR_PARAM" {
 		t.Errorf("Unable to set site.Info.Param")
 	}
-	s.prepTemplates()
-	s.addTemplate("template", SITE_INFO_PARAM_TEMPLATE)
+
+	s.prepTemplates("template", SITE_INFO_PARAM_TEMPLATE)
+
 	buf := new(bytes.Buffer)
 
 	err := s.renderThing(s.NewNode(), "template", buf)
--- a/hugolib/sitemap_test.go
+++ b/hugolib/sitemap_test.go
@@ -50,8 +50,7 @@
 
 	s.initializeSiteInfo()
 
-	s.prepTemplates()
-	s.addTemplate("sitemap.xml", SITEMAP_TEMPLATE)
+	s.prepTemplates("sitemap.xml", SITEMAP_TEMPLATE)
 
 	if err := s.CreatePages(); err != nil {
 		t.Fatalf("Unable to create pages: %s", err)
--- a/tpl/template.go
+++ b/tpl/template.go
@@ -37,8 +37,10 @@
 	Lookup(name string) *template.Template
 	Templates() []*template.Template
 	New(name string) *template.Template
+	GetClone() *template.Template
 	LoadTemplates(absPath string)
 	LoadTemplatesWithPrefix(absPath, prefix string)
+	MarkReady()
 	AddTemplate(name, tpl string) error
 	AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error
 	AddInternalTemplate(prefix, name, tpl string) error
@@ -53,6 +55,7 @@
 
 type GoHTMLTemplate struct {
 	template.Template
+	clone  *template.Template
 	errors []*templateErr
 }
 
@@ -109,12 +112,12 @@
 
 		name := layout
 
-		if localTemplates.Lookup(name) == nil {
+		if Lookup(name) == nil {
 			name = layout + ".html"
 		}
 
-		if localTemplates.Lookup(name) != nil {
-			err := localTemplates.ExecuteTemplate(w, name, context)
+		if templ := Lookup(name); templ != nil {
+			err := templ.Execute(w, context)
 			if err != nil {
 				jww.ERROR.Println(err, "in", name)
 			}
@@ -135,11 +138,49 @@
 	return template.HTML(b.String())
 }
 
+func Lookup(name string) *template.Template {
+	return (tmpl.(*GoHTMLTemplate)).Lookup(name)
+}
+
+func (t *GoHTMLTemplate) Lookup(name string) *template.Template {
+
+	templ := localTemplates.Lookup(name)
+
+	if templ != nil {
+		return templ
+	}
+
+	if t.clone != nil {
+		return t.clone.Lookup(name)
+	}
+
+	return nil
+
+}
+
+func (t *GoHTMLTemplate) GetClone() *template.Template {
+	return t.clone
+}
+
 func (t *GoHTMLTemplate) LoadEmbedded() {
 	t.EmbedShortcodes()
 	t.EmbedTemplates()
 }
 
+// MarkReady marks the template as "ready for execution". No changes allowed
+// after this is set.
+func (t *GoHTMLTemplate) MarkReady() {
+	if t.clone == nil {
+		t.clone = template.Must(t.Template.Clone())
+	}
+}
+
+func (t *GoHTMLTemplate) checkState() {
+	if t.clone != nil {
+		panic("template is cloned and cannot be modfified")
+	}
+}
+
 func (t *GoHTMLTemplate) AddInternalTemplate(prefix, name, tpl string) error {
 	if prefix != "" {
 		return t.AddTemplate("_internal/"+prefix+"/"+name, tpl)
@@ -153,6 +194,7 @@
 }
 
 func (t *GoHTMLTemplate) AddTemplate(name, tpl string) error {
+	t.checkState()
 	_, err := t.New(name).Parse(tpl)
 	if err != nil {
 		t.errors = append(t.errors, &templateErr{name: name, err: err})
@@ -161,6 +203,7 @@
 }
 
 func (t *GoHTMLTemplate) AddAceTemplate(name, basePath, innerPath string, baseContent, innerContent []byte) error {
+	t.checkState()
 	var base, inner *ace.File
 	name = name[:len(name)-len(filepath.Ext(innerPath))] + ".html"
 
@@ -188,6 +231,7 @@
 }
 
 func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) error {
+	t.checkState()
 	// get the suffix and switch on that
 	ext := filepath.Ext(path)
 	switch ext {