diff --git a/feed.go b/feed.go index 93d6449..3474289 100644 --- a/feed.go +++ b/feed.go @@ -4,6 +4,7 @@ import ( "encoding/json" "encoding/xml" "io" + "sort" "time" ) @@ -134,3 +135,11 @@ func (f *Feed) WriteJSON(w io.Writer) error { e.SetIndent("", " ") return e.Encode(feed) } + +// Sort sorts the Items in the feed with the given less function. +func (f *Feed) Sort(less func(a, b *Item) bool) { + lessFunc := func(i, j int) bool { + return less(f.Items[i], f.Items[j]) + } + sort.SliceStable(f.Items, lessFunc) +} diff --git a/feed_test.go b/feed_test.go index e7cb0a8..50da71f 100644 --- a/feed_test.go +++ b/feed_test.go @@ -255,3 +255,228 @@ func TestFeed(t *testing.T) { t.Errorf("JSON not what was expected. Got:\n||%s||\n\nExpected:\n||%s||\n", got, jsonOutput) } } + +var atomOutputSorted = ` + jmoiron.net blog + http://jmoiron.net/blog + 2013-01-16T21:52:35-05:00 + This work is copyright © Benjamin Button + discussion about tech, footie, photos + + + Jason Moiron + jmoiron@jmoiron.net + + + Limiting Concurrency in Go + 2013-01-18T21:52:35-05:00 + tag:jmoiron.net,2013-01-18:/blog/limiting-concurrency-in-go/ + + + + + Logic-less Template Redux + 2013-01-17T21:52:35-05:00 + tag:jmoiron.net,2013-01-17:/blog/logicless-template-redux/ + + + + + Idiomatic Code Reuse in Go + 2013-01-17T09:52:35-05:00 + tag:jmoiron.net,2013-01-17:/blog/idiomatic-code-reuse-in-go/ + + + + + Never Gonna Give You Up Mp3 + 2013-01-17T07:52:35-05:00 + tag:example.com,2013-01-17:/RickRoll.mp3 + + + + + String formatting in Go + 2013-01-16T21:52:35-05:00 + tag:example.com,2013-01-16:/strings + + + +` + +var rssOutputSorted = ` + + jmoiron.net blog + http://jmoiron.net/blog + discussion about tech, footie, photos + This work is copyright © Benjamin Button + jmoiron@jmoiron.net (Jason Moiron) + Wed, 16 Jan 2013 21:52:35 -0500 + + Limiting Concurrency in Go + http://jmoiron.net/blog/limiting-concurrency-in-go/ + + Fri, 18 Jan 2013 21:52:35 -0500 + + + Logic-less Template Redux + http://jmoiron.net/blog/logicless-template-redux/ + + Thu, 17 Jan 2013 21:52:35 -0500 + + + Idiomatic Code Reuse in Go + http://jmoiron.net/blog/idiomatic-code-reuse-in-go/ + + Thu, 17 Jan 2013 09:52:35 -0500 + + + Never Gonna Give You Up Mp3 + http://example.com/RickRoll.mp3 + + Thu, 17 Jan 2013 07:52:35 -0500 + + + String formatting in Go + http://example.com/strings + + Wed, 16 Jan 2013 21:52:35 -0500 + + +` + +var jsonOutputSorted = `{ + "version": "https://jsonfeed.org/version/1", + "title": "jmoiron.net blog", + "home_page_url": "http://jmoiron.net/blog", + "description": "discussion about tech, footie, photos", + "author": { + "name": "Jason Moiron" + }, + "items": [ + { + "id": "", + "url": "http://jmoiron.net/blog/limiting-concurrency-in-go/", + "title": "Limiting Concurrency in Go", + "date_published": "2013-01-18T21:52:35-05:00" + }, + { + "id": "", + "url": "http://jmoiron.net/blog/logicless-template-redux/", + "title": "Logic-less Template Redux", + "date_published": "2013-01-17T21:52:35-05:00" + }, + { + "id": "", + "url": "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/", + "title": "Idiomatic Code Reuse in Go", + "date_published": "2013-01-17T09:52:35-05:00" + }, + { + "id": "", + "url": "http://example.com/RickRoll.mp3", + "title": "Never Gonna Give You Up Mp3", + "date_published": "2013-01-17T07:52:35-05:00" + }, + { + "id": "", + "url": "http://example.com/strings", + "title": "String formatting in Go", + "date_published": "2013-01-16T21:52:35-05:00" + } + ] +}` + +func TestFeedSorted(t *testing.T) { + now, err := time.Parse(time.RFC3339, "2013-01-16T21:52:35-05:00") + if err != nil { + t.Error(err) + } + tz := time.FixedZone("EST", -5*60*60) + now = now.In(tz) + + feed := &Feed{ + Title: "jmoiron.net blog", + Link: &Link{Href: "http://jmoiron.net/blog"}, + Description: "discussion about tech, footie, photos", + Author: &Author{Name: "Jason Moiron", Email: "jmoiron@jmoiron.net"}, + Created: now, + Copyright: "This work is copyright © Benjamin Button", + } + + feed.Items = []*Item{ + { + Title: "Limiting Concurrency in Go", + Link: &Link{Href: "http://jmoiron.net/blog/limiting-concurrency-in-go/"}, + Created: now.Add(time.Duration(time.Hour * 48)), + }, + { + Title: "Logic-less Template Redux", + Link: &Link{Href: "http://jmoiron.net/blog/logicless-template-redux/"}, + Created: now.Add(time.Duration(time.Hour * 24)), + }, + { + Title: "Idiomatic Code Reuse in Go", + Link: &Link{Href: "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/"}, + Created: now.Add(time.Duration(time.Hour * 12)), + }, + { + Title: "Never Gonna Give You Up Mp3", + Link: &Link{Href: "http://example.com/RickRoll.mp3"}, + Created: now.Add(time.Duration(time.Hour * 10)), + }, + { + Title: "String formatting in Go", + Link: &Link{Href: "http://example.com/strings"}, + Created: now, + }} + + feed.Sort(func(a, b *Item) bool { + return a.Created.After(b.Created) + }) + atom, err := feed.ToAtom() + if err != nil { + t.Errorf("unexpected error encoding Atom: %v", err) + } + if atom != atomOutputSorted { + t.Errorf("Atom not what was expected. Got:\n%s\n\nExpected:\n%s\n", atom, atomOutputSorted) + } + var buf bytes.Buffer + if err := feed.WriteAtom(&buf); err != nil { + t.Errorf("unexpected error writing Atom: %v", err) + } + if got := buf.String(); got != atomOutputSorted { + t.Errorf("Atom not what was expected. Got:\n%s\n\nExpected:\n%s\n", got, atomOutputSorted) + } + + rss, err := feed.ToRss() + if err != nil { + t.Errorf("unexpected error encoding RSS: %v", err) + } + + if rss != rssOutputSorted { + t.Errorf("Rss not what was expected. Got:\n%s\n\nExpected:\n%s\n", rss, rssOutputSorted) + } + buf.Reset() + if err := feed.WriteRss(&buf); err != nil { + t.Errorf("unexpected error writing RSS: %v", err) + } + if got := buf.String(); got != rssOutputSorted { + t.Errorf("Rss not what was expected. Got:\n%s\n\nExpected:\n%s\n", got, rssOutputSorted) + } + + json, err := feed.ToJSON() + if err != nil { + t.Errorf("unexpected error encoding JSON: %v", err) + } + if json != jsonOutputSorted { + t.Errorf("JSON not what was expected. Got:\n%s\n\nExpected:\n%s\n", json, jsonOutputSorted) + } + buf.Reset() + if err := feed.WriteJSON(&buf); err != nil { + t.Errorf("unexpected error writing JSON: %v", err) + } + if got := buf.String(); got != jsonOutputSorted+"\n" { //json.Encode appends a newline after the JSON output: https://github.com/golang/go/commit/6f25f1d4c901417af1da65e41992d71c30f64f8f#diff-50848cbd686f250623a2ef6ddb07e157 + t.Errorf("JSON not what was expected. Got:\n||%s||\n\nExpected:\n||%s||\n", got, jsonOutputSorted) + } +}