ref: 70745e8cb5f0c97b1d405ef1301aba77e90de17c
parent: 6aa3e512280e7b11d44ac6effb4e01e4e2c7af66
author: spf13 <[email protected]>
date: Fri Dec 20 04:10:05 EST 2013
Complete refactor of indexes, move (and rewrite) page sorting to page.go, add tests
--- a/hugolib/index.go
+++ b/hugolib/index.go
@@ -18,150 +18,153 @@
"sort"
)
-type WeightedIndexEntry struct {
+/*
+ * An index list is a list of all indexes and their values
+ * EG. List['tags'] => TagIndex (from above)
+ */
+type IndexList map[string]Index
+
+/*
+ * An index is a map of keywords to a list of pages.
+ * For example
+ * TagIndex['technology'] = WeightedPages
+ * TagIndex['golang'] = WeightedPages2
+ */
+type Index map[string]WeightedPages
+
+/*
+ * A list of Pages with their corresponding (and relative) weight
+ * [{Weight: 30, Page: *1}, {Weight: 40, Page: *2}]
+ */
+type WeightedPages []WeightedPage
+type WeightedPage struct {
Weight int
Page *Page
}
-type IndexedPages []WeightedIndexEntry
+/*
+ * This is another representation of an Index using an array rather than a map.
+ * Important because you can't order a map.
+ */
+type OrderedIndex []OrderedIndexEntry
-func (p IndexedPages) Len() int { return len(p) }
-func (p IndexedPages) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
-func (p IndexedPages) Sort() { sort.Sort(p) }
-func (p IndexedPages) Count() int { return len(p) }
-func (p IndexedPages) Less(i, j int) bool {
- if p[i].Weight == p[j].Weight {
- return p[i].Page.Date.Unix() > p[j].Page.Date.Unix()
- } else {
- return p[i].Weight < p[j].Weight
- }
+/*
+ * Similar to an element of an Index, but with the key embedded (as name)
+ * Eg: {Name: Technology, WeightedPages: Indexedpages}
+ */
+type OrderedIndexEntry struct {
+ Name string
+ WeightedPages WeightedPages
}
-func (ip IndexedPages) Pages() Pages {
- pages := make(Pages, len(ip))
- for i := range ip {
- pages[i] = ip[i].Page
- }
- return pages
-}
-
-func (ip IndexedPages) PagesByDate(asc bool) Pages {
- by := func(p1, p2 *Page) bool {
- return p1.Date.Unix() < p2.Date.Unix()
- }
-
- if asc == false {
- by = func(p1, p2 *Page) bool {
- return p1.Date.Unix() > p2.Date.Unix()
- }
- }
-
- ps := &PageSorter{
- pages: ip.Pages(),
- by: by,
- }
-
- sort.Sort(ps)
-
- return ps.pages
-}
-
-type Index map[string]IndexedPages
-type IndexList map[string]Index
-
// KeyPrep... Indexes should be case insensitive. Can make it easily conditional later.
func kp(in string) string {
return helpers.Urlize(in)
}
-func (i Index) Get(key string) IndexedPages { return i[kp(key)] }
-func (i Index) Count(key string) int { return len(i[kp(key)]) }
-func (i Index) Add(key string, w WeightedIndexEntry) {
+func (i Index) Get(key string) WeightedPages { return i[kp(key)] }
+func (i Index) Count(key string) int { return len(i[kp(key)]) }
+func (i Index) Add(key string, w WeightedPage) {
key = kp(key)
i[key] = append(i[key], w)
}
-func (i Index) IndexArray() IndexEntries {
- ies := make([]IndexEntry, len(i))
+// Returns an ordered index with a non defined order
+func (i Index) IndexArray() OrderedIndex {
+ ies := make([]OrderedIndexEntry, len(i))
count := 0
for k, v := range i {
- ies[count] = IndexEntry{Name: k, WeightedPages: v}
+ ies[count] = OrderedIndexEntry{Name: k, WeightedPages: v}
count++
}
return ies
}
-func (i Index) Alphabetical() IndexEntries {
- name := func(i1, i2 *IndexEntry) bool {
+// Returns an ordered index sorted by key name
+func (i Index) Alphabetical() OrderedIndex {
+ name := func(i1, i2 *OrderedIndexEntry) bool {
return i1.Name < i2.Name
}
ia := i.IndexArray()
- By(name).Sort(ia)
+ OIby(name).Sort(ia)
return ia
}
-func (i Index) ByCount() IndexEntries {
- count := func(i1, i2 *IndexEntry) bool {
+// Returns an ordered index sorted by # of pages per key
+func (i Index) ByCount() OrderedIndex {
+ count := func(i1, i2 *OrderedIndexEntry) bool {
return len(i1.WeightedPages) > len(i2.WeightedPages)
}
ia := i.IndexArray()
- By(count).Sort(ia)
+ OIby(count).Sort(ia)
return ia
}
-type IndexEntry struct {
- Name string
- WeightedPages IndexedPages
-}
-
-func (ie IndexEntry) Pages() []*Page {
+// Helper to move the page access up a level
+func (ie OrderedIndexEntry) Pages() []*Page {
return ie.WeightedPages.Pages()
}
-func (ie IndexEntry) Count() int {
+func (ie OrderedIndexEntry) Count() int {
return len(ie.WeightedPages)
}
-type IndexEntries []IndexEntry
+/*
+ * Implementation of a custom sorter for OrderedIndexes
+ */
-type By func(i1, i2 *IndexEntry) bool
+// A type to implement the sort interface for IndexEntries.
+type orderedIndexSorter struct {
+ index OrderedIndex
+ by OIby
+}
-func (by By) Sort(indexEntrys []IndexEntry) {
- ps := &indexEntrySorter{
- indexEntrys: indexEntrys,
- by: by, // The Sort method's receiver is the function (closure) that defines the sort order.
+// Closure used in the Sort.Less method.
+type OIby func(i1, i2 *OrderedIndexEntry) bool
+
+func (by OIby) Sort(index OrderedIndex) {
+ ps := &orderedIndexSorter{
+ index: index,
+ by: by, // The Sort method's receiver is the function (closure) that defines the sort order.
}
sort.Sort(ps)
}
-type indexEntrySorter struct {
- indexEntrys []IndexEntry
- by func(p1, p2 *IndexEntry) bool // Closure used in the Less method.
-}
-
// Len is part of sort.Interface.
-func (s *indexEntrySorter) Len() int {
- return len(s.indexEntrys)
+func (s *orderedIndexSorter) Len() int {
+ return len(s.index)
}
// Swap is part of sort.Interface.
-func (s *indexEntrySorter) Swap(i, j int) {
- s.indexEntrys[i], s.indexEntrys[j] = s.indexEntrys[j], s.indexEntrys[i]
+func (s *orderedIndexSorter) Swap(i, j int) {
+ s.index[i], s.index[j] = s.index[j], s.index[i]
}
// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
-func (s *indexEntrySorter) Less(i, j int) bool {
- return s.by(&s.indexEntrys[i], &s.indexEntrys[j])
+func (s *orderedIndexSorter) Less(i, j int) bool {
+ return s.by(&s.index[i], &s.index[j])
}
-// Sorting pages
-type PageSorter struct {
- pages Pages
- by func(p1, p2 *Page) bool
+func (wp WeightedPages) Pages() Pages {
+ pages := make(Pages, len(wp))
+ for i := range wp {
+ pages[i] = wp[i].Page
+ }
+ return pages
}
-func (ps *PageSorter) Len() int { return len(ps.pages) }
-func (ps *PageSorter) Swap(i, j int) { ps.pages[i], ps.pages[j] = ps.pages[j], ps.pages[i] }
-func (ps *PageSorter) Less(i, j int) bool { return ps.by(ps.pages[i], ps.pages[j]) }
+func (p WeightedPages) Len() int { return len(p) }
+func (p WeightedPages) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (p WeightedPages) Sort() { sort.Sort(p) }
+func (p WeightedPages) Count() int { return len(p) }
+func (p WeightedPages) Less(i, j int) bool {
+ if p[i].Weight == p[j].Weight {
+ return p[i].Page.Date.Unix() > p[j].Page.Date.Unix()
+ } else {
+ return p[i].Weight < p[j].Weight
+ }
+}
+
+// TODO mimic PagesSorter for WeightedPages
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -74,20 +74,78 @@
type Pages []*Page
-func (p Pages) Len() int { return len(p) }
-func (p Pages) Less(i, j int) bool {
- if p[i].Weight == p[j].Weight {
- return p[i].Date.Unix() > p[j].Date.Unix()
+/*
+ * Implementation of a custom sorter for Pages
+ */
+
+// A type to implement the sort interface for Pages
+type PageSorter struct {
+ 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)
+}
+
+var DefaultPageSort = func(p1, p2 *Page) bool {
+ if p1.Weight == p2.Weight {
+ return p1.Date.Unix() > p2.Date.Unix()
} else {
- return p[i].Weight > p[j].Weight
+ return p1.Weight < p2.Weight
}
}
-func (p Pages) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
+func (ps *PageSorter) Len() int { return len(ps.pages) }
+func (ps *PageSorter) Swap(i, j int) { ps.pages[i], ps.pages[j] = ps.pages[j], ps.pages[i] }
-// TODO eliminate unnecessary things
-func (p Pages) Sort() { sort.Sort(p) }
-func (p Pages) Limit(n int) Pages { return p[0:n] }
+// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
+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)
+}
+
+func (p Pages) Limit(n int) Pages {
+ if len(p) < n {
+ return p[0:n]
+ } else {
+ return p
+ }
+}
+
+func (p Pages) ByDate() Pages {
+ date := func(p1, p2 *Page) bool {
+ return p1.Date.Unix() < p2.Date.Unix()
+ }
+
+ PageBy(date).Sort(p)
+ return p
+}
+
+func (p Pages) ByLength() Pages {
+ length := func(p1, p2 *Page) bool {
+ return len(p1.Content) < len(p2.Content)
+ }
+
+ 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]
+ }
+
+ return p
+}
func (p Page) Plain() string {
if len(p.plain) == 0 {
--- a/hugolib/site.go
+++ b/hugolib/site.go
@@ -315,7 +315,7 @@
v, ok := vals.([]string)
if ok {
for _, idx := range v {
- x := WeightedIndexEntry{weight.(int), p}
+ x := WeightedPage{weight.(int), p}
s.Indexes[plural].Add(idx, x)
}
@@ -332,7 +332,7 @@
}
for i, p := range s.Pages {
- s.Sections.Add(p.Section, WeightedIndexEntry{s.Pages[i].Weight, s.Pages[i]})
+ s.Sections.Add(p.Section, WeightedPage{s.Pages[i].Weight, s.Pages[i]})
}
for k := range s.Sections {
--- a/hugolib/site_test.go
+++ b/hugolib/site_test.go
@@ -355,7 +355,7 @@
title = "Four"
date = "2012-01-01"
+++
-Front Matter with Ordered Pages 4`)
+Front Matter with Ordered Pages 4. This is longer content`)
var WEIGHTED_SOURCES = []source.ByteSource{
{"sect/doc1.md", WEIGHTED_PAGE_1, "sect"},
@@ -388,6 +388,27 @@
if s.Sections["sect"][1].Page.Title != "Three" || s.Sections["sect"][2].Page.Title != "Four" {
t.Errorf("Pages in unexpected order. Second should be '%s', got '%s'", "Three", s.Sections["sect"][1].Page.Title)
+ }
+
+ bydate := s.Pages.ByDate()
+
+ if bydate[0].Title != "One" {
+ t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bydate[0].Title)
+ }
+
+ rev := bydate.Reverse()
+ if rev[0].Title != "Three" {
+ t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Three", rev[0].Title)
+ }
+
+ bylength := s.Pages.ByLength()
+ if bylength[0].Title != "One" {
+ t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "One", bylength[0].Title)
+ }
+
+ rbylength := bylength.Reverse()
+ if rbylength[0].Title != "Four" {
+ t.Errorf("Pages in unexpected order. First should be '%s', got '%s'", "Four", rbylength[0].Title)
}
}