From bec4bdae992841f011239dac8c685e13470a90f3 Mon Sep 17 00:00:00 2001 From: bep Date: Tue, 31 Mar 2015 21:33:24 +0100 Subject: Return error on wrong use of the Paginator `Paginate`now returns error when 1) `.Paginate` is called after `.Paginator` 2) `.Paginate` is repeatedly called with different arguments This should help remove some confusion. This commit also introduces DistinctErrorLogger, to prevent spamming the log for duplicate rendering errors from the pagers. Fixes #993 --- hugolib/pagination.go | 76 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 65 insertions(+), 11 deletions(-) (limited to 'hugolib/pagination.go') diff --git a/hugolib/pagination.go b/hugolib/pagination.go index 650a355cf..f5380a337 100644 --- a/hugolib/pagination.go +++ b/hugolib/pagination.go @@ -22,6 +22,7 @@ import ( "html/template" "math" "path" + "reflect" ) type pager struct { @@ -37,8 +38,10 @@ type paginator struct { paginatedPages []Pages pagers paginationURLFactory - total int - size int + total int + size int + source interface{} + options []interface{} } type paginationURLFactory func(int) string @@ -164,6 +167,8 @@ func (n *Node) Paginator(options ...interface{}) (*pager, error) { if len(pagers) > 0 { // the rest of the nodes will be created later n.paginator = pagers[0] + n.paginator.source = "paginator" + n.paginator.options = options n.Site.addToPaginationPageCount(uint64(n.paginator.TotalPages())) } @@ -212,6 +217,8 @@ func (n *Node) Paginate(seq interface{}, options ...interface{}) (*pager, error) if len(pagers) > 0 { // the rest of the nodes will be created later n.paginator = pagers[0] + n.paginator.source = seq + n.paginator.options = options n.Site.addToPaginationPageCount(uint64(n.paginator.TotalPages())) } @@ -221,6 +228,14 @@ func (n *Node) Paginate(seq interface{}, options ...interface{}) (*pager, error) return nil, initError } + if n.paginator.source == "paginator" { + return nil, errors.New("a Paginator was previously built for this Node without filters; look for earlier .Paginator usage") + } else { + if !reflect.DeepEqual(options, n.paginator.options) || !probablyEqualPageLists(n.paginator.source, seq) { + return nil, errors.New("invoked multiple times with different arguments") + } + } + return n.paginator, nil } @@ -248,25 +263,64 @@ func paginatePages(seq interface{}, pagerSize int, section string) (pagers, erro return nil, errors.New("'paginate' configuration setting must be positive to paginate") } - var pages Pages + pages, err := toPages(seq) + if err != nil { + return nil, err + } + + urlFactory := newPaginationURLFactory(section) + paginator, _ := newPaginator(pages, pagerSize, urlFactory) + pagers := paginator.Pagers() + + return pagers, nil +} + +func toPages(seq interface{}) (Pages, error) { switch seq.(type) { case Pages: - pages = seq.(Pages) + return seq.(Pages), nil case *Pages: - pages = *(seq.(*Pages)) + return *(seq.(*Pages)), nil case WeightedPages: - pages = (seq.(WeightedPages)).Pages() + return (seq.(WeightedPages)).Pages(), nil case PageGroup: - pages = (seq.(PageGroup)).Pages + return (seq.(PageGroup)).Pages, nil default: return nil, errors.New(fmt.Sprintf("unsupported type in paginate, got %T", seq)) } +} - urlFactory := newPaginationURLFactory(section) - paginator, _ := newPaginator(pages, pagerSize, urlFactory) - pagers := paginator.Pagers() +// probablyEqual checks page lists for probable equality. +// It may return false positives. +// The motivation behind this is to avoid potential costly reflect.DeepEqual +// when "probably" is good enough. +func probablyEqualPageLists(a1 interface{}, a2 interface{}) bool { - return pagers, nil + if a1 == nil || a2 == nil { + return a1 == a2 + } + + p1, err1 := toPages(a1) + p2, err2 := toPages(a2) + + // probably the same wrong type + if err1 != nil && err2 != nil { + return true + } + + if err1 != nil || err2 != nil { + return false + } + + if len(p1) != len(p2) { + return false + } + + if len(p1) == 0 { + return true + } + + return p1[0] == p2[0] } func newPaginator(pages Pages, size int, urlFactory paginationURLFactory) (*paginator, error) { -- cgit v1.2.3