From 3851117c25be59dc5bd6adec63610adf77efcfe1 Mon Sep 17 00:00:00 2001 From: spf13 Date: Tue, 21 Jan 2014 17:12:59 -0500 Subject: [PATCH] renaming MinRead to ReadingTime and adding to documentation. --- hugolib/page.go | 922 +++++++++++++++++++++---------------------- hugolib/page_test.go | 492 +++++++++++------------ 2 files changed, 707 insertions(+), 707 deletions(-) diff --git a/hugolib/page.go b/hugolib/page.go index dcd93a964..950022d34 100644 --- a/hugolib/page.go +++ b/hugolib/page.go @@ -14,62 +14,62 @@ package hugolib import ( - "bytes" - "errors" - "fmt" - "github.com/BurntSushi/toml" - "github.com/spf13/hugo/helpers" - "github.com/spf13/hugo/parser" - "github.com/spf13/hugo/template/bundle" - "github.com/theplant/blackfriday" - "html/template" - "io" - "launchpad.net/goyaml" - json "launchpad.net/rjson" - "net/url" - "path" - "sort" - "strings" - "time" + "bytes" + "errors" + "fmt" + "github.com/BurntSushi/toml" + "github.com/spf13/hugo/helpers" + "github.com/spf13/hugo/parser" + "github.com/spf13/hugo/template/bundle" + "github.com/theplant/blackfriday" + "html/template" + "io" + "launchpad.net/goyaml" + json "launchpad.net/rjson" + "net/url" + "path" + "sort" + "strings" + "time" ) type Page struct { - Status string - Images []string - RawContent []byte - Content template.HTML - Summary template.HTML - Truncated bool - plain string // TODO should be []byte - Params map[string]interface{} - contentType string - Draft bool - Aliases []string - Tmpl bundle.Template - Markup string - renderable bool - layout string - linkTitle string - PageMeta - File - Position - Node + Status string + Images []string + RawContent []byte + Content template.HTML + Summary template.HTML + Truncated bool + plain string // TODO should be []byte + Params map[string]interface{} + contentType string + Draft bool + Aliases []string + Tmpl bundle.Template + Markup string + renderable bool + layout string + linkTitle string + PageMeta + File + Position + Node } type File struct { - FileName, Extension, Dir string + FileName, Extension, Dir string } type PageMeta struct { - WordCount int - FuzzyWordCount int - MinRead int - Weight int + WordCount int + FuzzyWordCount int + ReadingTime int + Weight int } type Position struct { - Prev *Page - Next *Page + Prev *Page + Next *Page } type Pages []*Page @@ -80,27 +80,27 @@ type Pages []*Page // A type to implement the sort interface for Pages type PageSorter struct { - pages Pages - by PageBy + pages Pages + by PageBy } // Closure used in the Sort.Less method. type PageBy func(p1, p2 *Page) bool func (by PageBy) Sort(pages Pages) { - ps := &PageSorter{ - pages: pages, - by: by, // The Sort method's receiver is the function (closure) that defines the sort order. - } - sort.Sort(ps) + ps := &PageSorter{ + pages: pages, + by: by, // The Sort method's receiver is the function (closure) that defines the sort order. + } + sort.Sort(ps) } var DefaultPageSort = func(p1, p2 *Page) bool { - if p1.Weight == p2.Weight { - return p1.Date.Unix() > p2.Date.Unix() - } else { - return p1.Weight < p2.Weight - } + if p1.Weight == p2.Weight { + return p1.Date.Unix() > p2.Date.Unix() + } else { + return p1.Weight < p2.Weight + } } func (ps *PageSorter) Len() int { return len(ps.pages) } @@ -110,571 +110,571 @@ func (ps *PageSorter) Swap(i, j int) { ps.pages[i], ps.pages[j] = ps.pages[j], p func (ps *PageSorter) Less(i, j int) bool { return ps.by(ps.pages[i], ps.pages[j]) } func (p Pages) Sort() { - PageBy(DefaultPageSort).Sort(p) + PageBy(DefaultPageSort).Sort(p) } func (p Pages) Limit(n int) Pages { - if len(p) < n { - return p[0:n] - } else { - return p - } + if len(p) < n { + return p[0:n] + } else { + return p + } } func (p Pages) ByWeight() Pages { - PageBy(DefaultPageSort).Sort(p) - return p + PageBy(DefaultPageSort).Sort(p) + return p } func (p Pages) ByDate() Pages { - date := func(p1, p2 *Page) bool { - return p1.Date.Unix() < p2.Date.Unix() - } + date := func(p1, p2 *Page) bool { + return p1.Date.Unix() < p2.Date.Unix() + } - PageBy(date).Sort(p) - return p + PageBy(date).Sort(p) + return p } func (p Pages) ByLength() Pages { - length := func(p1, p2 *Page) bool { - return len(p1.Content) < len(p2.Content) - } + length := func(p1, p2 *Page) bool { + return len(p1.Content) < len(p2.Content) + } - PageBy(length).Sort(p) - return p + PageBy(length).Sort(p) + return p } func (p Pages) Reverse() Pages { - for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 { - p[i], p[j] = p[j], p[i] - } + for i, j := 0, len(p)-1; i < j; i, j = i+1, j-1 { + p[i], p[j] = p[j], p[i] + } - return p + return p } func (p Page) Plain() string { - if len(p.plain) == 0 { - p.plain = StripHTML(StripShortcodes(string(p.Content))) - } - return p.plain + if len(p.plain) == 0 { + p.plain = StripHTML(StripShortcodes(string(p.Content))) + } + return p.plain } // nb: this is only called for recognised types; so while .html might work for // creating posts, it results in missing summaries. func getSummaryString(content []byte, pagefmt string) (summary []byte, truncates bool) { - if bytes.Contains(content, summaryDivider) { - // If user defines split: - // Split then render - truncates = true // by definition - summary = renderBytes(bytes.Split(content, summaryDivider)[0], pagefmt) - } else { - // If hugo defines split: - // render, strip html, then split - plain := strings.TrimSpace(StripHTML(StripShortcodes(string(renderBytes(content, pagefmt))))) - summary = []byte(TruncateWordsToWholeSentence(plain, summaryLength)) - truncates = len(summary) != len(plain) - } - return + if bytes.Contains(content, summaryDivider) { + // If user defines split: + // Split then render + truncates = true // by definition + summary = renderBytes(bytes.Split(content, summaryDivider)[0], pagefmt) + } else { + // If hugo defines split: + // render, strip html, then split + plain := strings.TrimSpace(StripHTML(StripShortcodes(string(renderBytes(content, pagefmt))))) + summary = []byte(TruncateWordsToWholeSentence(plain, summaryLength)) + truncates = len(summary) != len(plain) + } + return } func renderBytes(content []byte, pagefmt string) []byte { - switch pagefmt { - default: - return blackfriday.MarkdownCommon(content) - case "markdown": - return blackfriday.MarkdownCommon(content) - case "rst": - return []byte(getRstContent(content)) - } + switch pagefmt { + default: + return blackfriday.MarkdownCommon(content) + case "markdown": + return blackfriday.MarkdownCommon(content) + case "rst": + return []byte(getRstContent(content)) + } } // TODO abstract further to support loading from more // than just files on disk. Should load reader (file, []byte) func newPage(filename string) *Page { - page := Page{contentType: "", - File: File{FileName: filename, Extension: "html"}, - Node: Node{Keywords: make([]string, 10, 30)}, - Params: make(map[string]interface{})} - page.Date, _ = time.Parse("20060102", "20080101") - page.guessSection() - return &page + page := Page{contentType: "", + File: File{FileName: filename, Extension: "html"}, + Node: Node{Keywords: make([]string, 10, 30)}, + Params: make(map[string]interface{})} + page.Date, _ = time.Parse("20060102", "20080101") + page.guessSection() + return &page } func StripHTML(s string) string { - output := "" + output := "" - // Shortcut strings with no tags in them - if !strings.ContainsAny(s, "<>") { - output = s - } else { - s = strings.Replace(s, "\n", " ", -1) - s = strings.Replace(s, "

", " \n", -1) - s = strings.Replace(s, "
", " \n", -1) - s = strings.Replace(s, "
", " \n", -1) + // Shortcut strings with no tags in them + if !strings.ContainsAny(s, "<>") { + output = s + } else { + s = strings.Replace(s, "\n", " ", -1) + s = strings.Replace(s, "

", " \n", -1) + s = strings.Replace(s, "
", " \n", -1) + s = strings.Replace(s, "
", " \n", -1) - // Walk through the string removing all tags - b := new(bytes.Buffer) - inTag := false - for _, r := range s { - switch r { - case '<': - inTag = true - case '>': - inTag = false - default: - if !inTag { - b.WriteRune(r) - } - } - } - output = b.String() - } - return output + // Walk through the string removing all tags + b := new(bytes.Buffer) + inTag := false + for _, r := range s { + switch r { + case '<': + inTag = true + case '>': + inTag = false + default: + if !inTag { + b.WriteRune(r) + } + } + } + output = b.String() + } + return output } func (p *Page) IsRenderable() bool { - return p.renderable + return p.renderable } func (p *Page) guessSection() { - if p.Section == "" { - x := strings.Split(p.FileName, "/") - x = x[:len(x)-1] - if len(x) == 0 { - return - } - if x[0] == "content" { - x = x[1:] - } - p.Section = path.Join(x...) - } + if p.Section == "" { + x := strings.Split(p.FileName, "/") + x = x[:len(x)-1] + if len(x) == 0 { + return + } + if x[0] == "content" { + x = x[1:] + } + p.Section = path.Join(x...) + } } func (page *Page) Type() string { - if page.contentType != "" { - return page.contentType - } - page.guessSection() - if x := page.Section; x != "" { - return x - } + if page.contentType != "" { + return page.contentType + } + page.guessSection() + if x := page.Section; x != "" { + return x + } - return "page" + return "page" } func (page *Page) Layout(l ...string) []string { - if page.layout != "" { - return layouts(page.Type(), page.layout) - } + if page.layout != "" { + return layouts(page.Type(), page.layout) + } - layout := "" - if len(l) == 0 { - layout = "single" - } else { - layout = l[0] - } + layout := "" + if len(l) == 0 { + layout = "single" + } else { + layout = l[0] + } - return layouts(page.Type(), layout) + return layouts(page.Type(), layout) } 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 + 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) { - if len(name) == 0 { - return nil, errors.New("Zero length page name") - } + if len(name) == 0 { + return nil, errors.New("Zero length page name") + } - p := newPage(name) + p := newPage(name) - if err = p.parse(buf); err != nil { - return - } + if err = p.parse(buf); err != nil { + return + } - p.analyzePage() + p.analyzePage() - return p, nil + return p, nil } func (p *Page) ProcessShortcodes(t bundle.Template) { - p.Content = template.HTML(ShortcodesHandle(string(p.Content), p, t)) - p.Summary = template.HTML(ShortcodesHandle(string(p.Summary), p, t)) + p.Content = template.HTML(ShortcodesHandle(string(p.Content), p, t)) + p.Summary = template.HTML(ShortcodesHandle(string(p.Summary), p, t)) } func (p *Page) analyzePage() { - p.WordCount = TotalWords(p.Plain()) - p.FuzzyWordCount = int((p.WordCount+100)/100) * 100 - p.MinRead = int((p.WordCount + 212) / 213) + p.WordCount = TotalWords(p.Plain()) + p.FuzzyWordCount = int((p.WordCount+100)/100) * 100 + p.ReadingTime = int((p.WordCount + 212) / 213) } func (p *Page) permalink() (*url.URL, error) { - baseUrl := string(p.Site.BaseUrl) - dir := strings.TrimSpace(p.Dir) - pSlug := strings.TrimSpace(p.Slug) - pUrl := strings.TrimSpace(p.Url) - var permalink string - var err error + baseUrl := string(p.Site.BaseUrl) + dir := strings.TrimSpace(p.Dir) + pSlug := strings.TrimSpace(p.Slug) + pUrl := strings.TrimSpace(p.Url) + var permalink string + var err error - if override, ok := p.Site.Permalinks[p.Section]; ok { - permalink, err = override.Expand(p) - if err != nil { - return nil, err - } - //fmt.Printf("have an override for %q in section %s → %s\n", p.Title, p.Section, permalink) - } else { + if override, ok := p.Site.Permalinks[p.Section]; ok { + permalink, err = override.Expand(p) + if err != nil { + return nil, err + } + //fmt.Printf("have an override for %q in section %s → %s\n", p.Title, p.Section, permalink) + } else { - if len(pSlug) > 0 { - if p.Site.Config != nil && p.Site.Config.UglyUrls { - permalink = path.Join(dir, p.Slug, p.Extension) - } else { - permalink = path.Join(dir, p.Slug) + "/" - } - } else if len(pUrl) > 2 { - permalink = pUrl - } else { - _, t := path.Split(p.FileName) - if p.Site.Config != nil && p.Site.Config.UglyUrls { - x := replaceExtension(strings.TrimSpace(t), p.Extension) - permalink = path.Join(dir, x) - } else { - file, _ := fileExt(strings.TrimSpace(t)) - permalink = path.Join(dir, file) - } - } + if len(pSlug) > 0 { + if p.Site.Config != nil && p.Site.Config.UglyUrls { + permalink = path.Join(dir, p.Slug, p.Extension) + } else { + permalink = path.Join(dir, p.Slug) + "/" + } + } else if len(pUrl) > 2 { + permalink = pUrl + } else { + _, t := path.Split(p.FileName) + if p.Site.Config != nil && p.Site.Config.UglyUrls { + x := replaceExtension(strings.TrimSpace(t), p.Extension) + permalink = path.Join(dir, x) + } else { + file, _ := fileExt(strings.TrimSpace(t)) + permalink = path.Join(dir, file) + } + } - } + } - base, err := url.Parse(baseUrl) - if err != nil { - return nil, err - } + base, err := url.Parse(baseUrl) + if err != nil { + return nil, err + } - path, err := url.Parse(permalink) - if err != nil { - return nil, err - } + path, err := url.Parse(permalink) + if err != nil { + return nil, err + } - return MakePermalink(base, path), nil + return MakePermalink(base, path), nil } func (p *Page) LinkTitle() string { - if len(p.linkTitle) > 0 { - return p.linkTitle - } else { - return p.Title - } + if len(p.linkTitle) > 0 { + return p.linkTitle + } else { + return p.Title + } } func (p *Page) Permalink() (string, error) { - link, err := p.permalink() - if err != nil { - return "", err - } - return link.String(), nil + link, err := p.permalink() + if err != nil { + return "", err + } + return link.String(), nil } func (p *Page) RelPermalink() (string, error) { - link, err := p.permalink() - if err != nil { - return "", err - } + link, err := p.permalink() + if err != nil { + return "", err + } - link.Scheme = "" - link.Host = "" - link.User = nil - link.Opaque = "" - return link.String(), nil + link.Scheme = "" + link.Host = "" + link.User = nil + link.Opaque = "" + return link.String(), nil } func (page *Page) handleTomlMetaData(datum []byte) (interface{}, error) { - m := map[string]interface{}{} - datum = removeTomlIdentifier(datum) - if _, err := toml.Decode(string(datum), &m); err != nil { - return m, fmt.Errorf("Invalid TOML in %s \nError parsing page meta data: %s", page.FileName, err) - } - return m, nil + m := map[string]interface{}{} + datum = removeTomlIdentifier(datum) + if _, err := toml.Decode(string(datum), &m); err != nil { + return m, fmt.Errorf("Invalid TOML in %s \nError parsing page meta data: %s", page.FileName, err) + } + return m, nil } func removeTomlIdentifier(datum []byte) []byte { - return bytes.Replace(datum, []byte("+++"), []byte(""), -1) + return bytes.Replace(datum, []byte("+++"), []byte(""), -1) } func (page *Page) handleYamlMetaData(datum []byte) (interface{}, error) { - m := map[string]interface{}{} - if err := goyaml.Unmarshal(datum, &m); err != nil { - return m, fmt.Errorf("Invalid YAML in %s \nError parsing page meta data: %s", page.FileName, err) - } - return m, nil + m := map[string]interface{}{} + if err := goyaml.Unmarshal(datum, &m); err != nil { + return m, fmt.Errorf("Invalid YAML in %s \nError parsing page meta data: %s", page.FileName, err) + } + return m, nil } func (page *Page) handleJsonMetaData(datum []byte) (interface{}, error) { - var f interface{} - if err := json.Unmarshal(datum, &f); err != nil { - return f, fmt.Errorf("Invalid JSON in %v \nError parsing page meta data: %s", page.FileName, err) - } - return f, nil + var f interface{} + if err := json.Unmarshal(datum, &f); err != nil { + return f, fmt.Errorf("Invalid JSON in %v \nError parsing page meta data: %s", page.FileName, err) + } + return f, nil } func (page *Page) update(f interface{}) error { - m := f.(map[string]interface{}) + m := f.(map[string]interface{}) - for k, v := range m { - loki := strings.ToLower(k) - switch loki { - case "title": - page.Title = interfaceToString(v) - case "linktitle": - page.linkTitle = interfaceToString(v) - case "description": - page.Description = interfaceToString(v) - case "slug": - page.Slug = helpers.Urlize(interfaceToString(v)) - case "url": - if url := interfaceToString(v); strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") { - return fmt.Errorf("Only relative urls are supported, %v provided", url) - } - page.Url = helpers.Urlize(interfaceToString(v)) - case "type": - page.contentType = interfaceToString(v) - case "keywords": - page.Keywords = interfaceArrayToStringArray(v) - case "date", "pubdate": - page.Date = interfaceToTime(v) - case "draft": - page.Draft = interfaceToBool(v) - case "layout": - page.layout = interfaceToString(v) - case "markup": - page.Markup = interfaceToString(v) - case "weight": - page.Weight = interfaceToInt(v) - case "aliases": - page.Aliases = interfaceArrayToStringArray(v) - for _, alias := range page.Aliases { - if strings.HasPrefix(alias, "http://") || strings.HasPrefix(alias, "https://") { - return fmt.Errorf("Only relative aliases are supported, %v provided", alias) - } - } - case "status": - page.Status = interfaceToString(v) - default: - // If not one of the explicit values, store in Params - switch vv := v.(type) { - case string: - page.Params[loki] = vv - case int64, int32, int16, int8, int: - page.Params[loki] = vv - case float64, float32: - page.Params[loki] = vv - case time.Time: - page.Params[loki] = vv - default: // handle array of strings as well - switch vvv := vv.(type) { - case []interface{}: - var a = make([]string, len(vvv)) - for i, u := range vvv { - a[i] = interfaceToString(u) - } - page.Params[loki] = a - } - } - } - } - return nil + for k, v := range m { + loki := strings.ToLower(k) + switch loki { + case "title": + page.Title = interfaceToString(v) + case "linktitle": + page.linkTitle = interfaceToString(v) + case "description": + page.Description = interfaceToString(v) + case "slug": + page.Slug = helpers.Urlize(interfaceToString(v)) + case "url": + if url := interfaceToString(v); strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://") { + return fmt.Errorf("Only relative urls are supported, %v provided", url) + } + page.Url = helpers.Urlize(interfaceToString(v)) + case "type": + page.contentType = interfaceToString(v) + case "keywords": + page.Keywords = interfaceArrayToStringArray(v) + case "date", "pubdate": + page.Date = interfaceToTime(v) + case "draft": + page.Draft = interfaceToBool(v) + case "layout": + page.layout = interfaceToString(v) + case "markup": + page.Markup = interfaceToString(v) + case "weight": + page.Weight = interfaceToInt(v) + case "aliases": + page.Aliases = interfaceArrayToStringArray(v) + for _, alias := range page.Aliases { + if strings.HasPrefix(alias, "http://") || strings.HasPrefix(alias, "https://") { + return fmt.Errorf("Only relative aliases are supported, %v provided", alias) + } + } + case "status": + page.Status = interfaceToString(v) + default: + // If not one of the explicit values, store in Params + switch vv := v.(type) { + case string: + page.Params[loki] = vv + case int64, int32, int16, int8, int: + page.Params[loki] = vv + case float64, float32: + page.Params[loki] = vv + case time.Time: + page.Params[loki] = vv + default: // handle array of strings as well + switch vvv := vv.(type) { + case []interface{}: + var a = make([]string, len(vvv)) + for i, u := range vvv { + a[i] = interfaceToString(u) + } + page.Params[loki] = a + } + } + } + } + return nil } func (page *Page) GetParam(key string) interface{} { - v := page.Params[strings.ToLower(key)] + v := page.Params[strings.ToLower(key)] - if v == nil { - return nil - } + if v == nil { + return nil + } - switch v.(type) { - case string: - return interfaceToString(v) - case int64, int32, int16, int8, int: - return interfaceToInt(v) - case float64, float32: - return interfaceToFloat64(v) - case time.Time: - return interfaceToTime(v) - case []string: - return v - } - return nil + switch v.(type) { + case string: + return interfaceToString(v) + case int64, int32, int16, int8, int: + return interfaceToInt(v) + case float64, float32: + return interfaceToFloat64(v) + case time.Time: + return interfaceToTime(v) + case []string: + return v + } + return nil } type frontmatterType struct { - markstart, markend []byte - parse func([]byte) (interface{}, error) - includeMark bool + markstart, markend []byte + parse func([]byte) (interface{}, error) + includeMark bool } const YAML_DELIM = "---" const TOML_DELIM = "+++" func (page *Page) detectFrontMatter(mark rune) (f *frontmatterType) { - switch mark { - case '-': - return &frontmatterType{[]byte(YAML_DELIM), []byte(YAML_DELIM), page.handleYamlMetaData, false} - case '+': - return &frontmatterType{[]byte(TOML_DELIM), []byte(TOML_DELIM), page.handleTomlMetaData, false} - case '{': - return &frontmatterType{[]byte{'{'}, []byte{'}'}, page.handleJsonMetaData, true} - default: - return nil - } + switch mark { + case '-': + return &frontmatterType{[]byte(YAML_DELIM), []byte(YAML_DELIM), page.handleYamlMetaData, false} + case '+': + return &frontmatterType{[]byte(TOML_DELIM), []byte(TOML_DELIM), page.handleTomlMetaData, false} + case '{': + return &frontmatterType{[]byte{'{'}, []byte{'}'}, page.handleJsonMetaData, true} + default: + return nil + } } func (p *Page) Render(layout ...string) template.HTML { - curLayout := "" + curLayout := "" - if len(layout) > 0 { - curLayout = layout[0] - } + if len(layout) > 0 { + curLayout = layout[0] + } - return template.HTML(string(p.ExecuteTemplate(curLayout).Bytes())) + return template.HTML(string(p.ExecuteTemplate(curLayout).Bytes())) } func (p *Page) ExecuteTemplate(layout string) *bytes.Buffer { - l := p.Layout(layout) - buffer := new(bytes.Buffer) - for _, layout := range l { - if p.Tmpl.Lookup(layout) != nil { - p.Tmpl.ExecuteTemplate(buffer, layout, p) - break - } - } - return buffer + l := p.Layout(layout) + buffer := new(bytes.Buffer) + for _, layout := range l { + if p.Tmpl.Lookup(layout) != nil { + p.Tmpl.ExecuteTemplate(buffer, layout, p) + break + } + } + return buffer } func (page *Page) guessMarkupType() string { - // First try the explicitly set markup from the frontmatter - if page.Markup != "" { - format := guessType(page.Markup) - if format != "unknown" { - return format - } - } + // First try the explicitly set markup from the frontmatter + if page.Markup != "" { + format := guessType(page.Markup) + if format != "unknown" { + return format + } + } - // Then try to guess from the extension - ext := strings.ToLower(path.Ext(page.FileName)) - if strings.HasPrefix(ext, ".") { - return guessType(ext[1:]) - } + // Then try to guess from the extension + ext := strings.ToLower(path.Ext(page.FileName)) + if strings.HasPrefix(ext, ".") { + return guessType(ext[1:]) + } - return "unknown" + return "unknown" } func guessType(in string) string { - switch in { - case "md", "markdown", "mdown": - return "markdown" - case "rst": - return "rst" - case "html", "htm": - return "html" - } - return "unknown" + switch in { + case "md", "markdown", "mdown": + return "markdown" + case "rst": + return "rst" + case "html", "htm": + return "html" + } + return "unknown" } func (page *Page) parse(reader io.Reader) error { - p, err := parser.ReadFrom(reader) - if err != nil { - return err - } + p, err := parser.ReadFrom(reader) + if err != nil { + return err + } - page.renderable = p.IsRenderable() + page.renderable = p.IsRenderable() - front := p.FrontMatter() + front := p.FrontMatter() - if len(front) != 0 { - fm := page.detectFrontMatter(rune(front[0])) - meta, err := fm.parse(front) - if err != nil { - return err - } + if len(front) != 0 { + fm := page.detectFrontMatter(rune(front[0])) + meta, err := fm.parse(front) + if err != nil { + return err + } - if err = page.update(meta); err != nil { - return err - } + if err = page.update(meta); err != nil { + return err + } - } - page.Content = template.HTML(p.Content()) + } + page.Content = template.HTML(p.Content()) - return nil + return nil } func (page *Page) Convert() error { - switch page.guessMarkupType() { - case "markdown": - page.convertMarkdown(bytes.NewReader([]byte(page.Content))) - case "rst": - page.convertRestructuredText(bytes.NewReader([]byte(page.Content))) - } - return nil + switch page.guessMarkupType() { + case "markdown": + page.convertMarkdown(bytes.NewReader([]byte(page.Content))) + case "rst": + page.convertRestructuredText(bytes.NewReader([]byte(page.Content))) + } + return nil } func (page *Page) convertMarkdown(lines io.Reader) { - b := new(bytes.Buffer) - b.ReadFrom(lines) - content := b.Bytes() - page.Content = template.HTML(string(blackfriday.MarkdownCommon(RemoveSummaryDivider(content)))) - summary, truncated := getSummaryString(content, "markdown") - page.Summary = template.HTML(string(summary)) - page.Truncated = truncated + b := new(bytes.Buffer) + b.ReadFrom(lines) + content := b.Bytes() + page.Content = template.HTML(string(blackfriday.MarkdownCommon(RemoveSummaryDivider(content)))) + summary, truncated := getSummaryString(content, "markdown") + page.Summary = template.HTML(string(summary)) + page.Truncated = truncated } func (page *Page) convertRestructuredText(lines io.Reader) { - b := new(bytes.Buffer) - b.ReadFrom(lines) - content := b.Bytes() - page.Content = template.HTML(getRstContent(content)) - summary, truncated := getSummaryString(content, "rst") - page.Summary = template.HTML(string(summary)) - page.Truncated = truncated + b := new(bytes.Buffer) + b.ReadFrom(lines) + content := b.Bytes() + page.Content = template.HTML(getRstContent(content)) + summary, truncated := getSummaryString(content, "rst") + page.Summary = template.HTML(string(summary)) + page.Truncated = truncated } func (p *Page) TargetPath() (outfile string) { - // Always use Url if it's specified - if len(strings.TrimSpace(p.Url)) > 2 { - outfile = strings.TrimSpace(p.Url) + // Always use Url if it's specified + if len(strings.TrimSpace(p.Url)) > 2 { + outfile = strings.TrimSpace(p.Url) - if strings.HasSuffix(outfile, "/") { - outfile = outfile + "index.html" - } - return - } + if strings.HasSuffix(outfile, "/") { + outfile = outfile + "index.html" + } + return + } - // If there's a Permalink specification, we use that - if override, ok := p.Site.Permalinks[p.Section]; ok { - var err error - outfile, err = override.Expand(p) - if err == nil { - if strings.HasSuffix(outfile, "/") { - outfile += "index.html" - } - return - } - } + // If there's a Permalink specification, we use that + if override, ok := p.Site.Permalinks[p.Section]; ok { + var err error + outfile, err = override.Expand(p) + if err == nil { + if strings.HasSuffix(outfile, "/") { + outfile += "index.html" + } + return + } + } - if len(strings.TrimSpace(p.Slug)) > 0 { - outfile = strings.TrimSpace(p.Slug) + "." + p.Extension - } else { - // Fall back to filename - _, t := path.Split(p.FileName) - outfile = replaceExtension(strings.TrimSpace(t), p.Extension) - } + if len(strings.TrimSpace(p.Slug)) > 0 { + outfile = strings.TrimSpace(p.Slug) + "." + p.Extension + } else { + // Fall back to filename + _, t := path.Split(p.FileName) + outfile = replaceExtension(strings.TrimSpace(t), p.Extension) + } - return path.Join(p.Dir, strings.TrimSpace(outfile)) + return path.Join(p.Dir, strings.TrimSpace(outfile)) } diff --git a/hugolib/page_test.go b/hugolib/page_test.go index 78d5797df..de99f9b6a 100644 --- a/hugolib/page_test.go +++ b/hugolib/page_test.go @@ -1,34 +1,34 @@ package hugolib import ( - "html/template" - "path" - "strings" - "testing" - "time" + "html/template" + "path" + "strings" + "testing" + "time" ) var EMPTY_PAGE = "" const ( - SIMPLE_PAGE = "---\ntitle: Simple\n---\nSimple Page\n" - INVALID_FRONT_MATTER_MISSING = "This is a test" - RENDER_NO_FRONT_MATTER = "This is a test" - INVALID_FRONT_MATTER_SHORT_DELIM = ` + SIMPLE_PAGE = "---\ntitle: Simple\n---\nSimple Page\n" + INVALID_FRONT_MATTER_MISSING = "This is a test" + RENDER_NO_FRONT_MATTER = "This is a test" + INVALID_FRONT_MATTER_SHORT_DELIM = ` -- title: Short delim start --- Short Delim ` - INVALID_FRONT_MATTER_SHORT_DELIM_ENDING = ` + INVALID_FRONT_MATTER_SHORT_DELIM_ENDING = ` --- title: Short delim ending -- Short Delim ` - INVALID_FRONT_MATTER_LEADING_WS = ` + INVALID_FRONT_MATTER_LEADING_WS = ` --- title: Leading WS @@ -36,7 +36,7 @@ title: Leading WS Leading ` - SIMPLE_PAGE_JSON = ` + SIMPLE_PAGE_JSON = ` { "title": "spf13-vim 3.0 release and new website", "description": "spf13-vim is a cross platform distribution of vim plugins and resources for Vim.", @@ -51,7 +51,7 @@ Leading Content of the file goes Here ` - SIMPLE_PAGE_JSON_LOOSE = ` + SIMPLE_PAGE_JSON_LOOSE = ` { "title": "spf13-vim 3.0 release and new website" "description": "spf13-vim is a cross platform distribution of vim plugins and resources for Vim." @@ -66,8 +66,8 @@ Content of the file goes Here Content of the file goes Here ` - SIMPLE_PAGE_RFC3339_DATE = "---\ntitle: RFC3339 Date\ndate: \"2013-05-17T16:59:30Z\"\n---\nrfc3339 content" - SIMPLE_PAGE_JSON_MULTIPLE = ` + SIMPLE_PAGE_RFC3339_DATE = "---\ntitle: RFC3339 Date\ndate: \"2013-05-17T16:59:30Z\"\n---\nrfc3339 content" + SIMPLE_PAGE_JSON_MULTIPLE = ` { "title": "foobar", "customData": { "foo": "bar" }, @@ -76,34 +76,34 @@ Content of the file goes Here Some text ` - SIMPLE_PAGE_JSON_COMPACT = ` + SIMPLE_PAGE_JSON_COMPACT = ` {"title":"foobar","customData":{"foo":"bar"},"date":"2012-08-06"} Text ` - SIMPLE_PAGE_NOLAYOUT = `--- + SIMPLE_PAGE_NOLAYOUT = `--- title: simple_no_layout --- No Layout called out` - SIMPLE_PAGE_LAYOUT_FOOBAR = `--- + SIMPLE_PAGE_LAYOUT_FOOBAR = `--- title: simple layout foobar layout: foobar --- Layout foobar` - SIMPLE_PAGE_TYPE_FOOBAR = `--- + SIMPLE_PAGE_TYPE_FOOBAR = `--- type: foobar --- type foobar` - SIMPLE_PAGE_TYPE_LAYOUT = `--- + SIMPLE_PAGE_TYPE_LAYOUT = `--- type: barfoo layout: buzfoo --- type and layout set` - SIMPLE_PAGE_WITH_SUMMARY_DELIMITER = `--- + SIMPLE_PAGE_WITH_SUMMARY_DELIMITER = `--- title: Simple --- Summary Next Line @@ -111,7 +111,7 @@ Summary Next Line Some more text ` - SIMPLE_PAGE_WITH_SHORTCODE_IN_SUMMARY = `--- + SIMPLE_PAGE_WITH_SHORTCODE_IN_SUMMARY = `--- title: Simple --- Summary Next Line. {{% img src="/not/real" %}}. @@ -120,7 +120,7 @@ More text here. Some more text ` - SIMPLE_PAGE_WITH_SUMMARY_DELIMITER_SAME_LINE = `--- + SIMPLE_PAGE_WITH_SUMMARY_DELIMITER_SAME_LINE = `--- title: Simple --- Summary Same Line @@ -128,7 +128,7 @@ Summary Same Line Some more text ` - SIMPLE_PAGE_WITH_LONG_CONTENT = `--- + SIMPLE_PAGE_WITH_LONG_CONTENT = `--- title: Simple --- Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. @@ -150,310 +150,310 @@ a_date = 1979-05-27T07:32:00Z Front Matter with various frontmatter types` func checkError(t *testing.T, err error, expected string) { - if err == nil { - t.Fatalf("err is nil. Expected: %s", expected) - } - if err.Error() != expected { - t.Errorf("err.Error() returned: '%s'. Expected: '%s'", err.Error(), expected) - } + if err == nil { + t.Fatalf("err is nil. Expected: %s", expected) + } + if err.Error() != expected { + t.Errorf("err.Error() returned: '%s'. Expected: '%s'", err.Error(), expected) + } } func TestDegenerateEmptyPageZeroLengthName(t *testing.T) { - _, err := ReadFrom(strings.NewReader(EMPTY_PAGE), "") - if err == nil { - t.Fatalf("A zero length page name must return an error") - } + _, err := ReadFrom(strings.NewReader(EMPTY_PAGE), "") + if err == nil { + t.Fatalf("A zero length page name must return an error") + } - checkError(t, err, "Zero length page name") + checkError(t, err, "Zero length page name") } func TestDegenerateEmptyPage(t *testing.T) { - _, err := ReadFrom(strings.NewReader(EMPTY_PAGE), "test") - if err != nil { - t.Fatalf("Empty files should not trigger an error. Should be able to touch a file while watching without erroring out.") - } + _, err := ReadFrom(strings.NewReader(EMPTY_PAGE), "test") + if err != nil { + t.Fatalf("Empty files should not trigger an error. Should be able to touch a file while watching without erroring out.") + } - //checkError(t, err, "EOF") + //checkError(t, err, "EOF") } func checkPageTitle(t *testing.T, page *Page, title string) { - if page.Title != title { - t.Fatalf("Page title is: %s. Expected %s", page.Title, title) - } + if page.Title != title { + t.Fatalf("Page title is: %s. Expected %s", page.Title, title) + } } func checkPageContent(t *testing.T, page *Page, content string) { - if page.Content != template.HTML(content) { - t.Fatalf("Page content mismatch\nexp: %q\ngot: %q", content, page.Content) - } + if page.Content != template.HTML(content) { + t.Fatalf("Page content mismatch\nexp: %q\ngot: %q", content, page.Content) + } } func checkPageSummary(t *testing.T, page *Page, summary string) { - if page.Summary != template.HTML(summary) { - t.Fatalf("Page summary is: `%s`. Expected `%s`", page.Summary, summary) - } + if page.Summary != template.HTML(summary) { + t.Fatalf("Page summary is: `%s`. Expected `%s`", page.Summary, summary) + } } func checkPageType(t *testing.T, page *Page, pageType string) { - if page.Type() != pageType { - t.Fatalf("Page type is: %s. Expected: %s", page.Type(), pageType) - } + if page.Type() != pageType { + t.Fatalf("Page type is: %s. Expected: %s", page.Type(), pageType) + } } 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) - } + if !listEqual(page.Layout(), layout) { + t.Fatalf("Page layout is: %s. Expected: %s", page.Layout(), layout) + } } func checkPageDate(t *testing.T, page *Page, time time.Time) { - if page.Date != time { - t.Fatalf("Page date is: %s. Expected: %s", page.Date, time) - } + if page.Date != time { + t.Fatalf("Page date is: %s. Expected: %s", page.Date, time) + } } func checkTruncation(t *testing.T, page *Page, shouldBe bool, msg string) { - if page.Summary == "" { - t.Fatal("page has no summary, can not check truncation") - } - if page.Truncated != shouldBe { - if shouldBe { - t.Fatalf("page wasn't truncated: %s", msg) - } else { - t.Fatalf("page was truncated: %s", msg) - } - } + if page.Summary == "" { + t.Fatal("page has no summary, can not check truncation") + } + if page.Truncated != shouldBe { + if shouldBe { + t.Fatalf("page wasn't truncated: %s", msg) + } else { + t.Fatalf("page was truncated: %s", msg) + } + } } func TestCreateNewPage(t *testing.T) { - p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE), "simple.md") - p.Convert() + p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE), "simple.md") + p.Convert() - if err != nil { - t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) - } - checkPageTitle(t, p, "Simple") - checkPageContent(t, p, "

