Complete refactor of indexes, move (and rewrite) page sorting to page.go, add tests
authorspf13 <steve.francia@gmail.com>
Fri, 20 Dec 2013 14:10:05 +0000 (09:10 -0500)
committerspf13 <steve.francia@gmail.com>
Fri, 20 Dec 2013 14:10:05 +0000 (09:10 -0500)
hugolib/index.go
hugolib/page.go
hugolib/site.go
hugolib/site_test.go

index f39723e4e3c3c04dd7b1b8f70d7bf80aef6555b9..0189eccb369af93b17268e5dc345073533003d93 100644 (file)
@@ -18,150 +18,153 @@ import (
        "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
-
-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
-       }
-}
-
-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)
+/*
+ * 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
 
-       return ps.pages
+/*
+ * 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
 }
 
-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])
+}
+
+func (wp WeightedPages) Pages() Pages {
+       pages := make(Pages, len(wp))
+       for i := range wp {
+               pages[i] = wp[i].Page
+       }
+       return pages
 }
 
-// Sorting pages
-type PageSorter struct {
-       pages Pages
-       by    func(p1, p2 *Page) bool
+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
+       }
 }
 
-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]) }
+// TODO mimic PagesSorter for WeightedPages
index 50d8786f31a2a36f319a20a17d6e4c987966e8cb..8856c43d9621c9f4e3ea322d67655971c811f1eb 100644 (file)
@@ -74,20 +74,78 @@ type Position struct {
 
 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 {
index d34fa5acf79afc3030c6a24223c784494c15f31e..3bc122e0a506689c496614ddf1daa90e846f5c86 100644 (file)
@@ -315,7 +315,7 @@ func (s *Site) BuildSiteMeta() (err error) {
                                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 @@ func (s *Site) BuildSiteMeta() (err error) {
        }
 
        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 {
index 3f78f7b3ecf4a4f4b68e2391574c19e14d4fef38..5f6fe6e7f1421025df186b2b3dbc92cd3e95bcbe 100644 (file)
@@ -355,7 +355,7 @@ weight = "4"
 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"},
@@ -389,6 +389,27 @@ func TestOrderedPages(t *testing.T) {
        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)
+       }
 }
 
 var PAGE_WITH_WEIGHTED_INDEXES_2 = []byte(`+++