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=“/not/real” %}}.\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()