Simple Page

\n") - checkPageSummary(t, p, "Simple Page") - checkPageType(t, p, "page") - checkPageLayout(t, p, "page/single.html", "single.html") - checkTruncation(t, p, false, "simple short page") + if err != nil { + t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) + } + checkPageTitle(t, p, "Simple") + checkPageContent(t, p, "

Simple Page

\n") + checkPageSummary(t, p, "Simple Page") + checkPageType(t, p, "page") + checkPageLayout(t, p, "page/single.html", "single.html") + checkTruncation(t, p, false, "simple short page") } func TestPageWithDelimiter(t *testing.T) { - p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SUMMARY_DELIMITER), "simple.md") - p.Convert() - if err != nil { - t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) - } - checkPageTitle(t, p, "Simple") - checkPageContent(t, p, "

Summary Next Line

\n\n

Some more text

\n") - checkPageSummary(t, p, "

Summary Next Line

\n") - checkPageType(t, p, "page") - checkPageLayout(t, p, "page/single.html", "single.html") - checkTruncation(t, p, true, "page with summary delimiter") + p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SUMMARY_DELIMITER), "simple.md") + p.Convert() + if err != nil { + t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) + } + checkPageTitle(t, p, "Simple") + checkPageContent(t, p, "

Summary Next Line

\n\n

