Made Feed.CanUpdate() public. This returns true if all cache timeout values have expired and a fresh remote update can be performed. Fixed bug where repeated calls to feed.Fetch() creates duplicate Channels instead of generating a fresh list. Did some minor code householding to replace manual slice appends with the builtin append() function.
This commit is contained in:
parent
24864b01f1
commit
eb15b6a3ef
17
src/atom.go
17
src/atom.go
|
@ -29,16 +29,14 @@ func (this *Feed) readAtom(doc *xmlx.Document) (err os.Error) {
|
||||||
ch.SubTitle.Text = tn.Value
|
ch.SubTitle.Text = tn.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
tn = node.SelectNode(ns, "generator")
|
if tn = node.SelectNode(ns, "generator"); tn != nil {
|
||||||
if tn != nil {
|
|
||||||
ch.Generator = Generator{}
|
ch.Generator = Generator{}
|
||||||
ch.Generator.Uri = tn.GetAttr("", "uri")
|
ch.Generator.Uri = tn.GetAttr("", "uri")
|
||||||
ch.Generator.Version = tn.GetAttr("", "version")
|
ch.Generator.Version = tn.GetAttr("", "version")
|
||||||
ch.Generator.Text = tn.Value
|
ch.Generator.Text = tn.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
tn = node.SelectNode(ns, "author")
|
if tn = node.SelectNode(ns, "author"); tn != nil {
|
||||||
if tn != nil {
|
|
||||||
ch.Author = Author{}
|
ch.Author = Author{}
|
||||||
ch.Author.Name = tn.GetValue("", "name")
|
ch.Author.Name = tn.GetValue("", "name")
|
||||||
ch.Author.Uri = tn.GetValue("", "uri")
|
ch.Author.Uri = tn.GetValue("", "uri")
|
||||||
|
@ -61,14 +59,14 @@ func (this *Feed) readAtom(doc *xmlx.Document) (err os.Error) {
|
||||||
enc := Enclosure{}
|
enc := Enclosure{}
|
||||||
enc.Url = lv.GetAttr("", "href")
|
enc.Url = lv.GetAttr("", "href")
|
||||||
enc.Type = lv.GetAttr("", "type")
|
enc.Type = lv.GetAttr("", "type")
|
||||||
item.addEnclosure(enc)
|
item.Enclosures = append(item.Enclosures, enc)
|
||||||
} else {
|
} else {
|
||||||
lnk := Link{}
|
lnk := Link{}
|
||||||
lnk.Href = lv.GetAttr("", "href")
|
lnk.Href = lv.GetAttr("", "href")
|
||||||
lnk.Rel = lv.GetAttr("", "rel")
|
lnk.Rel = lv.GetAttr("", "rel")
|
||||||
lnk.Type = lv.GetAttr("", "type")
|
lnk.Type = lv.GetAttr("", "type")
|
||||||
lnk.HrefLang = lv.GetAttr("", "hreflang")
|
lnk.HrefLang = lv.GetAttr("", "hreflang")
|
||||||
item.addLink(lnk)
|
item.Links = append(item.Links, lnk)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,18 +76,17 @@ func (this *Feed) readAtom(doc *xmlx.Document) (err os.Error) {
|
||||||
item.Contributors[ci] = cv.GetValue("", "name")
|
item.Contributors[ci] = cv.GetValue("", "name")
|
||||||
}
|
}
|
||||||
|
|
||||||
tn = v.SelectNode(ns, "content")
|
if tn = v.SelectNode(ns, "content"); tn != nil {
|
||||||
if tn != nil {
|
|
||||||
item.Content = Content{}
|
item.Content = Content{}
|
||||||
item.Content.Type = tn.GetAttr("", "type")
|
item.Content.Type = tn.GetAttr("", "type")
|
||||||
item.Content.Lang = tn.GetValue("xml", "lang")
|
item.Content.Lang = tn.GetValue("xml", "lang")
|
||||||
item.Content.Base = tn.GetValue("xml", "base")
|
item.Content.Base = tn.GetValue("xml", "base")
|
||||||
item.Content.Text = tn.Value
|
item.Content.Text = tn.Value
|
||||||
}
|
}
|
||||||
ch.addItem(item)
|
ch.Items = append(ch.Items, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addChannel(ch)
|
this.Channels = append(this.Channels, ch)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,17 +28,3 @@ type Channel struct {
|
||||||
Author Author
|
Author Author
|
||||||
SubTitle SubTitle
|
SubTitle SubTitle
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Channel) addItem(item Item) {
|
|
||||||
c := make([]Item, len(this.Items)+1)
|
|
||||||
copy(c, this.Items)
|
|
||||||
c[len(c)-1] = item
|
|
||||||
this.Items = c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Channel) addLink(l Link) {
|
|
||||||
c := make([]Link, len(this.Links)+1)
|
|
||||||
copy(c, this.Links)
|
|
||||||
c[len(c)-1] = l
|
|
||||||
this.Links = c
|
|
||||||
}
|
|
||||||
|
|
46
src/feed.go
46
src/feed.go
|
@ -26,13 +26,11 @@
|
||||||
package feeder
|
package feeder
|
||||||
|
|
||||||
import "os"
|
import "os"
|
||||||
import "http"
|
|
||||||
import "time"
|
import "time"
|
||||||
import "xmlx"
|
import "xmlx"
|
||||||
import "fmt"
|
import "fmt"
|
||||||
import "strconv"
|
import "strconv"
|
||||||
import "strings"
|
import "strings"
|
||||||
import "io/ioutil"
|
|
||||||
|
|
||||||
type Feed struct {
|
type Feed struct {
|
||||||
// Custom cache timeout in minutes.
|
// Custom cache timeout in minutes.
|
||||||
|
@ -60,46 +58,25 @@ type Feed struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cachetimeout int, enforcecachelimit bool) *Feed {
|
func New(cachetimeout int, enforcecachelimit bool) *Feed {
|
||||||
return &Feed{
|
v := new(Feed)
|
||||||
CacheTimeout: cachetimeout,
|
v.CacheTimeout = cachetimeout
|
||||||
EnforceCacheLimit: enforcecachelimit,
|
v.EnforceCacheLimit = enforcecachelimit
|
||||||
Type: "none",
|
v.Type = "none"
|
||||||
Version: [2]int{0, 0},
|
return v
|
||||||
Channels: make([]Channel, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Feed) addChannel(ch Channel) {
|
|
||||||
c := make([]Channel, len(this.Channels)+1)
|
|
||||||
copy(c, this.Channels)
|
|
||||||
c[len(c)-1] = ch
|
|
||||||
this.Channels = c
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Feed) Fetch(uri string) (err os.Error) {
|
func (this *Feed) Fetch(uri string) (err os.Error) {
|
||||||
if !this.canUpdate() {
|
if !this.CanUpdate() {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch data from remote location.
|
|
||||||
r, _, err := http.Get(uri)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer r.Body.Close()
|
|
||||||
|
|
||||||
var b []byte
|
|
||||||
if b, err = ioutil.ReadAll(r.Body); err != nil {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.Url = uri
|
this.Url = uri
|
||||||
|
this.Channels = nil
|
||||||
|
|
||||||
// Extract type and version of the feed so we can have the appropriate
|
// Extract type and version of the feed so we can have the appropriate
|
||||||
// function parse it (rss 0.91, rss 0.92, rss 2, atom etc).
|
// function parse it (rss 0.91, rss 0.92, rss 2, atom etc).
|
||||||
doc := xmlx.New()
|
doc := xmlx.New()
|
||||||
if err = doc.LoadString(string(b)); err != nil {
|
if err = doc.LoadUri(uri); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.Type, this.Version = this.GetVersionInfo(doc)
|
this.Type, this.Version = this.GetVersionInfo(doc)
|
||||||
|
@ -120,7 +97,12 @@ func (this *Feed) Fetch(uri string) (err os.Error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Feed) canUpdate() bool {
|
// This function returns true or false, depending on whether the CacheTimeout
|
||||||
|
// value has expired or not. Additionally, it will ensure that we adhere to the
|
||||||
|
// RSS spec's SkipDays and SkipHours values (if Feed.EnforceCacheLimit is set to
|
||||||
|
// true). If this function returns true, you can be sure that a fresh feed
|
||||||
|
// update will be performed.
|
||||||
|
func (this *Feed) CanUpdate() bool {
|
||||||
// Make sure we are not within the specified cache-limit.
|
// Make sure we are not within the specified cache-limit.
|
||||||
// This ensures we don't request data too often.
|
// This ensures we don't request data too often.
|
||||||
utc := time.UTC()
|
utc := time.UTC()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package feeder
|
package feeder
|
||||||
|
|
||||||
import "testing"
|
import "testing"
|
||||||
|
import "os"
|
||||||
|
|
||||||
func TestFeed(t *testing.T) {
|
func TestFeed(t *testing.T) {
|
||||||
urilist := []string{
|
urilist := []string{
|
||||||
|
|
14
src/item.go
14
src/item.go
|
@ -19,17 +19,3 @@ type Item struct {
|
||||||
Contributors []string
|
Contributors []string
|
||||||
Content Content
|
Content Content
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *Item) addEnclosure(e Enclosure) {
|
|
||||||
c := make([]Enclosure, len(this.Enclosures)+1)
|
|
||||||
copy(c, this.Enclosures)
|
|
||||||
c[len(c)-1] = e
|
|
||||||
this.Enclosures = c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (this *Item) addLink(l Link) {
|
|
||||||
c := make([]Link, len(this.Links)+1)
|
|
||||||
copy(c, this.Links)
|
|
||||||
c[len(c)-1] = l
|
|
||||||
this.Links = c
|
|
||||||
}
|
|
||||||
|
|
18
src/rss.go
18
src/rss.go
|
@ -52,8 +52,7 @@ func (this *Feed) readRss2(doc *xmlx.Document) (err os.Error) {
|
||||||
ch.SkipDays[i] = mapDay(v.Value)
|
ch.SkipDays[i] = mapDay(v.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
n = node.SelectNode("", "image")
|
if n = node.SelectNode("", "image"); n != nil {
|
||||||
if n != nil {
|
|
||||||
ch.Image.Title = n.GetValue("", "title")
|
ch.Image.Title = n.GetValue("", "title")
|
||||||
ch.Image.Url = n.GetValue("", "url")
|
ch.Image.Url = n.GetValue("", "url")
|
||||||
ch.Image.Link = n.GetValue("", "link")
|
ch.Image.Link = n.GetValue("", "link")
|
||||||
|
@ -62,8 +61,7 @@ func (this *Feed) readRss2(doc *xmlx.Document) (err os.Error) {
|
||||||
ch.Image.Description = n.GetValue("", "description")
|
ch.Image.Description = n.GetValue("", "description")
|
||||||
}
|
}
|
||||||
|
|
||||||
n = node.SelectNode("", "cloud")
|
if n = node.SelectNode("", "cloud"); n != nil {
|
||||||
if n != nil {
|
|
||||||
ch.Cloud = Cloud{}
|
ch.Cloud = Cloud{}
|
||||||
ch.Cloud.Domain = n.GetAttr("", "domain")
|
ch.Cloud.Domain = n.GetAttr("", "domain")
|
||||||
ch.Cloud.Port = n.GetAttri("", "port")
|
ch.Cloud.Port = n.GetAttri("", "port")
|
||||||
|
@ -72,8 +70,7 @@ func (this *Feed) readRss2(doc *xmlx.Document) (err os.Error) {
|
||||||
ch.Cloud.Protocol = n.GetAttr("", "protocol")
|
ch.Cloud.Protocol = n.GetAttr("", "protocol")
|
||||||
}
|
}
|
||||||
|
|
||||||
n = node.SelectNode("", "textInput")
|
if n = node.SelectNode("", "textInput"); n != nil {
|
||||||
if n != nil {
|
|
||||||
ch.TextInput = Input{}
|
ch.TextInput = Input{}
|
||||||
ch.TextInput.Title = n.GetValue("", "title")
|
ch.TextInput.Title = n.GetValue("", "title")
|
||||||
ch.TextInput.Description = n.GetValue("", "description")
|
ch.TextInput.Description = n.GetValue("", "description")
|
||||||
|
@ -92,11 +89,10 @@ func (this *Feed) readRss2(doc *xmlx.Document) (err os.Error) {
|
||||||
for _, v := range list {
|
for _, v := range list {
|
||||||
lnk := Link{}
|
lnk := Link{}
|
||||||
lnk.Href = v.Value
|
lnk.Href = v.Value
|
||||||
i.addLink(lnk)
|
i.Links = append(i.Links, lnk)
|
||||||
}
|
}
|
||||||
|
|
||||||
n = item.SelectNode("", "author")
|
if n = item.SelectNode("", "author"); n != nil {
|
||||||
if n != nil {
|
|
||||||
i.Author = Author{}
|
i.Author = Author{}
|
||||||
i.Author.Name = n.Value
|
i.Author.Name = n.Value
|
||||||
}
|
}
|
||||||
|
@ -127,10 +123,10 @@ func (this *Feed) readRss2(doc *xmlx.Document) (err os.Error) {
|
||||||
i.Source.Text = src.Value
|
i.Source.Text = src.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
ch.addItem(i)
|
ch.Items = append(ch.Items, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addChannel(ch)
|
this.Channels = append(this.Channels, ch)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue