shithub: hugo

Download patch

ref: 9500ec1b6bd23276621d267d7532ab22f33fb157
parent: 197aacb6472732126312c22b5bde730ef34258a6
author: Noah Campbell <[email protected]>
date: Mon Oct 7 03:57:45 EDT 2013

Refactor layout selection code

The render code path would use a fallback if there was an exception.
This change instead relies on explicit declaration of the layout to use
and includes a check to see if the layout indeed exists before
attempting to render it.

--- a/docs/content/layout/templates.md
+++ b/docs/content/layout/templates.md
@@ -22,7 +22,7 @@
 ### [RSS](/layout/rss/)
 Used to render all rss documents.
 
-### [Index](/layout/index)
+### [Index](/layout/indexes)
 Page that list multiple pieces of content.
 
 ### [Content](/layout/content)
--- a/docs/layouts/chrome/menu.html
+++ b/docs/layouts/chrome/menu.html
@@ -2,40 +2,40 @@
             <li> <a href="/">Home</a></li>
             <li class="divider"></li>
             <li class="nav-header">Getting Started</li>
-            <li> <a href="/overview/installing">Installing Hugo</a></li>
-            <li> <a href="/overview/usage">Usage</a> </li>
-            <li> <a href="/overview/configuration">Configuration</a></li>
-            <li> <a href="/overview/source-directory">Source Directory Layout</a></li>
+            <li hugo-nav="/overview/installing"> <a href="/overview/installing">Installing Hugo</a></li>
+            <li hugo-nav="/overview/usage"> <a href="/overview/usage">Usage</a> </li>
+            <li hugo-nav="/overview/configuration"> <a href="/overview/configuration">Configuration</a></li>
+            <li hugo-nav="/overview/source-directory"> <a href="/overview/source-directory">Source Directory Layout</a></li>
             <li class="divider"></li>
             <li class="nav-header">Layout</li>
             <li> <a href="/layout/templates">Overview</a></li>
             <!--<li> <a href="/layout/go-templates">Go Templates</a></li>-->
-            <li> <a href="/layout/variables">Variables</a></li>
-            <li> <a href="/layout/homepage">Homepage</a></li>
-            <li> <a href="/layout/rss">RSS</a></li>
-            <li> <a href="/layout/index">Index</a></li>
-            <li> <a href="/layout/content">Content</a></li>
-            <li> <a href="/layout/views">Content Views</a></li>
-            <li> <a href="/layout/chrome">Chrome</a></li>
+            <li hugo-nav="/layout/variables"> <a href="/layout/variables">Variables</a></li>
+            <li hugo-nav="/layout/homepage"> <a href="/layout/homepage">Homepage</a></li>
+            <li hugo-nav="/layout/rss"> <a href="/layout/rss">RSS</a></li>
+            <li hugo-nav="/layout/indexes"> <a href="/layout/indexes">Index</a></li>
+            <li hugo-nav="/layout/content"> <a href="/layout/content">Content</a></li>
+            <li hugo-nav="/layout/views"> <a href="/layout/views">Content Views</a></li>
+            <li hugo-nav="/layout/chrome"> <a href="/layout/chrome">Chrome</a></li>
             <li class="divider"></li>
             <li class="nav-header">Content</li>
-            <li> <a href="/content/organization">Organization</a></li>
-            <li> <a href="/content/sections">Sections</a></li>
-            <li> <a href="/content/types">Types</a></li>
-            <li> <a href="/content/front-matter">Front Matter</a></li>
-            <li> <a href="/content/example">Example</a></li>
+            <li hugo-nav="/content/organization"> <a href="/content/organization">Organization</a></li>
+            <li hugo-nav="/content/sections"> <a href="/content/sections">Sections</a></li>
+            <li hugo-nav="/content/types"> <a href="/content/types">Types</a></li>
+            <li hugo-nav="/content/front-matter"> <a href="/content/front-matter">Front Matter</a></li>
+            <li hugo-nav="/content/example"> <a href="/content/example">Example</a></li>
             <li class="divider"></li>
             <li class="nav-header">Extras</li>
