syndication 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/IMPLEMENTATION +33 -0
- data/README +208 -0
- data/examples/yahoo.rb +21 -0
- data/lib/syndication/atom.rb +479 -0
- data/lib/syndication/common.rb +267 -0
- data/lib/syndication/content.rb +37 -0
- data/lib/syndication/dublincore.rb +92 -0
- data/lib/syndication/podcast.rb +85 -0
- data/lib/syndication/rss.rb +326 -0
- data/lib/syndication/syndication.rb +45 -0
- data/test/atomtest.rb +186 -0
- data/test/rsstest.rb +314 -0
- metadata +55 -0
data/test/rsstest.rb
ADDED
@@ -0,0 +1,314 @@
|
|
1
|
+
|
2
|
+
require 'syndication/rss'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'syndication/dublincore'
|
5
|
+
require 'syndication/content'
|
6
|
+
require 'syndication/podcast'
|
7
|
+
|
8
|
+
module Syndication
|
9
|
+
|
10
|
+
# This class contains the unit tests for the Syndication module.
|
11
|
+
class Tests < Test::Unit::TestCase
|
12
|
+
|
13
|
+
# A set of minimal assertions that can be applied to every well-formed parsed
|
14
|
+
# feed.
|
15
|
+
def baseline_assertions(feed)
|
16
|
+
assert_not_nil(feed)
|
17
|
+
assert_kind_of(Syndication::RSS::Feed, feed)
|
18
|
+
loi = feed.items
|
19
|
+
assert_not_nil(loi)
|
20
|
+
assert_kind_of(Array, loi)
|
21
|
+
assert(loi.length >= 1)
|
22
|
+
assert_not_nil(loi[0])
|
23
|
+
assert_not_nil(loi[0].description)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Test a minimal well-formed RSS2.0 feed
|
27
|
+
def test_rss2_wf_minimal
|
28
|
+
xml = <<-EOF
|
29
|
+
<rss version="2.0">
|
30
|
+
<channel>
|
31
|
+
<title>I like coffee</title>
|
32
|
+
<link>http://www.coffeegeek.com/</link>
|
33
|
+
<description>Hand over the latte & nobody gets hurt.</description>
|
34
|
+
</channel>
|
35
|
+
<item>
|
36
|
+
<description>A day without coffee is incomplete.</description>
|
37
|
+
</item>
|
38
|
+
</rss>
|
39
|
+
EOF
|
40
|
+
f = Syndication::RSS::Parser.new.parse(xml)
|
41
|
+
baseline_assertions(f)
|
42
|
+
assert(f.channel.title == 'I like coffee')
|
43
|
+
assert(f.channel.link == 'http://www.coffeegeek.com/')
|
44
|
+
assert(f.channel.description == 'Hand over the latte & nobody gets hurt.')
|
45
|
+
assert(f.items.first.description == 'A day without coffee is incomplete.')
|
46
|
+
c = f.channel
|
47
|
+
assert_not_nil(c)
|
48
|
+
assert_kind_of(Syndication::RSS::Channel, c)
|
49
|
+
assert_not_nil(c.title)
|
50
|
+
assert_not_nil(c.link)
|
51
|
+
assert_not_nil(c.description)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Test a minimal well-formed RSS1.0 feed
|
55
|
+
def test_rss1_wf_minimal
|
56
|
+
xml = <<-EOF
|
57
|
+
<?xml version="1.0"?>
|
58
|
+
<rdf:RDF
|
59
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
60
|
+
xmlns="http://purl.org/rss/1.0/">
|
61
|
+
<channel rdf:about="http://www.otternet.com/">
|
62
|
+
<title>OtterNet</title>
|
63
|
+
<link>http://www.otternet.com/</link>
|
64
|
+
<description>Otternet has pages & pages of information about otters.</description>
|
65
|
+
</channel>
|
66
|
+
<item rdf:about="http://www.otternet.com/species/seaotter.htm">
|
67
|
+
<title>The Sea Otter</title>
|
68
|
+
<link>http://www.otternet.com/species/seaotter.htm</link>
|
69
|
+
<description>The enticingly cute enhydra lontris.</description>
|
70
|
+
</item>
|
71
|
+
</rdf:RDF>
|
72
|
+
EOF
|
73
|
+
f = Syndication::RSS::Parser.new.parse(xml)
|
74
|
+
baseline_assertions(f)
|
75
|
+
assert(f.channel.title == 'OtterNet')
|
76
|
+
assert(f.channel.link == 'http://www.otternet.com/')
|
77
|
+
assert(f.channel.description == 'Otternet has pages & pages of information about otters.')
|
78
|
+
assert(f.items.first.title == 'The Sea Otter')
|
79
|
+
assert(f.items.first.link == 'http://www.otternet.com/species/seaotter.htm')
|
80
|
+
assert(f.items.first.description == 'The enticingly cute enhydra lontris.')
|
81
|
+
c = f.channel
|
82
|
+
assert_not_nil(c)
|
83
|
+
assert_kind_of(Syndication::RSS::Channel, c)
|
84
|
+
assert_not_nil(c.title)
|
85
|
+
assert_not_nil(c.link)
|
86
|
+
assert_not_nil(c.description)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Test a well-formed RSS2 feed with every element possible and more than
|
90
|
+
# one item
|
91
|
+
def test_rss2_wf_full
|
92
|
+
xml = <<-EOF
|
93
|
+
<rss version="2">
|
94
|
+
<channel>
|
95
|
+
<title>Example Feed</title>
|
96
|
+
<link>http://www.example.com/</link>
|
97
|
+
<description>This is merely an example.</description>
|
98
|
+
<language>en-us</language>
|
99
|
+
<copyright>Copyright 2004 The Example Corporation.</copyright>
|
100
|
+
<managingEditor>editor@example.com</managingEditor>
|
101
|
+
<webMaster>webmaster@example.com</webMaster>
|
102
|
+
<pubDate>Sat, 07 Sep 2002 00:01:02 EDT</pubDate>
|
103
|
+
<lastBuildDate>Sat, 7 Sep 02 13:14:15 -0600</lastBuildDate>
|
104
|
+
<category>examples</category>
|
105
|
+
<category>boring</category>
|
106
|
+
<generator>vim of course</generator>
|
107
|
+
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
|
108
|
+
<cloud domain="rpc.sys.com" port="80" path="/RPC2" registerProcedure="pingMe" protocol="soap"/>
|
109
|
+
<ttl>90</ttl>
|
110
|
+
<image>
|
111
|
+
<title>Example Inc</title>
|
112
|
+
<url>http://www.example.com/images/logo.jpg</url>
|
113
|
+
<link>http://www.example.com</link>
|
114
|
+
<width>42</width>
|
115
|
+
<height>23</height>
|
116
|
+
<description>The Example Logo</description>
|
117
|
+
</image>
|
118
|
+
<rating>(PICS-1.1 "http://www.icra.org/ratingsv02.html" l gen true r (cz 1 lz 1 nz 1 oz 1 vz 1) "http://www.rsac.org/ratingsv01.html" l gen true r (n 0 s 0 v 0 l 0) "http://www.classify.org/safesurf/" l gen true r (SS~~000 1))</rating>
|
119
|
+
<textInput>
|
120
|
+
<title>Submit</title>
|
121
|
+
<description>Enter keywords</description>
|
122
|
+
<name>SearchKeywords</name>
|
123
|
+
<link>http://www.example.com/cgi-bin/search.pl</link>
|
124
|
+
</textInput>
|
125
|
+
<skipHours>
|
126
|
+
<hour>0</hour>
|
127
|
+
<hour>23</hour>
|
128
|
+
</skipHours>
|
129
|
+
<skipDays>
|
130
|
+
<day>Monday</day>
|
131
|
+
<day>Sunday</day>
|
132
|
+
</skipDays>
|
133
|
+
<item>
|
134
|
+
<title>Our stock price shot up</title>
|
135
|
+
<link>http://www.example.com/news/2.html</link>
|
136
|
+
<description>We were hyped in the press!</description>
|
137
|
+
</item>
|
138
|
+
<item>
|
139
|
+
<title>A dull example of little value.</title>
|
140
|
+
<link>http://www.example.com/news/1.html</link>
|
141
|
+
<description>If this was any less interesting, it would be amazing.</description>
|
142
|
+
<author>fred@example.com</author>
|
143
|
+
<pubDate>Sat, 07 Sep 2002 00:01:02 EDT</pubDate>
|
144
|
+
<category>dull</category>
|
145
|
+
<category>amazingly</category>
|
146
|
+
<comments>http://www.example.com/news/comments/1.html</comments>
|
147
|
+
<enclosure url="http://www.example.com/mp3/advertisement.mp3" length="123987" type="audio/mpeg" />
|
148
|
+
<guid>4asd98dgf9a74@example.com</guid>
|
149
|
+
<source url="http://www.example.com/news.xml">Example News</source>
|
150
|
+
</item>
|
151
|
+
</channel>
|
152
|
+
</rss>
|
153
|
+
EOF
|
154
|
+
f = Syndication::RSS::Parser.new.parse(xml)
|
155
|
+
baseline_assertions(f)
|
156
|
+
for elem in %w(title link description language copyright managingeditor webmaster pubdate lastbuilddate category generator docs cloud ttl textinput rating skiphours skipdays)
|
157
|
+
assert_not_nil(f.channel.send(elem), "feed.channel.#{elem} is nil, it shouldn't be")
|
158
|
+
assert(f.channel.send(elem).to_s.length > 0)
|
159
|
+
end
|
160
|
+
items = f.items
|
161
|
+
assert(items.length == 2)
|
162
|
+
i = items.last
|
163
|
+
for elem in %w(title link description author pubdate category comments enclosure guid source)
|
164
|
+
assert_not_nil(i.send(elem), "feed.channel.item[1].#{elem} is nil, it shouldn't be")
|
165
|
+
end
|
166
|
+
cats = i.category
|
167
|
+
assert(cats.length == 2)
|
168
|
+
assert(cats.first == 'dull')
|
169
|
+
assert(cats.last == 'amazingly')
|
170
|
+
assert(f.channel.skiphours.length == 2)
|
171
|
+
assert(f.channel.skiphours.first == 0)
|
172
|
+
assert(f.channel.skiphours.last == 23)
|
173
|
+
assert(f.channel.pubdate.kind_of?(DateTime))
|
174
|
+
assert(f.channel.lastbuilddate.kind_of?(DateTime))
|
175
|
+
assert(f.channel.pubdate.mday == 7)
|
176
|
+
assert(f.channel.pubdate.month == 9)
|
177
|
+
assert(f.channel.lastbuilddate.mday == 7)
|
178
|
+
assert(f.channel.lastbuilddate.month == 9)
|
179
|
+
c = f.channel
|
180
|
+
assert_not_nil(c)
|
181
|
+
assert_kind_of(Syndication::RSS::Channel, c)
|
182
|
+
assert_not_nil(c.title)
|
183
|
+
assert_not_nil(c.link)
|
184
|
+
assert_not_nil(c.description)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Test a well-formed RSS 1.0 feed with every element possible, more
|
188
|
+
# than one item, and rdf:resource links in the channel
|
189
|
+
def test_rss1_wf_full
|
190
|
+
xml = <<-EOF
|
191
|
+
<rdf:RDF
|
192
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
193
|
+
xmlns="http://purl.org/rss/1.0/">
|
194
|
+
<channel>
|
195
|
+
<title>Example Dot Org</title>
|
196
|
+
<link>http://www.example.org</link>
|
197
|
+
<description>the Example Organization web site</description>
|
198
|
+
<image rdf:resource="http://www.example.org/images/logo.gif"/>
|
199
|
+
<items>
|
200
|
+
<rdf:Seq>
|
201
|
+
<rdf:li resource="http://www.example.org/items/1"/>
|
202
|
+
<rdf:li resource="http://www.example.org/items/2"/>
|
203
|
+
</rdf:Seq>
|
204
|
+
</items>
|
205
|
+
<textinput rdf:resource="http://www.example.org/cgi-bin/input.pl"/>
|
206
|
+
</channel>
|
207
|
+
<textinput rdf:about="http://www.example.org/cgi-bin/input.pl">
|
208
|
+
<title>Search example.org</title>
|
209
|
+
<description>Search the example.org web site</description>
|
210
|
+
<name>query</name>
|
211
|
+
<link>http://www.example.org/cgi-bin/input.pl</link>
|
212
|
+
</textinput>
|
213
|
+
<image rdf:about="http://www.example.org/images/logo.gif">
|
214
|
+
<title>Example.org logo</title>
|
215
|
+
<link>http://www.example.org/</link>
|
216
|
+
<url>http://www.example.org/images/logo.gif</url>
|
217
|
+
</image>
|
218
|
+
<item rdf:about="http://www.example.org/items/1">
|
219
|
+
<title>Welcome</title>
|
220
|
+
<link>http://www.example.org/items/1</link>
|
221
|
+
<description>Welcome to our new news feed</description>
|
222
|
+
</item>
|
223
|
+
<item rdf:about="http://www.example.org/items/2">
|
224
|
+
<title>New Status Update</title>
|
225
|
+
<link>http://www.example.org/items/1</link>
|
226
|
+
<description>News about the Example project</description>
|
227
|
+
</item>
|
228
|
+
</rdf:RDF>
|
229
|
+
EOF
|
230
|
+
f = Syndication::RSS::Parser.new.parse(xml)
|
231
|
+
baseline_assertions(f)
|
232
|
+
for elem in %w(title link description textinput)
|
233
|
+
assert_not_nil(f.channel.send(elem), "feed.channel.#{elem} is nil, it shouldn't be")
|
234
|
+
assert(f.channel.send(elem).to_s.length > 0)
|
235
|
+
end
|
236
|
+
il = f.items
|
237
|
+
assert(il.length == 2)
|
238
|
+
i = il.last
|
239
|
+
assert(i.link == 'http://www.example.org/items/1')
|
240
|
+
assert(i.title == 'New Status Update')
|
241
|
+
assert(i.description == 'News about the Example project')
|
242
|
+
assert(f.textinput.title == 'Search example.org')
|
243
|
+
f.channel.image.strip
|
244
|
+
assert(f.image.url == 'http://www.example.org/images/logo.gif')
|
245
|
+
c = f.channel
|
246
|
+
assert_not_nil(c)
|
247
|
+
assert_kind_of(Syndication::RSS::Channel, c)
|
248
|
+
assert_not_nil(c.title)
|
249
|
+
assert_not_nil(c.link)
|
250
|
+
assert_not_nil(c.description)
|
251
|
+
end
|
252
|
+
|
253
|
+
# Test HTML encoded content in RSS 1.0 and namespace remapping
|
254
|
+
def test_rss1_content
|
255
|
+
xml = <<-EOF
|
256
|
+
<?xml version="1.0"?>
|
257
|
+
<rdf:RDF
|
258
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
259
|
+
xmlns:html="http://purl.org/rss/1.0/modules/content/"
|
260
|
+
xmlns="http://purl.org/rss/1.0/">
|
261
|
+
<channel rdf:about="http://www.otternet.com/">
|
262
|
+
<title>OtterNet</title>
|
263
|
+
<link>http://www.otternet.com/</link>
|
264
|
+
<description>Otternet has dozens of pages of information about otters.</description>
|
265
|
+
<content:encoded><![CDATA[<p><cite>OtterNet</cite> has <em>dozens</em> of pages of information about otters.</p>]]></content:encoded>
|
266
|
+
</channel>
|
267
|
+
<item rdf:about="http://www.otternet.com/species/seaotter.htm">
|
268
|
+
<title>The Sea Otter</title>
|
269
|
+
<link>http://www.otternet.com/species/seaotter.htm</link>
|
270
|
+
<description>The enticingly cute enhydra lontris.</description>
|
271
|
+
<html:encoded>The enticingly cute <i>enhydra lontris</i></html:encoded>
|
272
|
+
</item>
|
273
|
+
<item rdf:about="http://www.ruby-lang.org/">
|
274
|
+
<title>Ruby</title>
|
275
|
+
<link>http://www.ruby-lang.org/</link>
|
276
|
+
<description>There's this language called Ruby, you may have heard of it.</description>
|
277
|
+
<html:encoded>There's this language called <strong>Ruby</strong>, you <em>may</em> have heard of it.</html:encoded>
|
278
|
+
</item>
|
279
|
+
</rdf:RDF>
|
280
|
+
EOF
|
281
|
+
f = Syndication::RSS::Parser.new.parse(xml)
|
282
|
+
baseline_assertions(f)
|
283
|
+
il = f.items
|
284
|
+
assert(il.length == 2)
|
285
|
+
i1 = il.first
|
286
|
+
i2 = il.last
|
287
|
+
assert_not_nil(i1.content_encoded, "content_encoded nil, shouldn't be")
|
288
|
+
assert_not_nil(i2.content_encoded, "content_encoded nil, shouldn't be")
|
289
|
+
assert(i1.content_decoded == 'The enticingly cute <i>enhydra lontris</i>')
|
290
|
+
assert(i2.content_decoded == "There's this language called <strong>Ruby</strong>, you <em>may</em> have heard of it.")
|
291
|
+
c = f.channel
|
292
|
+
assert_not_nil(c)
|
293
|
+
assert_kind_of(Syndication::RSS::Channel, c)
|
294
|
+
assert_not_nil(c.title)
|
295
|
+
assert_not_nil(c.link)
|
296
|
+
assert_not_nil(c.description)
|
297
|
+
end
|
298
|
+
|
299
|
+
# Test iTunes-specific duration parsing
|
300
|
+
def test_itunes
|
301
|
+
i = Syndication::RSS::Item.new(nil)
|
302
|
+
i.itunes_duration = "12:34:56"
|
303
|
+
assert(i.itunes_duration == 45296, "Duration computed incorrectly")
|
304
|
+
i.itunes_duration = "5:43:21"
|
305
|
+
assert(i.itunes_duration == 20601, "Duration computed incorrectly")
|
306
|
+
i.itunes_duration = "20:01"
|
307
|
+
assert(i.itunes_duration == 1201, "Duration computed incorrectly")
|
308
|
+
i.itunes_duration = "3:52"
|
309
|
+
assert(i.itunes_duration == 232, "Duration computed incorrectly")
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
313
|
+
|
314
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: syndication
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.4.0
|
7
|
+
date: 2005-09-29 00:00:00 -05:00
|
8
|
+
summary: A web syndication parser for Atom and RSS with a uniform API
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: meta@pobox.com
|
12
|
+
homepage: http://www.pobox.com/~meta/
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
-
|
22
|
+
- ">"
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: 0.0.0
|
25
|
+
version:
|
26
|
+
platform: ruby
|
27
|
+
signing_key:
|
28
|
+
cert_chain:
|
29
|
+
authors:
|
30
|
+
- mathew
|
31
|
+
files:
|
32
|
+
- lib/syndication
|
33
|
+
- lib/syndication/dublincore.rb
|
34
|
+
- lib/syndication/common.rb
|
35
|
+
- lib/syndication/podcast.rb
|
36
|
+
- lib/syndication/content.rb
|
37
|
+
- lib/syndication/rss.rb
|
38
|
+
- lib/syndication/syndication.rb
|
39
|
+
- lib/syndication/atom.rb
|
40
|
+
- test/rsstest.rb
|
41
|
+
- test/atomtest.rb
|
42
|
+
- examples/yahoo.rb
|
43
|
+
- README
|
44
|
+
- IMPLEMENTATION
|
45
|
+
test_files:
|
46
|
+
- test/atomtest.rb
|
47
|
+
- test/rsstest.rb
|
48
|
+
rdoc_options: []
|
49
|
+
extra_rdoc_files:
|
50
|
+
- README
|
51
|
+
- IMPLEMENTATION
|
52
|
+
executables: []
|
53
|
+
extensions: []
|
54
|
+
requirements: []
|
55
|
+
dependencies: []
|