jeffrafter-feedzirra 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,54 @@
1
+ require 'digest/md5'
2
+
3
+ module Feedzirra
4
+ module FeedEntryUtilities
5
+ module Sanitize
6
+ def sanitize!
7
+ self.replace(sanitize) unless self.nil?
8
+ end
9
+
10
+ def sanitize
11
+ Dryopteris.sanitize(self)
12
+ end
13
+ end
14
+
15
+ attr_reader :published
16
+
17
+ def parse_datetime(string)
18
+ DateTime.parse(string).feed_utils_to_gm_time
19
+ end
20
+
21
+ def published=(val)
22
+ @published = parse_datetime(val)
23
+ end
24
+
25
+ def content
26
+ @content.extend(Sanitize)
27
+ end
28
+
29
+ def title
30
+ @title.extend(Sanitize)
31
+ end
32
+
33
+ def author
34
+ @author.extend(Sanitize)
35
+ end
36
+
37
+ def summary
38
+ @summary.extend(Sanitize)
39
+ end
40
+
41
+ def sanitize!
42
+ self.title.sanitize!
43
+ self.author.sanitize!
44
+ self.content.sanitize!
45
+ self.summary.sanitize!
46
+ end
47
+
48
+ def checksum
49
+ Digest::MD5.hexdigest("#{title}--#{url}--#{summary}--#{content}")
50
+ end
51
+
52
+ alias_method :last_modified, :published
53
+ end
54
+ end
@@ -0,0 +1,71 @@
1
+ module Feedzirra
2
+ module FeedUtilities
3
+ UPDATABLE_ATTRIBUTES = %w(title feed_url url last_modified)
4
+
5
+ attr_writer :new_entries, :updated, :last_modified
6
+ attr_accessor :etag
7
+
8
+ def last_modified
9
+ @last_modified ||= begin
10
+ entry = entries.reject {|e| e.published.nil? }.sort_by { |entry| entry.published if entry.published }.last
11
+ entry ? entry.published : nil
12
+ end
13
+ end
14
+
15
+ def updated?
16
+ @updated
17
+ end
18
+
19
+ def new_entries
20
+ @new_entries ||= []
21
+ end
22
+
23
+ def has_new_entries?
24
+ new_entries.size > 0
25
+ end
26
+
27
+ def update_from_feed(feed)
28
+ self.new_entries += find_new_entries_for(feed)
29
+ self.entries.unshift(*self.new_entries)
30
+
31
+ updated! if UPDATABLE_ATTRIBUTES.any? { |name| update_attribute(feed, name) }
32
+ end
33
+
34
+ def update_attribute(feed, name)
35
+ old_value, new_value = send(name), feed.send(name)
36
+
37
+ if old_value != new_value
38
+ send("#{name}=", new_value)
39
+ end
40
+ end
41
+
42
+ def sanitize_entries!
43
+ entries.each {|entry| entry.sanitize!}
44
+ end
45
+
46
+ private
47
+
48
+ def updated!
49
+ @updated = true
50
+ end
51
+
52
+ def find_new_entries_for(feed)
53
+ # this implementation is a hack, which is why it's so ugly.
54
+ # it's to get around the fact that not all feeds have a published date.
55
+ # however, they're always ordered with the newest one first.
56
+ # So we go through the entries just parsed and insert each one as a new entry
57
+ # until we get to one that has the same url as the the newest for the feed
58
+ latest_entry = self.entries.first
59
+ found_new_entries = []
60
+ feed.entries.each do |entry|
61
+ break if entry.url == latest_entry.url
62
+ found_new_entries << entry
63
+ end
64
+ found_new_entries
65
+ end
66
+
67
+ def existing_entry?(test_entry)
68
+ entries.any? { |entry| entry.url == test_entry.url }
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,15 @@
1
+ module Feedzirra
2
+ class RDF
3
+ include SAXMachine
4
+ include FeedUtilities
5
+ element :title
6
+ element :link, :as => :url
7
+ elements :item, :as => :entries, :class => RDFEntry
8
+
9
+ attr_accessor :feed_url
10
+
11
+ def self.able_to_parse?(xml)
12
+ xml =~ /(rdf\:RDF)|(#{Regexp.escape("http://purl.org/rss/1.0")})|(rss version\=\"0\.9.?\")/ || false
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ module Feedzirra
2
+ class RDFEntry
3
+ include SAXMachine
4
+ include FeedEntryUtilities
5
+ element :title
6
+ element :link, :as => :url
7
+ element :"dc:creator", :as => :author
8
+ element :"content:encoded", :as => :content
9
+ element :description, :as => :summary
10
+ element :"dc:date", :as => :published
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ module Feedzirra
2
+ class RSS
3
+ include SAXMachine
4
+ include FeedUtilities
5
+ element :title
6
+ element :link, :as => :url
7
+ elements :item, :as => :entries, :class => RSSEntry
8
+
9
+ attr_accessor :feed_url
10
+
11
+ def self.able_to_parse?(xml)
12
+ xml =~ /\<rss|rdf/
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ module Feedzirra
2
+ class RSSEntry
3
+ include SAXMachine
4
+ include FeedEntryUtilities
5
+ element :title
6
+ element :link, :as => :url
7
+
8
+ element :"dc:creator", :as => :author
9
+ element :"content:encoded", :as => :content
10
+ element :description, :as => :summary
11
+
12
+ element :pubDate, :as => :published
13
+ element :"dc:date", :as => :published
14
+ elements :category, :as => :categories
15
+ end
16
+ end
@@ -0,0 +1,37 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Feedzirra::AtomEntry do
4
+ before(:each) do
5
+ # I don't really like doing it this way because these unit test should only rely on AtomEntry,
6
+ # but this is actually how it should work. You would never just pass entry xml straight to the AtomEnry
7
+ @entry = Feedzirra::Atom.parse(sample_atom_feed).entries.first
8
+ end
9
+
10
+ it "should parse the title" do
11
+ @entry.title.should == "AWS Job: Architect & Designer Position in Turkey"
12
+ end
13
+
14
+ it "should parse the url" do
15
+ @entry.url.should == "http://aws.typepad.com/aws/2009/01/aws-job-architect-designer-position-in-turkey.html"
16
+ end
17
+
18
+ it "should parse the author" do
19
+ @entry.author.should == "AWS Editor"
20
+ end
21
+
22
+ it "should parse the content" do
23
+ @entry.content.should == sample_atom_entry_content
24
+ end
25
+
26
+ it "should provide a summary" do
27
+ @entry.summary.should == "Late last year an entrepreneur from Turkey visited me at Amazon HQ in Seattle. We talked about his plans to use AWS as part of his new social video portal startup. I won't spill any beans before he's ready to..."
28
+ end
29
+
30
+ it "should parse the published date" do
31
+ @entry.published.to_s.should == "Fri Jan 16 18:21:00 UTC 2009"
32
+ end
33
+
34
+ it "should parse the categories" do
35
+ @entry.categories.should == ['Turkey', 'Seattle']
36
+ end
37
+ end
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Feedzirra::AtomFeedBurnerEntry do
4
+ before(:each) do
5
+ # I don't really like doing it this way because these unit test should only rely on AtomEntry,
6
+ # but this is actually how it should work. You would never just pass entry xml straight to the AtomEnry
7
+ @entry = Feedzirra::AtomFeedBurner.parse(sample_feedburner_atom_feed).entries.first
8
+ end
9
+
10
+ it "should parse the title" do
11
+ @entry.title.should == "Making a Ruby C library even faster"
12
+ end
13
+
14
+ it "should be able to fetch a url via the 'alternate' rel if no origLink exists" do
15
+ entry = Feedzirra::AtomFeedBurner.parse(File.read("#{File.dirname(__FILE__)}/../sample_feeds/PaulDixExplainsNothingAlternate.xml")).entries.first
16
+ entry.url.should == 'http://feeds.feedburner.com/~r/PaulDixExplainsNothing/~3/519925023/making-a-ruby-c-library-even-faster.html'
17
+ end
18
+
19
+ it "should parse the url" do
20
+ @entry.url.should == "http://www.pauldix.net/2009/01/making-a-ruby-c-library-even-faster.html"
21
+ end
22
+
23
+ it "should parse the author" do
24
+ @entry.author.should == "Paul Dix"
25
+ end
26
+
27
+ it "should parse the content" do
28
+ @entry.content.should == sample_feedburner_atom_entry_content
29
+ end
30
+
31
+ it "should provide a summary" do
32
+ @entry.summary.should == "Last week I released the first version of a SAX based XML parsing library called SAX-Machine. It uses Nokogiri, which uses libxml, so it's pretty fast. However, I felt that it could be even faster. The only question was how..."
33
+ end
34
+
35
+ it "should parse the published date" do
36
+ @entry.published.to_s.should == "Thu Jan 22 15:50:22 UTC 2009"
37
+ end
38
+
39
+ it "should parse the categories" do
40
+ @entry.categories.should == ['Ruby', 'Another Category']
41
+ end
42
+ end
@@ -0,0 +1,39 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Feedzirra::AtomFeedBurner do
4
+ describe "#will_parse?" do
5
+ it "should return true for a feedburner atom feed" do
6
+ Feedzirra::AtomFeedBurner.should be_able_to_parse(sample_feedburner_atom_feed)
7
+ end
8
+
9
+ it "should return false for an rdf feed" do
10
+ Feedzirra::AtomFeedBurner.should_not be_able_to_parse(sample_rdf_feed)
11
+ end
12
+
13
+ it "should return false for a regular atom feed" do
14
+ Feedzirra::AtomFeedBurner.should_not be_able_to_parse(sample_atom_feed)
15
+ end
16
+ end
17
+
18
+ describe "parsing" do
19
+ before(:each) do
20
+ @feed = Feedzirra::AtomFeedBurner.parse(sample_feedburner_atom_feed)
21
+ end
22
+
23
+ it "should parse the title" do
24
+ @feed.title.should == "Paul Dix Explains Nothing"
25
+ end
26
+
27
+ it "should parse the url" do
28
+ @feed.url.should == "http://www.pauldix.net/"
29
+ end
30
+
31
+ it "should parse the feed_url" do
32
+ @feed.feed_url.should == "http://feeds.feedburner.com/PaulDixExplainsNothing"
33
+ end
34
+
35
+ it "should parse entries" do
36
+ @feed.entries.size.should == 5
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Feedzirra::Atom do
4
+ describe "#will_parse?" do
5
+ it "should return true for an atom feed" do
6
+ Feedzirra::Atom.should be_able_to_parse(sample_atom_feed)
7
+ end
8
+
9
+ it "should return false for an rdf feed" do
10
+ Feedzirra::Atom.should_not be_able_to_parse(sample_rdf_feed)
11
+ end
12
+ end
13
+
14
+ describe "parsing" do
15
+ before(:each) do
16
+ @feed = Feedzirra::Atom.parse(sample_atom_feed)
17
+ end
18
+
19
+ it "should parse the title" do
20
+ @feed.title.should == "Amazon Web Services Blog"
21
+ end
22
+
23
+ it "should parse the url" do
24
+ @feed.url.should == "http://aws.typepad.com/aws/"
25
+ end
26
+
27
+ it "should parse the feed_url" do
28
+ @feed.feed_url.should == "http://aws.typepad.com/aws/atom.xml"
29
+ end
30
+
31
+ it "should parse entries" do
32
+ @feed.entries.size.should == 10
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,52 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Feedzirra::FeedUtilities do
4
+ before(:each) do
5
+ @klass = Class.new do
6
+ include Feedzirra::FeedEntryUtilities
7
+ end
8
+ end
9
+
10
+ describe "handling dates" do
11
+ it "should parse an ISO 8601 formatted datetime into Time" do
12
+ time = @klass.new.parse_datetime("2008-02-20T8:05:00-010:00")
13
+ time.class.should == Time
14
+ time.to_s.should == "Wed Feb 20 18:05:00 UTC 2008"
15
+ end
16
+ end
17
+
18
+ describe "sanitizing" do
19
+ before(:each) do
20
+ @feed = Feedzirra::Feed.parse(sample_atom_feed)
21
+ @entry = @feed.entries.first
22
+ end
23
+
24
+ it "should provide a sanitized title" do
25
+ new_title = "<script>" + @entry.title
26
+ @entry.title = new_title
27
+ @entry.title.sanitize.should == Dryopteris.sanitize(new_title)
28
+ end
29
+
30
+ it "should sanitize content in place" do
31
+ new_content = "<script>" + @entry.content
32
+ @entry.content = new_content.dup
33
+ @entry.content.sanitize!.should == Dryopteris.sanitize(new_content)
34
+ @entry.content.should == Dryopteris.sanitize(new_content)
35
+ end
36
+
37
+ it "should sanitize things in place" do
38
+ @entry.title += "<script>"
39
+ @entry.author += "<script>"
40
+ @entry.content += "<script>"
41
+
42
+ cleaned_title = Dryopteris.sanitize(@entry.title)
43
+ cleaned_author = Dryopteris.sanitize(@entry.author)
44
+ cleaned_content = Dryopteris.sanitize(@entry.content)
45
+
46
+ @entry.sanitize!
47
+ @entry.title.should == cleaned_title
48
+ @entry.author.should == cleaned_author
49
+ @entry.content.should == cleaned_content
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,251 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Feedzirra::Feed do
4
+ describe "#parse" do # many of these tests are redundant with the specific feed type tests, but I put them here for completeness
5
+ context "when there's an available parser" do
6
+ it "should parse an rdf feed" do
7
+ feed = Feedzirra::Feed.parse(sample_rdf_feed)
8
+ feed.title.should == "HREF Considered Harmful"
9
+ feed.entries.first.published.to_s.should == "Tue Sep 02 19:50:07 UTC 2008"
10
+ feed.entries.size.should == 10
11
+ end
12
+
13
+ it "should parse an rss feed" do
14
+ feed = Feedzirra::Feed.parse(sample_rss_feed)
15
+ feed.title.should == "Tender Lovemaking"
16
+ feed.entries.first.published.to_s.should == "Thu Dec 04 17:17:49 UTC 2008"
17
+ feed.entries.size.should == 10
18
+ end
19
+
20
+ it "should parse an atom feed" do
21
+ feed = Feedzirra::Feed.parse(sample_atom_feed)
22
+ feed.title.should == "Amazon Web Services Blog"
23
+ feed.entries.first.published.to_s.should == "Fri Jan 16 18:21:00 UTC 2009"
24
+ feed.entries.size.should == 10
25
+ end
26
+
27
+ it "should parse an feedburner atom feed" do
28
+ feed = Feedzirra::Feed.parse(sample_feedburner_atom_feed)
29
+ feed.title.should == "Paul Dix Explains Nothing"
30
+ feed.entries.first.published.to_s.should == "Thu Jan 22 15:50:22 UTC 2009"
31
+ feed.entries.size.should == 5
32
+ end
33
+ end
34
+
35
+ context "when there's no available parser" do
36
+ it "raises Feedzirra::NoParserAvailable" do
37
+ proc {
38
+ Feedzirra::Feed.parse("I'm an invalid feed")
39
+ }.should raise_error(Feedzirra::NoParserAvailable)
40
+ end
41
+ end
42
+
43
+ it "should parse an feedburner rss feed" do
44
+ feed = Feedzirra::Feed.parse(sample_rss_feed_burner_feed)
45
+ feed.title.should == "Sam Harris: Author, Philosopher, Essayist, Atheist"
46
+ feed.entries.first.published.to_s.should == "Tue Jan 13 17:20:28 UTC 2009"
47
+ feed.entries.size.should == 10
48
+ end
49
+ end
50
+
51
+ describe "#determine_feed_parser_for_xml" do
52
+ it "should return the Feedzirra::Atom class for an atom feed" do
53
+ Feedzirra::Feed.determine_feed_parser_for_xml(sample_atom_feed).should == Feedzirra::Atom
54
+ end
55
+
56
+ it "should return the Feedzirra::AtomFeedBurner class for an atom feedburner feed" do
57
+ Feedzirra::Feed.determine_feed_parser_for_xml(sample_feedburner_atom_feed).should == Feedzirra::AtomFeedBurner
58
+ end
59
+
60
+ it "should return the Feedzirra::RSS class for an rdf/rss 1.0 feed" do
61
+ Feedzirra::Feed.determine_feed_parser_for_xml(sample_rdf_feed).should == Feedzirra::RSS
62
+ end
63
+
64
+ it "should return the Feedzirra::RSS class for an rss feedburner feed" do
65
+ Feedzirra::Feed.determine_feed_parser_for_xml(sample_rss_feed_burner_feed).should == Feedzirra::RSS
66
+ end
67
+
68
+ it "should return the Feedzirra::RSS object for an rss 2.0 feed" do
69
+ Feedzirra::Feed.determine_feed_parser_for_xml(sample_rss_feed).should == Feedzirra::RSS
70
+ end
71
+ end
72
+
73
+ describe "adding feed types" do
74
+ it "should prioritize added feed types over the built in ones" do
75
+ feed_text = "Atom asdf"
76
+ Feedzirra::Atom.should be_able_to_parse(feed_text)
77
+ new_feed_type = Class.new do
78
+ def self.able_to_parse?(val)
79
+ true
80
+ end
81
+ end
82
+ new_feed_type.should be_able_to_parse(feed_text)
83
+ Feedzirra::Feed.add_feed_class(new_feed_type)
84
+ Feedzirra::Feed.determine_feed_parser_for_xml(feed_text).should == new_feed_type
85
+
86
+ # this is a hack so that this doesn't break the rest of the tests
87
+ Feedzirra::Feed.feed_classes.reject! {|o| o == new_feed_type }
88
+ end
89
+ end
90
+
91
+ describe "header parsing" do
92
+ before(:each) do
93
+ @header = "HTTP/1.0 200 OK\r\nDate: Thu, 29 Jan 2009 03:55:24 GMT\r\nServer: Apache\r\nX-FB-Host: chi-write6\r\nLast-Modified: Wed, 28 Jan 2009 04:10:32 GMT\r\nETag: ziEyTl4q9GH04BR4jgkImd0GvSE\r\nP3P: CP=\"ALL DSP COR NID CUR OUR NOR\"\r\nConnection: close\r\nContent-Type: text/xml;charset=utf-8\r\n\r\n"
94
+ end
95
+
96
+ it "should parse out an etag" do
97
+ Feedzirra::Feed.etag_from_header(@header).should == "ziEyTl4q9GH04BR4jgkImd0GvSE"
98
+ end
99
+
100
+ it "should return nil if there is no etag in header" do
101
+ Feedzirra::Feed.etag_from_header("foo").should be_nil
102
+ end
103
+
104
+ it "should parse out a last-modified date" do
105
+ Feedzirra::Feed.last_modified_from_header(@header).should == Time.parse("Wed, 28 Jan 2009 04:10:32 GMT")
106
+ end
107
+
108
+ it "should return nil if there is no last-modified in header" do
109
+ Feedzirra::Feed.last_modified_from_header("foo").should be_nil
110
+ end
111
+ end
112
+
113
+ describe "fetching feeds" do
114
+ before(:each) do
115
+ @paul_feed_url = "http://feeds.feedburner.com/PaulDixExplainsNothing"
116
+ @trotter_feed_url = "http://feeds2.feedburner.com/trottercashion"
117
+ end
118
+
119
+ describe "handling many feeds" do
120
+ it "should break a large number into more manageable blocks of 40"
121
+ it "should add to the queue as feeds finish (instead of waiting for each block of 40 to finsih)"
122
+ end
123
+
124
+ describe "#fetch_raw" do
125
+ it "should take :user_agent as an option"
126
+ it "should take :if_modified_since as an option"
127
+ it "should take :if_none_match as an option"
128
+ it "should take an optional on_success lambda"
129
+ it "should take an optional on_failure lambda"
130
+
131
+ it "should return raw xml" do
132
+ Feedzirra::Feed.fetch_raw(@paul_feed_url).should =~ /^#{Regexp.escape('<?xml version="1.0" encoding="UTF-8"?>')}/
133
+ end
134
+
135
+ it "should take multiple feed urls and return a hash of urls and response xml" do
136
+ results = Feedzirra::Feed.fetch_raw([@paul_feed_url, @trotter_feed_url])
137
+ results.keys.should include(@paul_feed_url)
138
+ results.keys.should include(@trotter_feed_url)
139
+ results[@paul_feed_url].should =~ /Paul Dix/
140
+ results[@trotter_feed_url].should =~ /Trotter Cashion/
141
+ end
142
+
143
+ it "should always return a hash when passed an array" do
144
+ results = Feedzirra::Feed.fetch_raw([@paul_feed_url])
145
+ results.class.should == Hash
146
+ end
147
+ end
148
+
149
+ describe "#fetch_and_parse" do
150
+ it "should return a feed object for a single url" do
151
+ feed = Feedzirra::Feed.fetch_and_parse(@paul_feed_url)
152
+ feed.title.should == "Paul Dix Explains Nothing"
153
+ end
154
+
155
+ it "should set the feed_url to the new url if redirected" do
156
+ feed = Feedzirra::Feed.fetch_and_parse("http://tinyurl.com/tenderlovemaking")
157
+ feed.feed_url.should == "http://tenderlovemaking.com/feed/"
158
+ end
159
+
160
+ it "should set the feed_url for an rdf feed" do
161
+ feed = Feedzirra::Feed.fetch_and_parse("http://www.avibryant.com/rss.xml")
162
+ feed.feed_url.should == "http://www.avibryant.com/rss.xml"
163
+ end
164
+
165
+ it "should set the feed_url for an rss feed" do
166
+ feed = Feedzirra::Feed.fetch_and_parse("http://tenderlovemaking.com/feed/")
167
+ feed.feed_url.should == "http://tenderlovemaking.com/feed/"
168
+ end
169
+
170
+ it "should return a hash of feed objects with the passed in feed_url for the key and parsed feed for the value for multiple feeds" do
171
+ feeds = Feedzirra::Feed.fetch_and_parse([@paul_feed_url, @trotter_feed_url])
172
+ feeds.size.should == 2
173
+ feeds[@paul_feed_url].feed_url.should == @paul_feed_url
174
+ feeds[@trotter_feed_url].feed_url.should == @trotter_feed_url
175
+ end
176
+
177
+ it "should always return a hash when passed an array" do
178
+ feeds = Feedzirra::Feed.fetch_and_parse([@paul_feed_url])
179
+ feeds.class.should == Hash
180
+ end
181
+
182
+ it "should yeild the url and feed object to a :on_success lambda" do
183
+ successful_call_mock = mock("successful_call_mock")
184
+ successful_call_mock.should_receive(:call)
185
+ Feedzirra::Feed.fetch_and_parse(@paul_feed_url, :on_success => lambda { |feed_url, feed|
186
+ feed_url.should == @paul_feed_url
187
+ feed.class.should == Feedzirra::AtomFeedBurner
188
+ successful_call_mock.call})
189
+ end
190
+
191
+ it "should yield the url, response_code, response_header, and response_body to a :on_failure lambda" do
192
+ failure_call_mock = mock("failure_call_mock")
193
+ failure_call_mock.should_receive(:call)
194
+ fail_url = "http://localhost"
195
+ Feedzirra::Feed.fetch_and_parse(fail_url, :on_failure => lambda {|feed_url, response_code, response_header, response_body|
196
+ feed_url.should == fail_url
197
+ response_code.should == 0
198
+ response_header.should == ""
199
+ response_body.should == ""
200
+ failure_call_mock.call})
201
+ end
202
+
203
+ it "should return a not modified status for a feed with a :if_modified_since is past its last update" do
204
+ Feedzirra::Feed.fetch_and_parse(@paul_feed_url, :if_modified_since => Time.now).should == 304
205
+ end
206
+
207
+ it "should set the etag from the header" # do
208
+ # Feedzirra::Feed.fetch_and_parse(@paul_feed_url).etag.should_not == ""
209
+ # end
210
+
211
+ it "should set the last_modified from the header" # do
212
+ # Feedzirra::Feed.fetch_and_parse(@paul_feed_url).last_modified.should.class == Time
213
+ # end
214
+ end
215
+
216
+ describe "#update" do
217
+ it "should update and return a single feed object" do
218
+ feed = Feedzirra::Feed.fetch_and_parse(@paul_feed_url)
219
+ feed.entries.delete_at(0)
220
+ feed.last_modified = nil
221
+ feed.etag = nil
222
+ updated_feed = Feedzirra::Feed.update(feed)
223
+ updated_feed.new_entries.size.should == 1
224
+ updated_feed.should have_new_entries
225
+ end
226
+
227
+ it "should update a collection of feed objects" do
228
+ feeds = Feedzirra::Feed.fetch_and_parse([@paul_feed_url, @trotter_feed_url])
229
+ paul_entries_size = feeds[@paul_feed_url].entries.size
230
+ trotter_entries_size = feeds[@trotter_feed_url].entries.size
231
+
232
+ feeds.values.each do |feed|
233
+ feed.last_modified = nil
234
+ feed.etag = nil
235
+ feed.entries.delete_at(0)
236
+ end
237
+ updated_feeds = Feedzirra::Feed.update(feeds.values)
238
+ updated_feeds.detect {|f| f.feed_url == @paul_feed_url}.entries.size.should == paul_entries_size
239
+ updated_feeds.detect {|f| f.feed_url == @trotter_feed_url}.entries.size.should == trotter_entries_size
240
+ end
241
+
242
+ it "should return the feed objects even when not updated" do
243
+ feeds = Feedzirra::Feed.fetch_and_parse([@paul_feed_url, @trotter_feed_url])
244
+ updated_feeds = Feedzirra::Feed.update(feeds.values)
245
+ updated_feeds.size.should == 2
246
+ updated_feeds.first.should_not be_updated
247
+ updated_feeds.last.should_not be_updated
248
+ end
249
+ end
250
+ end
251
+ end