Some more text

\n") + checkPageSummary(t, p, "

Summary Next Line

\n") + checkPageType(t, p, "page") + checkPageLayout(t, p, "page/single.html", "single.html") + checkTruncation(t, p, true, "page with summary delimiter") } func TestPageWithShortCodeInSummary(t *testing.T) { - p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SHORTCODE_IN_SUMMARY), "simple.md") - p.Convert() - if err != nil { - t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) - } - checkPageTitle(t, p, "Simple") - checkPageContent(t, p, "

Summary Next Line. {{% img src=“/not/real” %}}.\nMore text here.

\n\n

Some more text

\n") - checkPageSummary(t, p, "Summary Next Line. . More text here. Some more text") - checkPageType(t, p, "page") - checkPageLayout(t, p, "page/single.html", "single.html") + p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SHORTCODE_IN_SUMMARY), "simple.md") + p.Convert() + if err != nil { + t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) + } + checkPageTitle(t, p, "Simple") + checkPageContent(t, p, "

Summary Next Line. {{% img src=“/not/real” %}}.\nMore text here.

\n\n

Some more text

\n") + checkPageSummary(t, p, "Summary Next Line. . More text here. Some more text") + checkPageType(t, p, "page") + checkPageLayout(t, p, "page/single.html", "single.html") } func TestPageWithMoreTag(t *testing.T) { - p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SUMMARY_DELIMITER_SAME_LINE), "simple.md") - p.Convert() - if err != nil { - t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) - } - checkPageTitle(t, p, "Simple") - checkPageContent(t, p, "

