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)
+ }
+}