-            <li> <a href="/extras/shortcodes">ShortCodes</a></li>
-            <li> <a href="/extras/aliases">Aliases</a></li>
-            <li> <a href="/extras/indexes">Indexes</a></li>
-            <li> <a href="/extras/indexes/category">Example Index - Category</a></li>
+            <li hugo-nav="/extras/shortcodes"> <a href="/extras/shortcodes">ShortCodes</a></li>
+            <li hugo-nav="/extras/aliases"> <a href="/extras/aliases">Aliases</a></li>
+            <li hugo-nav="/extras/indexes"> <a href="/extras/indexes">Indexes</a></li>
+            <li hugo-nav="/extras/indexes/category"> <a href="/extras/indexes/category">Example Index - Category</a></li>
             <!--<li> <a href="/extras/indexes/series">Example Index - Series</a></li>-->
             <li class="divider"></li>
             <li class="nav-header">Meta</li>
-            <li> <a href="/meta/release-notes">Release Notes</a></li>
-            <li> <a href="/meta/roadmap">Roadmap</a> </li>
-            <li> <a href="/meta/contributing">Contributing</a></li>
-            <li> <a href="/meta/contributors">Contributors</a></li>
-            <li> <a href="/meta/license">License</a></li>
+            <li hugo-nav="/meta/release-notes"> <a href="/meta/release-notes">Release Notes</a></li>
+            <li hugo-nav="/meta/roadmap"> <a href="/meta/roadmap">Roadmap</a> </li>
+            <li hugo-nav="/meta/contributing"> <a href="/meta/contributing">Contributing</a></li>
+            <li hugo-nav="/meta/contributors"> <a href="/meta/contributors">Contributors</a></li>
+            <li hugo-nav="/meta/license"> <a href="/meta/license">License</a></li>
           </ul>
