hugo

Unnamed repository; edit this file 'description' to name the repository.

git clone git://git.shimmy1996.com/hugo.git
commit 973393c99e3e6927e1c8ef27b0cd8f22eae3f4e4
parent 9896cd0030da75bf6acab14dad0a7f78472ceff9
Author: Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Date:   Wed,  9 Mar 2016 14:05:31 +0100

Create template clone for late template execution

Fixing some breaking blogs on Go 1.6

Fixes #1879

Diffstat:
Mhugolib/handler_test.go | 9++++-----
Mhugolib/robotstxt_test.go | 3+--
Mhugolib/rss_test.go | 5+----
Mhugolib/shortcode_test.go | 8++++++--
Mhugolib/site.go | 33+++++++++++++++++++++++++--------
Mhugolib/site_test.go | 66+++++++++++++++++++++++++++---------------------------------------
Mhugolib/site_url_test.go | 3+--
Mhugolib/siteinfo_test.go | 5+++--
Mhugolib/sitemap_test.go | 3+--
Mtpl/template.go | 50+++++++++++++++++++++++++++++++++++++++++++++++---
10 files changed, 116 insertions(+), 69 deletions(-)
diff --git a/hugolib/handler_test.go b/hugolib/handler_test.go
@@ -51,12 +51,11 @@ func TestDefaultHandler(t *testing.T) {
 	}
 
 	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)
diff --git a/hugolib/robotstxt_test.go b/hugolib/robotstxt_test.go
@@ -31,8 +31,7 @@ func TestRobotsTXTOutput(t *testing.T) {
 
 	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)
diff --git a/hugolib/rss_test.go b/hugolib/rss_test.go
@@ -58,10 +58,7 @@ func TestRSSOutput(t *testing.T) {
 		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)
diff --git a/hugolib/shortcode_test.go b/hugolib/shortcode_test.go
@@ -458,12 +458,16 @@ e`,
 	}
 
 	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)
 
diff --git a/hugolib/site.go b/hugolib/site.go
@@ -50,6 +50,9 @@ import (
 
 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 @@ func (s *Site) Analyze() error {
 	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) prepTemplates() {
 	}
 }
 
-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 @@ func (s *Site) RenderPages() error {
 		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 @@ func (s *Site) render(name string, d interface{}, w io.Writer, layouts ...string
 	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) findFirstLayout(layouts ...string) (string, bool) {
 
 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 {
diff --git a/hugolib/site_test.go b/hugolib/site_test.go
@@ -30,7 +30,6 @@ import (
 	"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 @@ more text
 `
 )
 
+func init() {
+	testMode = true
+}
+
 // Issue #1797
 func TestReadPagesFromSourceWithEmptySource(t *testing.T) {
 	viper.Reset()
@@ -110,14 +113,6 @@ func createAndRenderPages(t *testing.T, s *Site) {
 	}
 }
 
-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 @@ func TestDegenerateRenderThingMissingTemplate(t *testing.T) {
 	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 TestDegenerateRenderThingMissingTemplate(t *testing.T) {
 
 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 @@ func TestRenderThing(t *testing.T) {
 	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 @@ func TestRenderThing(t *testing.T) {
 			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,17 +225,14 @@ func TestRenderThingOrDefault(t *testing.T) {
 	for i, test := range tests {
 
 		s := &Site{}
-		templatePrep(s)
 
 		p, err := NewPageFrom(strings.NewReader(PAGE_SIMPLE_TITLE), "content/a/file.md")
 		if err != nil {
 			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
 
@@ -387,9 +379,8 @@ THE END.`, refShortcode))},
 	}
 
 	s.initializeSiteInfo()
-	templatePrep(s)
 
-	must(s.addTemplate("_default/single.html", "{{.Content}}"))
+	s.prepTemplates("_default/single.html", "{{.Content}}")
 
 	createAndRenderPages(t, s)
 
@@ -454,15 +445,13 @@ func doTestShouldAlwaysHaveUglyURLs(t *testing.T, uglyURLs bool) {
 	}
 
 	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 }}"))
-
-	// 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>"))
+	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>")
 
 	createAndRenderPages(t, s)
 	s.RenderHomePage()
@@ -549,10 +538,9 @@ func doTestSectionNaming(t *testing.T, canonify, uglify, pluralize bool) {
 	}
 
 	s.initializeSiteInfo()
-	templatePrep(s)
-
-	must(s.addTemplate("_default/single.html", "{{.Content}}"))
-	must(s.addTemplate("_default/list.html", "{{ .Title }}"))
+	s.prepTemplates(
+		"_default/single.html", "{{.Content}}",
+		"_default/list.html", "{{ .Title }}")
 
 	createAndRenderPages(t, s)
 	s.RenderSectionLists()
@@ -614,11 +602,11 @@ func TestSkipRender(t *testing.T) {
 	}
 
 	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 @@ func TestAbsURLify(t *testing.T) {
 		}
 		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)
diff --git a/hugolib/site_url_test.go b/hugolib/site_url_test.go
@@ -103,8 +103,7 @@ func TestPageCount(t *testing.T) {
 		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)
diff --git a/hugolib/siteinfo_test.go b/hugolib/siteinfo_test.go
@@ -33,8 +33,9 @@ func TestSiteInfoParams(t *testing.T) {
 	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)
diff --git a/hugolib/sitemap_test.go b/hugolib/sitemap_test.go
@@ -50,8 +50,7 @@ func TestSitemapOutput(t *testing.T) {
 
 	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)
diff --git a/tpl/template.go b/tpl/template.go
@@ -37,8 +37,10 @@ type Template interface {
 	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 templateErr struct {
 
 type GoHTMLTemplate struct {
 	template.Template
+	clone  *template.Template
 	errors []*templateErr
 }
 
@@ -109,12 +112,12 @@ func executeTemplate(context interface{}, w io.Writer, layouts ...string) {
 
 		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 @@ func ExecuteTemplateToHTML(context interface{}, layouts ...string) template.HTML
 	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) AddInternalShortcode(name, content string) error {
 }
 
 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) AddTemplate(name, tpl string) error {
 }
 
 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) AddAceTemplate(name, basePath, innerPath string, baseCo
 }
 
 func (t *GoHTMLTemplate) AddTemplateFile(name, baseTemplatePath, path string) error {
+	t.checkState()
 	// get the suffix and switch on that
 	ext := filepath.Ext(path)
 	switch ext {