Renamed Indexes to Taxonomies. Old template and config parameters still work.

This commit is contained in:
spf13 2014-04-08 23:15:57 -04:00
parent aae6fa0b6b
commit 93bcddebb3
8 changed files with 171 additions and 150 deletions

View file

@ -84,6 +84,8 @@ func InitializeConfig() {
viper.AddConfigPath(Source) viper.AddConfigPath(Source)
viper.ReadInConfig() viper.ReadInConfig()
viper.RegisterAlias("taxonomies", "indexes")
viper.SetDefault("ContentDir", "content") viper.SetDefault("ContentDir", "content")
viper.SetDefault("LayoutDir", "layouts") viper.SetDefault("LayoutDir", "layouts")
viper.SetDefault("StaticDir", "static") viper.SetDefault("StaticDir", "static")

View file

@ -1,18 +0,0 @@
package hugolib
import (
"strings"
"testing"
)
func TestSitePossibleIndexes(t *testing.T) {
site := new(Site)
page, _ := ReadFrom(strings.NewReader(PAGE_YAML_WITH_INDEXES_A), "path/to/page")
site.Pages = append(site.Pages, page)
indexes := site.possibleIndexes()
if !compareStringSlice(indexes, []string{"tags", "categories"}) {
if !compareStringSlice(indexes, []string{"categories", "tags"}) {
t.Fatalf("possible indexes do not match [tags categories]. Got: %s", indexes)
}
}
}

View file

@ -5,22 +5,22 @@ import (
"testing" "testing"
) )
var PAGE_YAML_WITH_INDEXES_A = `--- var PAGE_YAML_WITH_TAXONOMIES_A = `---
tags: ['a', 'b', 'c'] tags: ['a', 'b', 'c']
categories: 'd' categories: 'd'
--- ---
YAML frontmatter with tags and categories index.` YAML frontmatter with tags and categories taxonomy.`
var PAGE_YAML_WITH_INDEXES_B = `--- var PAGE_YAML_WITH_TAXONOMIES_B = `---
tags: tags:
- "a" - "a"
- "b" - "b"
- "c" - "c"
categories: 'd' categories: 'd'
--- ---
YAML frontmatter with tags and categories index.` YAML frontmatter with tags and categories taxonomy.`
var PAGE_JSON_WITH_INDEXES = `{ var PAGE_JSON_WITH_TAXONOMIES = `{
"categories": "d", "categories": "d",
"tags": [ "tags": [
"a", "a",
@ -30,19 +30,19 @@ var PAGE_JSON_WITH_INDEXES = `{
} }
JSON Front Matter with tags and categories` JSON Front Matter with tags and categories`
var PAGE_TOML_WITH_INDEXES = `+++ var PAGE_TOML_WITH_TAXONOMIES = `+++
tags = [ "a", "b", "c" ] tags = [ "a", "b", "c" ]
categories = "d" categories = "d"
+++ +++
TOML Front Matter with tags and categories` TOML Front Matter with tags and categories`
func TestParseIndexes(t *testing.T) { func TestParseTaxonomies(t *testing.T) {
for _, test := range []string{PAGE_TOML_WITH_INDEXES, for _, test := range []string{PAGE_TOML_WITH_TAXONOMIES,
PAGE_JSON_WITH_INDEXES, PAGE_JSON_WITH_TAXONOMIES,
PAGE_YAML_WITH_INDEXES_A, PAGE_YAML_WITH_TAXONOMIES_A,
PAGE_YAML_WITH_INDEXES_B, PAGE_YAML_WITH_TAXONOMIES_B,
} { } {
p, err := ReadFrom(strings.NewReader(test), "page/with/index") p, err := ReadFrom(strings.NewReader(test), "page/with/taxonomy")
if err != nil { if err != nil {
t.Fatalf("Failed parsing %q: %s", test, err) t.Fatalf("Failed parsing %q: %s", test, err)
} }

View file

@ -48,7 +48,7 @@ var DefaultTimer *nitro.B
// various targets that will get generated. There will be canonical // various targets that will get generated. There will be canonical
// listing. The canonical path can be overruled based on a pattern. // listing. The canonical path can be overruled based on a pattern.
// //
// 3. Indexes are created via configuration and will present some aspect of // 3. Taxonomies are created via configuration and will present some aspect of
// the final page and typically a perm url. // the final page and typically a perm url.
// //
// 4. All Pages are passed through a template based on their desired // 4. All Pages are passed through a template based on their desired
@ -58,9 +58,9 @@ var DefaultTimer *nitro.B
type Site struct { type Site struct {
Pages Pages Pages Pages
Tmpl bundle.Template Tmpl bundle.Template
Indexes IndexList Taxonomies TaxonomyList
Source source.Input Source source.Input
Sections Index Sections Taxonomy
Info SiteInfo Info SiteInfo
Shortcodes map[string]ShortcodeFunc Shortcodes map[string]ShortcodeFunc
timer *nitro.B timer *nitro.B
@ -73,7 +73,8 @@ type Site struct {
type SiteInfo struct { type SiteInfo struct {
BaseUrl template.URL BaseUrl template.URL
Indexes IndexList Taxonomies TaxonomyList
Indexes *TaxonomyList // legacy, should be identical to Taxonomies
Recent *Pages Recent *Pages
LastChange time.Time LastChange time.Time
Title string Title string
@ -147,7 +148,7 @@ func (s *Site) Process() (err error) {
if err = s.BuildSiteMeta(); err != nil { if err = s.BuildSiteMeta(); err != nil {
return return
} }
s.timerStep("build indexes") s.timerStep("build taxonomies")
return return
} }
@ -168,13 +169,13 @@ func (s *Site) Render() (err error) {
return return
} }
s.timerStep("render and write aliases") s.timerStep("render and write aliases")
if err = s.RenderIndexes(); err != nil { if err = s.RenderTaxonomiesLists(); err != nil {
return return
} }
s.timerStep("render and write indexes") s.timerStep("render and write taxonomies")
s.RenderIndexesIndexes() s.RenderListsOfTaxonomyTerms()
s.timerStep("render & write index indexes") s.timerStep("render & write taxonomy lists")
if err = s.RenderLists(); err != nil { if err = s.RenderSectionLists(); err != nil {
return return
} }
s.timerStep("render and write lists") s.timerStep("render and write lists")
@ -299,14 +300,14 @@ func (s *Site) CreatePages() (err error) {
} }
func (s *Site) BuildSiteMeta() (err error) { func (s *Site) BuildSiteMeta() (err error) {
s.Indexes = make(IndexList) s.Taxonomies = make(TaxonomyList)
s.Sections = make(Index) s.Sections = make(Taxonomy)
indexes := viper.GetStringMapString("Indexes") taxonomies := viper.GetStringMapString("Taxonomies")
jww.INFO.Printf("found indexes: %#v\n", indexes) jww.INFO.Printf("found taxonomies: %#v\n", taxonomies)
for _, plural := range indexes { for _, plural := range taxonomies {
s.Indexes[plural] = make(Index) s.Taxonomies[plural] = make(Taxonomy)
for _, p := range s.Pages { for _, p := range s.Pages {
vals := p.GetParam(plural) vals := p.GetParam(plural)
weight := p.GetParam(plural + "_weight") weight := p.GetParam(plural + "_weight")
@ -320,15 +321,15 @@ func (s *Site) BuildSiteMeta() (err error) {
for _, idx := range v { for _, idx := range v {
x := WeightedPage{weight.(int), p} x := WeightedPage{weight.(int), p}
s.Indexes[plural].Add(idx, x) s.Taxonomies[plural].Add(idx, x)
} }
} else { } else {
jww.ERROR.Printf("Invalid %s in %s\n", plural, p.File.FileName) jww.ERROR.Printf("Invalid %s in %s\n", plural, p.File.FileName)
} }
} }
} }
for k := range s.Indexes[plural] { for k := range s.Taxonomies[plural] {
s.Indexes[plural][k].Sort() s.Taxonomies[plural][k].Sort()
} }
} }
@ -340,7 +341,8 @@ func (s *Site) BuildSiteMeta() (err error) {
s.Sections[k].Sort() s.Sections[k].Sort()
} }
s.Info.Indexes = s.Indexes s.Info.Taxonomies = s.Taxonomies
s.Info.Indexes = &s.Taxonomies
if len(s.Pages) == 0 { if len(s.Pages) == 0 {
return return
@ -355,11 +357,11 @@ func (s *Site) BuildSiteMeta() (err error) {
return return
} }
func (s *Site) possibleIndexes() (indexes []string) { func (s *Site) possibleTaxonomies() (taxonomies []string) {
for _, p := range s.Pages { for _, p := range s.Pages {
for k := range p.Params { for k := range p.Params {
if !inStringArray(indexes, k) { if !inStringArray(taxonomies, k) {
indexes = append(indexes, k) taxonomies = append(taxonomies, k)
} }
} }
} }
@ -375,6 +377,7 @@ func inStringArray(arr []string, el string) bool {
return false return false
} }
// Render shell pages that simply have a redirect in the header
func (s *Site) RenderAliases() error { func (s *Site) RenderAliases() error {
for _, p := range s.Pages { for _, p := range s.Pages {
for _, a := range p.Aliases { for _, a := range p.Aliases {
@ -390,12 +393,13 @@ func (s *Site) RenderAliases() error {
return nil return nil
} }
// Render pages each corresponding to a markdown file
func (s *Site) RenderPages() (err error) { func (s *Site) RenderPages() (err error) {
var wg sync.WaitGroup var wg sync.WaitGroup
for _, page := range s.Pages { for _, page := range s.Pages {
wg.Add(1) wg.Add(1)
go func(p *Page) (err error) { go func(p *Page) (err error) {
var layout []string var layouts []string
defer wg.Done() defer wg.Done()
if !p.IsRenderable() { if !p.IsRenderable() {
@ -404,13 +408,13 @@ func (s *Site) RenderPages() (err error) {
if err != nil { if err != nil {
return err return err
} }
layout = append(layout, self) layouts = append(layouts, self)
} else { } else {
layout = append(layout, p.Layout()...) layouts = append(layouts, p.Layout()...)
layout = append(layout, "_default/single.html") layouts = append(layouts, "_default/single.html")
} }
return s.render(p, p.TargetPath(), layout...) return s.render(p, p.TargetPath(), layouts...)
}(page) }(page)
} }
wg.Wait() wg.Wait()
@ -421,12 +425,14 @@ func (s *Site) RenderPages() (err error) {
return nil return nil
} }
func (s *Site) RenderIndexes() (err error) { // Render the listing pages based on the meta data
// each unique term within a taxonomy will have a page created
func (s *Site) RenderTaxonomiesLists() (err error) {
var wg sync.WaitGroup var wg sync.WaitGroup
indexes := viper.GetStringMapString("Indexes") taxonomies := viper.GetStringMapString("Taxonomies")
for sing, pl := range indexes { for sing, pl := range taxonomies {
for key, oo := range s.Indexes[pl] { for key, oo := range s.Taxonomies[pl] {
wg.Add(1) wg.Add(1)
go func(k string, o WeightedPages, singular string, plural string) (err error) { go func(k string, o WeightedPages, singular string, plural string) (err error) {
defer wg.Done() defer wg.Done()
@ -437,8 +443,8 @@ func (s *Site) RenderIndexes() (err error) {
n.Date = o[0].Page.Date n.Date = o[0].Page.Date
n.Data[singular] = o n.Data[singular] = o
n.Data["Pages"] = o.Pages() n.Data["Pages"] = o.Pages()
layout := "indexes/" + singular + ".html" err = s.render(n, base+".html", "taxonomies/"+singular+".html", "indexes/"+singular+".html")
err = s.render(n, base+".html", layout) //TODO add , "_default/taxonomy.html", "_default/list.html"
if err != nil { if err != nil {
return err return err
} }
@ -447,6 +453,7 @@ func (s *Site) RenderIndexes() (err error) {
// XML Feed // XML Feed
s.setUrls(n, base+".xml") s.setUrls(n, base+".xml")
err := s.render(n, base+".xml", "rss.xml") err := s.render(n, base+".xml", "rss.xml")
// TODO add "taxonomy.xml", "_internal/rss.xml"
if err != nil { if err != nil {
return err return err
} }
@ -459,22 +466,25 @@ func (s *Site) RenderIndexes() (err error) {
return nil return nil
} }
func (s *Site) RenderIndexesIndexes() (err error) { // Render a page per taxonomy that lists the terms for that taxonomy
layout := "indexes/indexes.html" func (s *Site) RenderListsOfTaxonomyTerms() (err error) {
if s.Tmpl.Lookup(layout) != nil { layouts := []string{"taxonomies/termslist.html", "indexes/indexes.html"}
// TODO add "_default/termsList.html", "_default/termslist.html"
indexes := viper.GetStringMapString("Indexes") // TODO add support for unique taxonomy terms list (`single`terms.html)
for singular, plural := range indexes { if s.layoutExists(layouts...) {
taxonomies := viper.GetStringMapString("Taxonomies")
for singular, plural := range taxonomies {
n := s.NewNode() n := s.NewNode()
n.Title = strings.Title(plural) n.Title = strings.Title(plural)
s.setUrls(n, plural) s.setUrls(n, plural)
n.Data["Singular"] = singular n.Data["Singular"] = singular
n.Data["Plural"] = plural n.Data["Plural"] = plural
n.Data["Index"] = s.Indexes[plural] n.Data["Terms"] = s.Taxonomies[plural]
// keep the following just for legacy reasons // keep the following just for legacy reasons
n.Data["OrderedIndex"] = s.Indexes[plural] n.Data["OrderedIndex"] = n.Data["Terms"]
n.Data["Index"] = n.Data["Terms"]
err := s.render(n, plural+"/index.html", layout) err := s.render(n, plural+"/index.html", layouts...)
if err != nil { if err != nil {
return err return err
} }
@ -483,16 +493,16 @@ func (s *Site) RenderIndexesIndexes() (err error) {
return return
} }
func (s *Site) RenderLists() error { // Render a page for each section
func (s *Site) RenderSectionLists() error {
for section, data := range s.Sections { for section, data := range s.Sections {
n := s.NewNode() n := s.NewNode()
n.Title = strings.Title(inflect.Pluralize(section)) n.Title = strings.Title(inflect.Pluralize(section))
s.setUrls(n, section) s.setUrls(n, section)
n.Date = data[0].Page.Date n.Date = data[0].Page.Date
n.Data["Pages"] = data.Pages() n.Data["Pages"] = data.Pages()
layout := "indexes/" + section + ".html"
err := s.render(n, section, layout, "_default/indexes.html") err := s.render(n, section, "section/"+section+".html", "indexes/"+section+".html", "_default/section.html", "_default/list.html", "_default/indexes.html")
if err != nil { if err != nil {
return err return err
} }
@ -501,6 +511,8 @@ func (s *Site) RenderLists() error {
// XML Feed // XML Feed
s.setUrls(n, section+".xml") s.setUrls(n, section+".xml")
err = s.render(n, section+".xml", "rss.xml") err = s.render(n, section+".xml", "rss.xml")
//TODO add section specific rss
// TODO add internal rss
if err != nil { if err != nil {
return err return err
} }
@ -533,6 +545,7 @@ func (s *Site) RenderHomePage() error {
n.Date = s.Pages[0].Date n.Date = s.Pages[0].Date
} }
err := s.render(n, ".xml", "rss.xml") err := s.render(n, ".xml", "rss.xml")
// TODO add internal RSS
if err != nil { if err != nil {
return err return err
} }
@ -551,10 +564,10 @@ func (s *Site) RenderHomePage() error {
func (s *Site) Stats() { func (s *Site) Stats() {
jww.FEEDBACK.Printf("%d pages created \n", len(s.Pages)) jww.FEEDBACK.Printf("%d pages created \n", len(s.Pages))
indexes := viper.GetStringMapString("Indexes") taxonomies := viper.GetStringMapString("Taxonomies")
for _, pl := range indexes { for _, pl := range taxonomies {
jww.FEEDBACK.Printf("%d %s index created\n", len(s.Indexes[pl]), pl) jww.FEEDBACK.Printf("%d %s created\n", len(s.Taxonomies[pl]), pl)
} }
} }
@ -587,10 +600,16 @@ func (s *Site) NewNode() *Node {
} }
} }
func (s *Site) layoutExists(layouts ...string) bool {
_, found := s.findFirstLayout(layouts...)
return found
}
func (s *Site) render(d interface{}, out string, layouts ...string) (err error) { func (s *Site) render(d interface{}, out string, layouts ...string) (err error) {
layout := s.findFirstLayout(layouts...) layout, found := s.findFirstLayout(layouts...)
if layout == "" { if found == false {
jww.WARN.Printf("Unable to locate layout: %s\n", layouts) jww.WARN.Printf("Unable to locate layout: %s\n", layouts)
return return
} }
@ -634,13 +653,13 @@ func (s *Site) render(d interface{}, out string, layouts ...string) (err error)
return s.WritePublic(out, outBuffer) return s.WritePublic(out, outBuffer)
} }
func (s *Site) findFirstLayout(layouts ...string) (layout string) { func (s *Site) findFirstLayout(layouts ...string) (string, bool) {
for _, layout = range layouts { for _, layout := range layouts {
if s.Tmpl.Lookup(layout) != nil { if s.Tmpl.Lookup(layout) != nil {
return return layout, true
} }
} }
return "" return "", false
} }
func (s *Site) renderThing(d interface{}, layout string, w io.Writer) error { func (s *Site) renderThing(d interface{}, layout string, w io.Writer) error {

View file

@ -423,7 +423,7 @@ func TestOrderedPages(t *testing.T) {
} }
} }
var PAGE_WITH_WEIGHTED_INDEXES_2 = []byte(`+++ var PAGE_WITH_WEIGHTED_TAXONOMIES_2 = []byte(`+++
tags = [ "a", "b", "c" ] tags = [ "a", "b", "c" ]
tags_weight = 22 tags_weight = 22
categories = ["d"] categories = ["d"]
@ -432,7 +432,7 @@ categories_weight = 44
+++ +++
Front Matter with weighted tags and categories`) Front Matter with weighted tags and categories`)
var PAGE_WITH_WEIGHTED_INDEXES_1 = []byte(`+++ var PAGE_WITH_WEIGHTED_TAXONOMIES_1 = []byte(`+++
tags = [ "a" ] tags = [ "a" ]
tags_weight = 33 tags_weight = 33
title = "bar" title = "bar"
@ -443,7 +443,7 @@ date = 1979-05-27T07:32:00Z
+++ +++
Front Matter with weighted tags and categories`) Front Matter with weighted tags and categories`)
var PAGE_WITH_WEIGHTED_INDEXES_3 = []byte(`+++ var PAGE_WITH_WEIGHTED_TAXONOMIES_3 = []byte(`+++
title = "bza" title = "bza"
categories = [ "e" ] categories = [ "e" ]
categories_weight = 11 categories_weight = 11
@ -452,21 +452,21 @@ date = 2010-05-27T07:32:00Z
+++ +++
Front Matter with weighted tags and categories`) Front Matter with weighted tags and categories`)
func TestWeightedIndexes(t *testing.T) { func TestWeightedTaxonomies(t *testing.T) {
files := make(map[string][]byte) files := make(map[string][]byte)
target := &target.InMemoryTarget{Files: files} target := &target.InMemoryTarget{Files: files}
sources := []source.ByteSource{ sources := []source.ByteSource{
{"sect/doc1.md", PAGE_WITH_WEIGHTED_INDEXES_1, "sect"}, {"sect/doc1.md", PAGE_WITH_WEIGHTED_TAXONOMIES_1, "sect"},
{"sect/doc2.md", PAGE_WITH_WEIGHTED_INDEXES_2, "sect"}, {"sect/doc2.md", PAGE_WITH_WEIGHTED_TAXONOMIES_2, "sect"},
{"sect/doc3.md", PAGE_WITH_WEIGHTED_INDEXES_3, "sect"}, {"sect/doc3.md", PAGE_WITH_WEIGHTED_TAXONOMIES_3, "sect"},
} }
indexes := make(map[string]string) taxonomies := make(map[string]string)
indexes["tag"] = "tags" taxonomies["tag"] = "tags"
indexes["category"] = "categories" taxonomies["category"] = "categories"
viper.Set("baseurl", "http://auth/bub") viper.Set("baseurl", "http://auth/bub")
viper.Set("indexes", indexes) viper.Set("taxonomies", taxonomies)
s := &Site{ s := &Site{
Target: target, Target: target,
Source: &source.InMemorySource{ByteSource: sources}, Source: &source.InMemorySource{ByteSource: sources},
@ -481,15 +481,15 @@ func TestWeightedIndexes(t *testing.T) {
t.Fatalf("Unable to build site metadata: %s", err) t.Fatalf("Unable to build site metadata: %s", err)
} }
if s.Indexes["tags"]["a"][0].Page.Title != "foo" { if s.Taxonomies["tags"]["a"][0].Page.Title != "foo" {
t.Errorf("Pages in unexpected order, 'foo' expected first, got '%v'", s.Indexes["tags"]["a"][0].Page.Title) t.Errorf("Pages in unexpected order, 'foo' expected first, got '%v'", s.Taxonomies["tags"]["a"][0].Page.Title)
} }
if s.Indexes["categories"]["d"][0].Page.Title != "bar" { if s.Taxonomies["categories"]["d"][0].Page.Title != "bar" {
t.Errorf("Pages in unexpected order, 'bar' expected first, got '%v'", s.Indexes["categories"]["d"][0].Page.Title) t.Errorf("Pages in unexpected order, 'bar' expected first, got '%v'", s.Taxonomies["categories"]["d"][0].Page.Title)
} }
if s.Indexes["categories"]["e"][0].Page.Title != "bza" { if s.Taxonomies["categories"]["e"][0].Page.Title != "bza" {
t.Errorf("Pages in unexpected order, 'bza' expected first, got '%v'", s.Indexes["categories"]["e"][0].Page.Title) t.Errorf("Pages in unexpected order, 'bza' expected first, got '%v'", s.Taxonomies["categories"]["e"][0].Page.Title)
} }
} }

View file

@ -71,8 +71,8 @@ func TestPageCount(t *testing.T) {
t.Errorf("Unable to build site metadata: %s", err) t.Errorf("Unable to build site metadata: %s", err)
} }
if err := s.RenderLists(); err != nil { if err := s.RenderSectionLists(); err != nil {
t.Errorf("Unable to render site lists: %s", err) t.Errorf("Unable to render section lists: %s", err)
} }
if err := s.RenderAliases(); err != nil { if err := s.RenderAliases(); err != nil {

View file

@ -20,18 +20,18 @@ import (
) )
/* /*
* An index list is a list of all indexes and their values * An taxonomy list is a list of all taxonomies and their values
* EG. List['tags'] => TagIndex (from above) * EG. List['tags'] => TagTaxonomy (from above)
*/ */
type IndexList map[string]Index type TaxonomyList map[string]Taxonomy
/* /*
* An index is a map of keywords to a list of pages. * An taxonomy is a map of keywords to a list of pages.
* For example * For example
* TagIndex['technology'] = WeightedPages * TagTaxonomy['technology'] = WeightedPages
* TagIndex['go'] = WeightedPages2 * TagTaxonomy['go'] = WeightedPages2
*/ */
type Index map[string]WeightedPages type Taxonomy map[string]WeightedPages
/* /*
* A list of Pages with their corresponding (and relative) weight * A list of Pages with their corresponding (and relative) weight
@ -44,108 +44,108 @@ type WeightedPage struct {
} }
/* /*
* This is another representation of an Index using an array rather than a map. * This is another representation of an Taxonomy using an array rather than a map.
* Important because you can't order a map. * Important because you can't order a map.
*/ */
type OrderedIndex []OrderedIndexEntry type OrderedTaxonomy []OrderedTaxonomyEntry
/* /*
* Similar to an element of an Index, but with the key embedded (as name) * Similar to an element of an Taxonomy, but with the key embedded (as name)
* Eg: {Name: Technology, WeightedPages: Indexedpages} * Eg: {Name: Technology, WeightedPages: Taxonomyedpages}
*/ */
type OrderedIndexEntry struct { type OrderedTaxonomyEntry struct {
Name string Name string
WeightedPages WeightedPages WeightedPages WeightedPages
} }
// KeyPrep... Indexes should be case insensitive. Can make it easily conditional later. // KeyPrep... Taxonomies should be case insensitive. Can make it easily conditional later.
func kp(in string) string { func kp(in string) string {
return helpers.MakePath(in) return helpers.MakePath(in)
} }
func (i Index) Get(key string) WeightedPages { return i[kp(key)] } func (i Taxonomy) Get(key string) WeightedPages { return i[kp(key)] }
func (i Index) Count(key string) int { return len(i[kp(key)]) } func (i Taxonomy) Count(key string) int { return len(i[kp(key)]) }
func (i Index) Add(key string, w WeightedPage) { func (i Taxonomy) Add(key string, w WeightedPage) {
key = kp(key) key = kp(key)
i[key] = append(i[key], w) i[key] = append(i[key], w)
} }
// Returns an ordered index with a non defined order // Returns an ordered taxonomy with a non defined order
func (i Index) IndexArray() OrderedIndex { func (i Taxonomy) TaxonomyArray() OrderedTaxonomy {
ies := make([]OrderedIndexEntry, len(i)) ies := make([]OrderedTaxonomyEntry, len(i))
count := 0 count := 0
for k, v := range i { for k, v := range i {
ies[count] = OrderedIndexEntry{Name: k, WeightedPages: v} ies[count] = OrderedTaxonomyEntry{Name: k, WeightedPages: v}
count++ count++
} }
return ies return ies
} }
// Returns an ordered index sorted by key name // Returns an ordered taxonomy sorted by key name
func (i Index) Alphabetical() OrderedIndex { func (i Taxonomy) Alphabetical() OrderedTaxonomy {
name := func(i1, i2 *OrderedIndexEntry) bool { name := func(i1, i2 *OrderedTaxonomyEntry) bool {
return i1.Name < i2.Name return i1.Name < i2.Name
} }
ia := i.IndexArray() ia := i.TaxonomyArray()
OIby(name).Sort(ia) OIby(name).Sort(ia)
return ia return ia
} }
// Returns an ordered index sorted by # of pages per key // Returns an ordered taxonomy sorted by # of pages per key
func (i Index) ByCount() OrderedIndex { func (i Taxonomy) ByCount() OrderedTaxonomy {
count := func(i1, i2 *OrderedIndexEntry) bool { count := func(i1, i2 *OrderedTaxonomyEntry) bool {
return len(i1.WeightedPages) > len(i2.WeightedPages) return len(i1.WeightedPages) > len(i2.WeightedPages)
} }
ia := i.IndexArray() ia := i.TaxonomyArray()
OIby(count).Sort(ia) OIby(count).Sort(ia)
return ia return ia
} }
// Helper to move the page access up a level // Helper to move the page access up a level
func (ie OrderedIndexEntry) Pages() []*Page { func (ie OrderedTaxonomyEntry) Pages() []*Page {
return ie.WeightedPages.Pages() return ie.WeightedPages.Pages()
} }
func (ie OrderedIndexEntry) Count() int { func (ie OrderedTaxonomyEntry) Count() int {
return len(ie.WeightedPages) return len(ie.WeightedPages)
} }
/* /*
* Implementation of a custom sorter for OrderedIndexes * Implementation of a custom sorter for OrderedTaxonomies
*/ */
// A type to implement the sort interface for IndexEntries. // A type to implement the sort interface for TaxonomyEntries.
type orderedIndexSorter struct { type orderedTaxonomySorter struct {
index OrderedIndex taxonomy OrderedTaxonomy
by OIby by OIby
} }
// Closure used in the Sort.Less method. // Closure used in the Sort.Less method.
type OIby func(i1, i2 *OrderedIndexEntry) bool type OIby func(i1, i2 *OrderedTaxonomyEntry) bool
func (by OIby) Sort(index OrderedIndex) { func (by OIby) Sort(taxonomy OrderedTaxonomy) {
ps := &orderedIndexSorter{ ps := &orderedTaxonomySorter{
index: index, taxonomy: taxonomy,
by: by, // The Sort method's receiver is the function (closure) that defines the sort order. by: by, // The Sort method's receiver is the function (closure) that defines the sort order.
} }
sort.Sort(ps) sort.Sort(ps)
} }
// Len is part of sort.Interface. // Len is part of sort.Interface.
func (s *orderedIndexSorter) Len() int { func (s *orderedTaxonomySorter) Len() int {
return len(s.index) return len(s.taxonomy)
} }
// Swap is part of sort.Interface. // Swap is part of sort.Interface.
func (s *orderedIndexSorter) Swap(i, j int) { func (s *orderedTaxonomySorter) Swap(i, j int) {
s.index[i], s.index[j] = s.index[j], s.index[i] s.taxonomy[i], s.taxonomy[j] = s.taxonomy[j], s.taxonomy[i]
} }
// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter. // Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s *orderedIndexSorter) Less(i, j int) bool { func (s *orderedTaxonomySorter) Less(i, j int) bool {
return s.by(&s.index[i], &s.index[j]) return s.by(&s.taxonomy[i], &s.taxonomy[j])
} }
func (wp WeightedPages) Pages() Pages { func (wp WeightedPages) Pages() Pages {

18
hugolib/taxonomy_test.go Normal file
View file

@ -0,0 +1,18 @@
package hugolib
import (
"strings"
"testing"
)
func TestSitePossibleTaxonomies(t *testing.T) {
site := new(Site)
page, _ := ReadFrom(strings.NewReader(PAGE_YAML_WITH_TAXONOMIES_A), "path/to/page")
site.Pages = append(site.Pages, page)
taxonomies := site.possibleTaxonomies()
if !compareStringSlice(taxonomies, []string{"tags", "categories"}) {
if !compareStringSlice(taxonomies, []string{"categories", "tags"}) {
t.Fatalf("possible taxonomies do not match [tags categories]. Got: %s", taxonomies)
}
}
}