--- a/hugolib/node.go
+++ b/hugolib/node.go
@@ -21,7 +21,7 @@
 type Node struct {
 	RSSlink     template.HTML
 	Site        SiteInfo
-	layout      string
+//	layout      string
 	Data        map[string]interface{}
 	Title       string
 	Description string
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -46,6 +46,7 @@
 	Tmpl        bundle.Template
 	Markup      string
 	renderable  bool
+	layout			string
 	PageMeta
 	File
 	Position
@@ -151,11 +152,14 @@
 func (p *Page) guessSection() {
 	if p.Section == "" {
 		x := strings.Split(p.FileName, "/")
-		if len(x) > 1 {
-			if section := x[len(x)-2]; section != "content" {
-				p.Section = section
-			}
+		x = x[:len(x)-1]
+		if len(x) == 0 {
+			return
 		}
+		if x[0] == "content" {
+			x = x[1:]
+		}
+		p.Section = path.Join(x...)
 	}
 }
 
@@ -171,7 +175,11 @@
 	return "page"
 }
 
-func (page *Page) Layout(l ...string) string {
+func (page *Page) Layout(l ...string) []string {
+	if page.layout != "" {
+		return layouts(page.Type(), page.layout)
+	}
+
 	layout := ""
 	if len(l) == 0 {
 		layout = "single"
@@ -179,11 +187,17 @@
 		layout = l[0]
 	}
 
-	if x := page.layout; x != "" {
-		return x
-	}
+	return layouts(page.Type(), layout)
+}
 
-	return strings.ToLower(page.Type()) + "/" + layout + ".html"
+func layouts(types string, layout string) (layouts []string) {
+	t := strings.Split(types, "/")
+	for i := range t  {
+		search := t[:len(t)-i]
+		layouts = append(layouts, fmt.Sprintf("%s/%s.html", strings.ToLower(path.Join(search...)), layout))
+	}
+	layouts = append(layouts, fmt.Sprintf("%s.html", layout))
+	return
 }
 
 func ReadFrom(buf io.Reader, name string) (page *Page, err error) {
@@ -214,7 +228,7 @@
 	pUrl := strings.TrimSpace(p.Url)
 	var permalink string
 	if len(pSlug) > 0 {
-		if p.Site.Config.UglyUrls {
+		if p.Site.Config != nil && p.Site.Config.UglyUrls {
 			permalink = section + "/" + p.Slug + "." + p.Extension
 		} else {
 			permalink = section + "/" + p.Slug + "/"
@@ -404,7 +418,12 @@
 func (p *Page) ExecuteTemplate(layout string) *bytes.Buffer {
 	l := p.Layout(layout)
 	buffer := new(bytes.Buffer)
-	p.Tmpl.ExecuteTemplate(buffer, l, p)
+	for _, layout := range l {
+		if p.Tmpl.Lookup(layout) != nil {
+			p.Tmpl.ExecuteTemplate(buffer, layout, p)
+			break
+		}
+	}
 	return buffer
 }
 
--- /dev/null
+++ b/hugolib/page_permalink_test.go
@@ -1,0 +1,9 @@
+package hugolib
+
+import (
+	"testing"
+)
+
+func TestPermalink(t *testing.T) {
+
+}
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -180,8 +180,8 @@
 	}
 }
 
-func checkPageLayout(t *testing.T, page *Page, layout string) {
-	if page.Layout() != layout {
+func checkPageLayout(t *testing.T, page *Page, layout ...string) {
+	if !listEqual(page.Layout(), layout) {
 		t.Fatalf("Page layout is: %s.  Expected: %s", page.Layout(), layout)
 	}
 }
@@ -201,7 +201,7 @@
 	checkPageContent(t, p, "<p>Simple Page</p>\n")
 	checkPageSummary(t, p, "Simple Page")
 	checkPageType(t, p, "page")
-	checkPageLayout(t, p, "page/single.html")
+	checkPageLayout(t, p, "page/single.html", "single.html")
 }
 
 func TestPageWithDelimiter(t *testing.T) {
@@ -213,7 +213,7 @@
 	checkPageContent(t, p, "<p>Summary Next Line</p>\n\n<p>Some more text</p>\n")
 	checkPageSummary(t, p, "<p>Summary Next Line</p>\n")
 	checkPageType(t, p, "page")
-	checkPageLayout(t, p, "page/single.html")
+	checkPageLayout(t, p, "page/single.html", "single.html")
 }
 
 func TestPageWithShortCodeInSummary(t *testing.T) {
@@ -225,7 +225,7 @@
 	checkPageContent(t, p, "<p>Summary Next Line. {{% img src=&ldquo;/not/real&rdquo; %}}.\nMore text here.</p>\n\n<p>Some more text</p>\n")
 	checkPageSummary(t, p, "Summary Next Line. . More text here. Some more text")
 	checkPageType(t, p, "page")
-	checkPageLayout(t, p, "page/single.html")
+	checkPageLayout(t, p, "page/single.html", "single.html")
 }
 
 func TestPageWithMoreTag(t *testing.T) {
@@ -237,7 +237,7 @@
 	checkPageContent(t, p, "<p>Summary Same Line</p>\n\n<p>Some more text</p>\n")
 	checkPageSummary(t, p, "<p>Summary Same Line</p>\n")
 	checkPageType(t, p, "page")
-	checkPageLayout(t, p, "page/single.html")
+	checkPageLayout(t, p, "page/single.html", "single.html")
 }
 
 func TestPageWithDate(t *testing.T) {
@@ -315,10 +315,14 @@
 	}
 }
 
+func L(s ...string) []string {
+	return s
+}
+
 func TestLayoutOverride(t *testing.T) {
 	var (
-		path_content_one_dir = path.Join("content", "gub", "file1.md")
 		path_content_two_dir = path.Join("content", "dub", "sub", "file1.md")
+		path_content_one_dir = path.Join("content", "gub", "file1.md")
 		path_content_no_dir  = path.Join("content", "file1")
 		path_one_directory   = path.Join("fub", "file1.md")
 		path_no_directory    = path.Join("file1.md")
@@ -326,27 +330,27 @@
 	tests := []struct {
 		content        string
 		path           string
-		expectedLayout string
+		expectedLayout []string
 	}{
-		{SIMPLE_PAGE_NOLAYOUT, path_content_two_dir, "sub/single.html"},
-		{SIMPLE_PAGE_NOLAYOUT, path_content_one_dir, "gub/single.html"},
-		{SIMPLE_PAGE_NOLAYOUT, path_content_no_dir, "page/single.html"},
-		{SIMPLE_PAGE_NOLAYOUT, path_one_directory, "fub/single.html"},
-		{SIMPLE_PAGE_NOLAYOUT, path_no_directory, "page/single.html"},
-		{SIMPLE_PAGE_LAYOUT_FOOBAR, path_content_two_dir, "foobar"},
-		{SIMPLE_PAGE_LAYOUT_FOOBAR, path_content_one_dir, "foobar"},
-		{SIMPLE_PAGE_LAYOUT_FOOBAR, path_one_directory, "foobar"},
-		{SIMPLE_PAGE_LAYOUT_FOOBAR, path_no_directory, "foobar"},
-		{SIMPLE_PAGE_TYPE_FOOBAR, path_content_two_dir, "foobar/single.html"},
-		{SIMPLE_PAGE_TYPE_FOOBAR, path_content_one_dir, "foobar/single.html"},
-		{SIMPLE_PAGE_TYPE_FOOBAR, path_content_no_dir, "foobar/single.html"},
-		{SIMPLE_PAGE_TYPE_FOOBAR, path_one_directory, "foobar/single.html"},
-		{SIMPLE_PAGE_TYPE_FOOBAR, path_no_directory, "foobar/single.html"},
-		{SIMPLE_PAGE_TYPE_LAYOUT, path_content_two_dir, "buzfoo"},
-		{SIMPLE_PAGE_TYPE_LAYOUT, path_content_one_dir, "buzfoo"},
-		{SIMPLE_PAGE_TYPE_LAYOUT, path_content_no_dir, "buzfoo"},
-		{SIMPLE_PAGE_TYPE_LAYOUT, path_one_directory, "buzfoo"},
-		{SIMPLE_PAGE_TYPE_LAYOUT, path_no_directory, "buzfoo"},
+		{SIMPLE_PAGE_NOLAYOUT, path_content_two_dir, L("dub/sub/single.html", "dub/single.html", "single.html")},
+		{SIMPLE_PAGE_NOLAYOUT, path_content_one_dir, L("gub/single.html", "single.html")},
+		{SIMPLE_PAGE_NOLAYOUT, path_content_no_dir, L("page/single.html", "single.html")},
+		{SIMPLE_PAGE_NOLAYOUT, path_one_directory, L("fub/single.html", "single.html")},
+		{SIMPLE_PAGE_NOLAYOUT, path_no_directory, L("page/single.html", "single.html")},
+		{SIMPLE_PAGE_LAYOUT_FOOBAR, path_content_two_dir, L("dub/sub/foobar.html", "dub/foobar.html", "foobar.html")},
+		{SIMPLE_PAGE_LAYOUT_FOOBAR, path_content_one_dir, L("gub/foobar.html", "foobar.html")},
+		{SIMPLE_PAGE_LAYOUT_FOOBAR, path_one_directory, L("fub/foobar.html", "foobar.html")},
+		{SIMPLE_PAGE_LAYOUT_FOOBAR, path_no_directory, L("page/foobar.html", "foobar.html")},
+		{SIMPLE_PAGE_TYPE_FOOBAR, path_content_two_dir, L("foobar/single.html", "single.html")},
+		{SIMPLE_PAGE_TYPE_FOOBAR, path_content_one_dir, L("foobar/single.html", "single.html")},
+		{SIMPLE_PAGE_TYPE_FOOBAR, path_content_no_dir, L("foobar/single.html", "single.html")},
+		{SIMPLE_PAGE_TYPE_FOOBAR, path_one_directory, L("foobar/single.html", "single.html")},
+		{SIMPLE_PAGE_TYPE_FOOBAR, path_no_directory, L("foobar/single.html", "single.html")},
+		{SIMPLE_PAGE_TYPE_LAYOUT, path_content_two_dir, L("barfoo/buzfoo.html", "buzfoo.html")},
+		{SIMPLE_PAGE_TYPE_LAYOUT, path_content_one_dir, L("barfoo/buzfoo.html", "buzfoo.html")},
+		{SIMPLE_PAGE_TYPE_LAYOUT, path_content_no_dir, L("barfoo/buzfoo.html", "buzfoo.html")},
+		{SIMPLE_PAGE_TYPE_LAYOUT, path_one_directory, L("barfoo/buzfoo.html", "buzfoo.html")},
+		{SIMPLE_PAGE_TYPE_LAYOUT, path_no_directory, L("barfoo/buzfoo.html", "buzfoo.html")},
 	}
 	for _, test := range tests {
 		p, err := ReadFrom(strings.NewReader(test.content), test.path)
@@ -353,8 +357,22 @@
 		if err != nil {
 			t.Fatalf("Unable to parse content:\n%s\n", test.content)
 		}
-		if p.Layout() != test.expectedLayout {
+		if !listEqual(p.Layout(), test.expectedLayout) {
 			t.Errorf("Layout mismatch. Expected: %s, got: %s", test.expectedLayout, p.Layout())
 		}
 	}
+}
+
+func listEqual(left, right []string) bool {
+	if len(left) != len(right) {
+		return false
+	}
+
+	for i := range left {
+		if left[i] != right[i] {
+			return false
+		}
+	}
+
+	return true
 }
--- a/hugolib/path_seperators_test.go
+++ b/hugolib/path_seperators_test.go
@@ -26,11 +26,12 @@
 	toCheck := []struct {
 		input   string
 		section string
-		layout  string
+		layout  []string
 	}{
-		{path.Join("sub", "foobar.html"), "sub", "sub/single.html"},
-		{path.Join("content", "sub", "foobar.html"), "sub", "sub/single.html"},
-		{path.Join("content", "dub", "sub", "foobar.html"), "sub", "sub/single.html"},
+		{path.Join("sub", "foobar.html"), "sub", L("sub/single.html", "single.html")},
+		{path.Join("content", "foobar.html"), "", L("page/single.html", "single.html")},
+		{path.Join("content", "sub", "foobar.html"), "sub", L("sub/single.html", "single.html")},
+		{path.Join("content", "dub", "sub", "foobar.html"), "dub/sub", L("dub/sub/single.html", "dub/single.html", "single.html")},
 	}
 
 	for _, el := range toCheck {
@@ -37,14 +38,14 @@
 		p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_YAML), el.input)
 		p.guessSection()
 		if err != nil {
-			t.Fatalf("Reading from SIMPLE_PAGE_YAML resulted in an error: %s", err)
+			t.Errorf("Reading from SIMPLE_PAGE_YAML resulted in an error: %s", err)
 		}
 		if p.Section != el.section {
-			t.Fatalf("Section not set to %s for page %s. Got: %s", el.section, el.input, p.Section)
+			t.Errorf("Section not set to %s for page %s. Got: %s", el.section, el.input, p.Section)
 		}
 
-		if p.Layout() != el.layout {
-			t.Fatalf("Layout incorrect. Expected: '%s', Got: '%s'", el.layout, p.Layout())
+		if !listEqual(p.Layout(), el.layout) {
+			t.Errorf("Layout incorrect. Expected: '%s', Got: '%s'", el.layout, p.Layout())
 		}
 	}
 }
--- a/hugolib/planner.go
+++ b/hugolib/planner.go
@@ -18,7 +18,9 @@
 			fmt.Fprintf(out, " (renderer: n/a)")
 		}
 		if s.Tmpl != nil {
-			fmt.Fprintf(out, " (layout: %s, exists: %t)", p.Layout(), s.Tmpl.Lookup(p.Layout()) != nil)
+			for _, l := range p.Layout() {
+				fmt.Fprintf(out, " (layout: %s, exists: %t)", l, s.Tmpl.Lookup(l) != nil)
+			}
 		}
 		fmt.Fprintf(out, "\n")
 		fmt.Fprintf(out, " canonical => ")
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -67,6 +67,7 @@
 	Transformer transform.Transformer
 	Target      target.Output
 	Alias       target.AliasPublisher
+	Completed	  chan bool
 }
 
 type SiteInfo struct {
@@ -364,19 +365,21 @@
 
 func (s *Site) RenderPages() (err error) {
 	for _, p := range s.Pages {
-		var layout string
+		var layout []string
 
 		if !p.IsRenderable() {
-			layout = "__" + p.TargetPath()
-			_, err := s.Tmpl.New(layout).Parse(string(p.Content))
+			self := "__" + p.TargetPath()
+			_, err := s.Tmpl.New(self).Parse(string(p.Content))
 			if err != nil {
 				return err
 			}
+			layout = append(layout, self)
 		} else {
-			layout = p.Layout()
+			layout = append(layout, p.Layout()...)
+			layout = append(layout, "_default/single.html")
 		}
 
-		err := s.render(p, p.TargetPath(), layout, "_default/single.html")
+		err := s.render(p, p.TargetPath(), layout...)
 		if err != nil {
 			return err
 		}
@@ -484,7 +487,7 @@
 			n.Data["Pages"] = s.Pages[:9]
 		}
 	}
-	err := s.render(n, "/", "index.html")
+	err := s.render(n, "/", "index.html", "_default/single.html")
 	if err != nil {
 		return err
 	}
@@ -553,8 +556,6 @@
 	if ok {
 		section, _ = page.RelPermalink()
 	}
-
-	fmt.Println("Section is:", section)
 
 	transformer := transform.NewChain(
 		&transform.AbsURL{BaseURL: s.Config.BaseUrl},
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -227,7 +227,7 @@
 
 	s := &Site{
 		Target: target,
-		Config: Config{BaseUrl: "http://auth/bub/"},
+		Config: Config{Verbose: true, BaseUrl: "http://auth/bub/"},
 		Source: &source.InMemorySource{sources},
 	}
 	s.initializeSiteInfo()