Summary Same Line

\n\n

Some more text

\n") - checkPageSummary(t, p, "

Summary Same Line

\n") - checkPageType(t, p, "page") - checkPageLayout(t, p, "page/single.html", "single.html") + p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_SUMMARY_DELIMITER_SAME_LINE), "simple.md") + p.Convert() + if err != nil { + t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) + } + checkPageTitle(t, p, "Simple") + checkPageContent(t, p, "

Summary Same Line

\n\n

Some more text

\n") + checkPageSummary(t, p, "

Summary Same Line

\n") + checkPageType(t, p, "page") + checkPageLayout(t, p, "page/single.html", "single.html") } func TestPageWithDate(t *testing.T) { - p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_RFC3339_DATE), "simple") - p.Convert() - if err != nil { - t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) - } - d, err := time.Parse(time.RFC3339, "2013-05-17T16:59:30Z") - if err != nil { - t.Fatalf("Unable to prase page.") - } - checkPageDate(t, p, d) + p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_RFC3339_DATE), "simple") + p.Convert() + if err != nil { + t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) + } + d, err := time.Parse(time.RFC3339, "2013-05-17T16:59:30Z") + if err != nil { + t.Fatalf("Unable to prase page.") + } + checkPageDate(t, p, d) } func TestWordCount(t *testing.T) { - p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_LONG_CONTENT), "simple.md") - p.Convert() - if err != nil { - t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) - } + p, err := ReadFrom(strings.NewReader(SIMPLE_PAGE_WITH_LONG_CONTENT), "simple.md") + p.Convert() + if err != nil { + t.Fatalf("Unable to create a page with frontmatter and body content: %s", err) + } - if p.WordCount != 483 { - t.Fatalf("incorrect word count. expected %v, got %v", 483, p.WordCount) - } + if p.WordCount != 483 { + t.Fatalf("incorrect word count. expected %v, got %v", 483, p.WordCount) + } - if p.FuzzyWordCount != 500 { - t.Fatalf("incorrect word count. expected %v, got %v", 500, p.WordCount) - } + if p.FuzzyWordCount != 500 { + t.Fatalf("incorrect word count. expected %v, got %v", 500, p.WordCount) + } - if p.MinRead != 3 { - t.Fatalf("incorrect min read. expected %v, got %v", 3, p.MinRead) - } + if p.ReadingTime != 3 { + t.Fatalf("incorrect min read. expected %v, got %v", 3, p.ReadingTime) + } - checkTruncation(t, p, true, "long page") + checkTruncation(t, p, true, "long page") } func TestCreatePage(t *testing.T) { - var tests = []struct { - r string - }{ - {SIMPLE_PAGE_JSON}, - {SIMPLE_PAGE_JSON_LOOSE}, - {SIMPLE_PAGE_JSON_MULTIPLE}, - //{strings.NewReader(SIMPLE_PAGE_JSON_COMPACT)}, - } + var tests = []struct { + r string + }{ + {SIMPLE_PAGE_JSON}, + {SIMPLE_PAGE_JSON_LOOSE}, + {SIMPLE_PAGE_JSON_MULTIPLE}, + //{strings.NewReader(SIMPLE_PAGE_JSON_COMPACT)}, + } - for _, test := range tests { - if _, err := ReadFrom(strings.NewReader(test.r), "page"); err != nil { - t.Errorf("Unable to parse page: %s", err) - } - } + for _, test := range tests { + if _, err := ReadFrom(strings.NewReader(test.r), "page"); err != nil { + t.Errorf("Unable to parse page: %s", err) + } + } } func TestDegenerateInvalidFrontMatterShortDelim(t *testing.T) { - var tests = []struct { - r string - err string - }{ - {INVALID_FRONT_MATTER_SHORT_DELIM_ENDING, "Unable to read frontmatter at filepos 45: EOF"}, - } - for _, test := range tests { - _, err := ReadFrom(strings.NewReader(test.r), "invalid/front/matter/short/delim") - checkError(t, err, test.err) - } + var tests = []struct { + r string + err string + }{ + {INVALID_FRONT_MATTER_SHORT_DELIM_ENDING, "Unable to read frontmatter at filepos 45: EOF"}, + } + for _, test := range tests { + _, err := ReadFrom(strings.NewReader(test.r), "invalid/front/matter/short/delim") + checkError(t, err, test.err) + } } func TestShouldRenderContent(t *testing.T) { - var tests = []struct { - text string - render bool - }{ - {INVALID_FRONT_MATTER_MISSING, true}, - // TODO how to deal with malformed frontmatter. In this case it'll be rendered as markdown. - {INVALID_FRONT_MATTER_SHORT_DELIM, true}, - {RENDER_NO_FRONT_MATTER, false}, - } + var tests = []struct { + text string + render bool + }{ + {INVALID_FRONT_MATTER_MISSING, true}, + // TODO how to deal with malformed frontmatter. In this case it'll be rendered as markdown. + {INVALID_FRONT_MATTER_SHORT_DELIM, true}, + {RENDER_NO_FRONT_MATTER, false}, + } - for _, test := range tests { - p := pageMust(ReadFrom(strings.NewReader(test.text), "render/front/matter")) - if p.IsRenderable() != test.render { - t.Errorf("expected p.IsRenderable() == %t, got %t", test.render, p.IsRenderable()) - } - } + for _, test := range tests { + p := pageMust(ReadFrom(strings.NewReader(test.text), "render/front/matter")) + if p.IsRenderable() != test.render { + t.Errorf("expected p.IsRenderable() == %t, got %t", test.render, p.IsRenderable()) + } + } } func TestDifferentFrontMatterVarTypes(t *testing.T) { - page, _ := ReadFrom(strings.NewReader(PAGE_WITH_VARIOUS_FRONTMATTER_TYPES), "test/file1.md") + page, _ := ReadFrom(strings.NewReader(PAGE_WITH_VARIOUS_FRONTMATTER_TYPES), "test/file1.md") - dateval, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") - if page.GetParam("a_string") != "bar" { - t.Errorf("frontmatter not handling strings correctly should be %s, got: %s", "bar", page.GetParam("a_string")) - } - if page.GetParam("an_integer") != 1 { - t.Errorf("frontmatter not handling ints correctly should be %s, got: %s", "1", page.GetParam("an_integer")) - } - if page.GetParam("a_float") != 1.3 { - t.Errorf("frontmatter not handling floats correctly should be %s, got: %s", 1.3, page.GetParam("a_float")) - } - if page.GetParam("a_date") != dateval { - t.Errorf("frontmatter not handling dates correctly should be %s, got: %s", dateval, page.GetParam("a_date")) - } + dateval, _ := time.Parse(time.RFC3339, "1979-05-27T07:32:00Z") + if page.GetParam("a_string") != "bar" { + t.Errorf("frontmatter not handling strings correctly should be %s, got: %s", "bar", page.GetParam("a_string")) + } + if page.GetParam("an_integer") != 1 { + t.Errorf("frontmatter not handling ints correctly should be %s, got: %s", "1", page.GetParam("an_integer")) + } + if page.GetParam("a_float") != 1.3 { + t.Errorf("frontmatter not handling floats correctly should be %s, got: %s", 1.3, page.GetParam("a_float")) + } + if page.GetParam("a_date") != dateval { + t.Errorf("frontmatter not handling dates correctly should be %s, got: %s", dateval, page.GetParam("a_date")) + } } func TestDegenerateInvalidFrontMatterLeadingWhitespace(t *testing.T) { - _, err := ReadFrom(strings.NewReader(INVALID_FRONT_MATTER_LEADING_WS), "invalid/front/matter/leading/ws") - if err != nil { - t.Fatalf("Unable to parse front matter given leading whitespace: %s", err) - } + _, err := ReadFrom(strings.NewReader(INVALID_FRONT_MATTER_LEADING_WS), "invalid/front/matter/leading/ws") + if err != nil { + t.Fatalf("Unable to parse front matter given leading whitespace: %s", err) + } } func TestSectionEvaluation(t *testing.T) { - page, _ := ReadFrom(strings.NewReader(SIMPLE_PAGE), "blue/file1.md") - if page.Section != "blue" { - t.Errorf("Section should be %s, got: %s", "blue", page.Section) - } + page, _ := ReadFrom(strings.NewReader(SIMPLE_PAGE), "blue/file1.md") + if page.Section != "blue" { + t.Errorf("Section should be %s, got: %s", "blue", page.Section) + } } func L(s ...string) []string { - return s + return s } func TestLayoutOverride(t *testing.T) { - var ( - 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") - ) - tests := []struct { - content string - path string - expectedLayout []string - }{ - {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) - if err != nil { - t.Fatalf("Unable to parse content:\n%s\n", test.content) - } - if !listEqual(p.Layout(), test.expectedLayout) { - t.Errorf("Layout mismatch. Expected: %s, got: %s", test.expectedLayout, p.Layout()) - } - } + var ( + 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") + ) + tests := []struct { + content string + path string + expectedLayout []string + }{ + {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) + if err != nil { + t.Fatalf("Unable to parse content:\n%s\n", test.content) + } + 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 - } + if len(left) != len(right) { + return false + } - for i := range left { - if left[i] != right[i] { - return false - } - } + for i := range left { + if left[i] != right[i] { + return false + } + } - return true + return true }