simple-rss 1.3.3 → 2.0.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.
- checksums.yaml +4 -4
- data/LICENSE +79 -7
- data/README.markdown +1 -1
- data/Rakefile +134 -136
- data/lib/simple-rss.rb +273 -154
- data/simple-rss.gemspec +4 -5
- data/test/base/array_tags_test.rb +37 -0
- data/test/base/base_test.rb +76 -77
- data/test/base/empty_tag_test.rb +56 -0
- data/test/base/encoding_test.rb +87 -0
- data/test/base/feed_attributes_test.rb +26 -0
- data/test/base/item_attributes_test.rb +26 -0
- data/test/data/atom_with_entry_attrs.xml +13 -0
- data/test/data/atom_with_feed_attrs.xml +13 -0
- data/test/data/media_rss.xml +465 -0
- data/test/data/rss20_utf8.xml +61 -0
- data/test/data/rss20_with_channel_attrs.xml +13 -0
- data/test/data/rss20_with_item_attrs.xml +13 -0
- data/test/test_helper.rb +10 -3
- metadata +14 -8
- data/install.rb +0 -40
data/lib/simple-rss.rb
CHANGED
|
@@ -1,160 +1,279 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# rbs_inline: enabled
|
|
2
|
+
|
|
3
|
+
require "cgi"
|
|
4
|
+
require "time"
|
|
3
5
|
|
|
4
6
|
class SimpleRSS
|
|
5
|
-
VERSION = "
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
7
|
+
VERSION = "2.0.0".freeze
|
|
8
|
+
|
|
9
|
+
# @rbs @items: Array[Hash[Symbol, untyped]]
|
|
10
|
+
# @rbs @source: String
|
|
11
|
+
# @rbs @options: Hash[Symbol, untyped]
|
|
12
|
+
|
|
13
|
+
attr_reader :items #: Array[Hash[Symbol, untyped]]
|
|
14
|
+
attr_reader :source #: String
|
|
15
|
+
alias entries items #: Array[Hash[Symbol, untyped]]
|
|
16
|
+
|
|
17
|
+
@@feed_tags = %i[
|
|
18
|
+
id
|
|
19
|
+
title subtitle link
|
|
20
|
+
description
|
|
21
|
+
author webMaster managingEditor contributor
|
|
22
|
+
pubDate lastBuildDate updated dc:date
|
|
23
|
+
generator language docs cloud
|
|
24
|
+
ttl skipHours skipDays
|
|
25
|
+
image logo icon rating
|
|
26
|
+
rights copyright
|
|
27
|
+
textInput feedburner:browserFriendly
|
|
28
|
+
itunes:author itunes:category
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
@@item_tags = %i[
|
|
32
|
+
id
|
|
33
|
+
title link link+alternate link+self link+edit link+replies
|
|
34
|
+
author contributor
|
|
35
|
+
description summary content content:encoded comments
|
|
36
|
+
pubDate published updated expirationDate modified dc:date
|
|
37
|
+
category guid
|
|
38
|
+
trackback:ping trackback:about
|
|
39
|
+
dc:creator dc:title dc:subject dc:rights dc:publisher
|
|
40
|
+
feedburner:origLink
|
|
41
|
+
media:content#url media:content#type media:content#height media:content#width media:content#duration
|
|
42
|
+
media:title media:thumbnail#url media:thumbnail#height media:thumbnail#width
|
|
43
|
+
media:credit media:credit#role
|
|
44
|
+
media:category media:category#scheme
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
# @rbs (untyped, ?Hash[Symbol, untyped]) -> void
|
|
48
|
+
def initialize(source, options = {})
|
|
49
|
+
@source = source.respond_to?(:read) ? source.read.to_s : source.to_s
|
|
50
|
+
@items = [] #: Array[Hash[Symbol, untyped]]
|
|
51
|
+
@options = {} #: Hash[Symbol, untyped]
|
|
52
|
+
@options.update(options)
|
|
53
|
+
|
|
54
|
+
parse
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @rbs () -> SimpleRSS
|
|
58
|
+
def channel
|
|
59
|
+
self
|
|
60
|
+
end
|
|
61
|
+
alias feed channel
|
|
62
|
+
|
|
63
|
+
class << self
|
|
64
|
+
# @rbs () -> Array[Symbol]
|
|
65
|
+
def feed_tags
|
|
66
|
+
@@feed_tags
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @rbs (Array[Symbol]) -> Array[Symbol]
|
|
70
|
+
def feed_tags=(ft)
|
|
71
|
+
@@feed_tags = ft
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @rbs () -> Array[Symbol]
|
|
75
|
+
def item_tags
|
|
76
|
+
@@item_tags
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @rbs (Array[Symbol]) -> Array[Symbol]
|
|
80
|
+
def item_tags=(it)
|
|
81
|
+
@@item_tags = it
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The strict attribute is for compatibility with Ruby's standard RSS parser
|
|
85
|
+
#
|
|
86
|
+
# @rbs (untyped, ?Hash[Symbol, untyped]) -> SimpleRSS
|
|
87
|
+
def parse(source, options = {})
|
|
88
|
+
new source, options
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
DATE_TAGS = %i[pubDate lastBuildDate published updated expirationDate modified dc:date].freeze
|
|
93
|
+
STRIP_HTML_TAGS = %i[author contributor skipHours skipDays].freeze
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
# @rbs () -> void
|
|
98
|
+
def parse
|
|
99
|
+
raise SimpleRSSError, "Poorly formatted feed" unless @source =~ %r{<(channel|feed).*?>.*?</(channel|feed)>}mi
|
|
100
|
+
|
|
101
|
+
# Feed's title and link
|
|
102
|
+
feed_content = Regexp.last_match(1) if @source =~ %r{(.*?)<(rss:|atom:)?(item|entry).*?>.*?</(rss:|atom:)?(item|entry)>}mi
|
|
103
|
+
|
|
104
|
+
# Capture channel/feed tag attributes
|
|
105
|
+
feed_attrs = nil
|
|
106
|
+
if @source =~ /<(channel|feed)([\s][^>]*)?>/mi
|
|
107
|
+
feed_attrs = Regexp.last_match(2)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
@@feed_tags.each do |tag|
|
|
111
|
+
next if tag.to_s.strip.empty?
|
|
112
|
+
|
|
113
|
+
tag_str = tag.to_s
|
|
114
|
+
|
|
115
|
+
# Handle channel#attr or feed#attr syntax
|
|
116
|
+
if tag_str.include?("#")
|
|
117
|
+
parse_feed_attr_tag(tag_str, feed_attrs)
|
|
118
|
+
next
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
if feed_content && feed_content =~ %r{<(rss:|atom:)?#{tag}(.*?)>(.*?)</(rss:|atom:)?#{tag}>}mi
|
|
122
|
+
nil
|
|
123
|
+
elsif feed_content && feed_content =~ %r{<(rss:|atom:)?#{tag}(.*?)\/\s*>}mi
|
|
124
|
+
nil
|
|
125
|
+
elsif @source =~ %r{<(rss:|atom:)?#{tag}(.*?)>(.*?)</(rss:|atom:)?#{tag}>}mi
|
|
126
|
+
nil
|
|
127
|
+
elsif @source =~ %r{<(rss:|atom:)?#{tag}(.*?)\/\s*>}mi
|
|
128
|
+
nil
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
next unless Regexp.last_match(2) || Regexp.last_match(3)
|
|
132
|
+
|
|
133
|
+
tag_cleaned = clean_tag(tag)
|
|
134
|
+
instance_variable_set("@#{tag_cleaned}", clean_content(tag, Regexp.last_match(2), Regexp.last_match(3)))
|
|
135
|
+
self.class.class_eval("attr_reader :#{tag_cleaned}")
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# RSS items' title, link, and description
|
|
139
|
+
@source.scan(%r{<(rss:|atom:)?(item|entry)([\s][^>]*)?>(.*?)</(rss:|atom:)?(item|entry)>}mi) do |match|
|
|
140
|
+
item = {} #: Hash[Symbol, untyped]
|
|
141
|
+
@@item_tags.each do |tag|
|
|
142
|
+
next if tag.to_s.strip.empty?
|
|
143
|
+
|
|
144
|
+
parse_item_tag(item, tag, match[3], match[2])
|
|
145
|
+
end
|
|
146
|
+
item.define_singleton_method(:method_missing) { |name, *| self[name] }
|
|
147
|
+
@items << item
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# @rbs (Hash[Symbol, untyped], Symbol, String?, String?) -> void
|
|
152
|
+
def parse_item_tag(item, tag, content, item_attrs = nil)
|
|
153
|
+
return if content.nil?
|
|
154
|
+
|
|
155
|
+
tag_str = tag.to_s
|
|
156
|
+
|
|
157
|
+
return parse_rel_tag(item, tag_str, content) if tag_str.include?("+")
|
|
158
|
+
return parse_attr_tag(item, tag_str, content, item_attrs) if tag_str.include?("#")
|
|
159
|
+
|
|
160
|
+
parse_simple_tag(item, tag, content)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @rbs (Hash[Symbol, untyped], String, String) -> void
|
|
164
|
+
def parse_rel_tag(item, tag_str, content)
|
|
165
|
+
tag, rel = tag_str.split("+")
|
|
166
|
+
return unless tag && rel
|
|
167
|
+
|
|
168
|
+
content =~ %r{<(rss:|atom:)?#{tag}(.*?)rel=['"]#{rel}['"](.*?)>(.*?)</(rss:|atom:)?#{tag}>}mi ||
|
|
169
|
+
content =~ %r{<(rss:|atom:)?#{tag}(.*?)rel=['"]#{rel}['"](.*?)/\s*>}mi
|
|
170
|
+
|
|
171
|
+
return unless Regexp.last_match(3) || Regexp.last_match(4)
|
|
172
|
+
|
|
173
|
+
item[clean_tag("#{tag}+#{rel}")] = clean_content(tag.to_sym, Regexp.last_match(3), Regexp.last_match(4))
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# @rbs (String, String?) -> void
|
|
177
|
+
def parse_feed_attr_tag(tag_str, feed_attrs)
|
|
178
|
+
tag, attrib = tag_str.split("#")
|
|
179
|
+
return unless tag && attrib && feed_attrs
|
|
180
|
+
|
|
181
|
+
# Only handle channel or feed tags
|
|
182
|
+
return unless %w[channel feed].include?(tag)
|
|
183
|
+
return unless feed_attrs =~ /#{attrib}=['"](.*?)['"]/mi
|
|
184
|
+
|
|
185
|
+
tag_cleaned = clean_tag("#{tag}_#{attrib}")
|
|
186
|
+
instance_variable_set("@#{tag_cleaned}", clean_content(tag.to_sym, attrib, Regexp.last_match(1)))
|
|
187
|
+
self.class.class_eval("attr_reader :#{tag_cleaned}")
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# @rbs (Hash[Symbol, untyped], String, String, String?) -> void
|
|
191
|
+
def parse_attr_tag(item, tag_str, content, item_attrs = nil)
|
|
192
|
+
tag, attrib = tag_str.split("#")
|
|
193
|
+
return unless tag && attrib
|
|
194
|
+
|
|
195
|
+
# Handle attributes on the item/entry tag itself
|
|
196
|
+
if %w[item entry].include?(tag) && item_attrs
|
|
197
|
+
return unless item_attrs =~ /#{attrib}=['"](.*?)['"]/mi
|
|
198
|
+
|
|
199
|
+
item[clean_tag("#{tag}_#{attrib}")] = clean_content(tag.to_sym, attrib, Regexp.last_match(1))
|
|
200
|
+
return
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
content =~ %r{<(rss:|atom:)?#{tag}(.*?)#{attrib}=['"](.*?)['"](.*?)>(.*?)</(rss:|atom:)?#{tag}>}mi ||
|
|
204
|
+
content =~ %r{<(rss:|atom:)?#{tag}(.*?)#{attrib}=['"](.*?)['"](.*?)/\s*>}mi
|
|
205
|
+
|
|
206
|
+
return unless Regexp.last_match(3)
|
|
207
|
+
|
|
208
|
+
item[clean_tag("#{tag}_#{attrib}")] = clean_content(tag.to_sym, attrib, Regexp.last_match(3))
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# @rbs (Hash[Symbol, untyped], Symbol, String) -> void
|
|
212
|
+
def parse_simple_tag(item, tag, content)
|
|
213
|
+
# Handle array_tags option - collect all values for this tag
|
|
214
|
+
if array_tag?(tag)
|
|
215
|
+
values = content.scan(%r{<(rss:|atom:)?#{tag}(?:[^>]*)>(.*?)</(rss:|atom:)?#{tag}>}mi).map do |match|
|
|
216
|
+
clean_content(tag, nil, match[1])
|
|
217
|
+
end
|
|
218
|
+
item[clean_tag(tag)] = values unless values.empty?
|
|
219
|
+
return
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
content =~ %r{<(rss:|atom:)?#{tag}(.*?)>(.*?)</(rss:|atom:)?#{tag}>}mi ||
|
|
223
|
+
content =~ %r{<(rss:|atom:)?#{tag}(.*?)/\s*>}mi
|
|
224
|
+
|
|
225
|
+
return unless Regexp.last_match(2) || Regexp.last_match(3)
|
|
226
|
+
|
|
227
|
+
item[clean_tag(tag)] = clean_content(tag, Regexp.last_match(2), Regexp.last_match(3))
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# @rbs (Symbol) -> bool
|
|
231
|
+
def array_tag?(tag)
|
|
232
|
+
array_tags = @options[:array_tags]
|
|
233
|
+
return false unless array_tags.is_a?(Array)
|
|
234
|
+
|
|
235
|
+
array_tags.include?(tag) || array_tags.include?(tag.to_sym)
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# @rbs (Symbol, String?, String?) -> (Time | String)
|
|
239
|
+
def clean_content(tag, attrs, content)
|
|
240
|
+
content = content.to_s
|
|
241
|
+
|
|
242
|
+
return parse_date(content) if DATE_TAGS.include?(tag)
|
|
243
|
+
return unescape(content.gsub(/<.*?>/, "")) if STRIP_HTML_TAGS.include?(tag)
|
|
244
|
+
return extract_href(attrs) if content.empty? && attrs
|
|
245
|
+
|
|
246
|
+
unescape(content)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# @rbs (String) -> (Time | String)
|
|
250
|
+
def parse_date(content)
|
|
251
|
+
Time.parse(content)
|
|
252
|
+
rescue StandardError
|
|
253
|
+
unescape(content)
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
# @rbs (String?) -> String
|
|
257
|
+
def extract_href(attrs)
|
|
258
|
+
return "" unless "#{attrs} " =~ /href=['"]?([^'"]*)['" ]/mi
|
|
259
|
+
|
|
260
|
+
Regexp.last_match(1)&.strip || ""
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
# @rbs (Symbol | String) -> Symbol
|
|
264
|
+
def clean_tag(tag)
|
|
265
|
+
tag.to_s.tr(":", "_").intern
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# @rbs (String) -> String
|
|
152
269
|
def unescape(content)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
270
|
+
result = if content =~ %r{([^-_.!~*'()a-zA-Z\d;/?:@&=+$,\[\]]%)}
|
|
271
|
+
CGI.unescape(content)
|
|
272
|
+
else
|
|
273
|
+
content
|
|
274
|
+
end.gsub(/(<!\[CDATA\[|\]\]>)/, "").strip
|
|
275
|
+
|
|
276
|
+
result.encode(Encoding::UTF_8)
|
|
158
277
|
end
|
|
159
278
|
end
|
|
160
279
|
|
data/simple-rss.gemspec
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
Gem::Specification.new do |s|
|
|
2
2
|
s.name = "simple-rss"
|
|
3
|
-
s.version = "
|
|
4
|
-
s.
|
|
5
|
-
s.date = "2015-08-17"
|
|
3
|
+
s.version = "2.0.0"
|
|
4
|
+
s.date = "2025-12-28"
|
|
6
5
|
s.summary = "A simple, flexible, extensible, and liberal RSS and Atom reader for Ruby. It is designed to be backwards compatible with the standard RSS parser, but will never do RSS generation."
|
|
7
6
|
s.email = "lucas@rufy.com"
|
|
8
7
|
s.homepage = "http://github.com/cardmagic/simple-rss"
|
|
9
8
|
s.description = "A simple, flexible, extensible, and liberal RSS and Atom reader for Ruby. It is designed to be backwards compatible with the standard RSS parser, but will never do RSS generation."
|
|
10
9
|
s.authors = ["Lucas Carlson"]
|
|
11
|
-
s.files = ["
|
|
12
|
-
s.rubyforge_project =
|
|
10
|
+
s.files = Dir["lib/**/*", "test/**/*", "LICENSE", "README.markdown", "Rakefile", "simple-rss.gemspec"]
|
|
11
|
+
s.rubyforge_project = "simple-rss"
|
|
13
12
|
s.add_development_dependency "rake"
|
|
14
13
|
s.add_development_dependency "rdoc"
|
|
15
14
|
s.add_development_dependency "test-unit"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
class ArrayTagsTest < Test::Unit::TestCase
|
|
4
|
+
def setup
|
|
5
|
+
@rss20 = SimpleRSS.parse(
|
|
6
|
+
open(File.dirname(__FILE__) + "/../data/rss20.xml"),
|
|
7
|
+
array_tags: [:category]
|
|
8
|
+
)
|
|
9
|
+
@rss20_no_array = SimpleRSS.parse(
|
|
10
|
+
open(File.dirname(__FILE__) + "/../data/rss20.xml")
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_array_tag_returns_array
|
|
15
|
+
assert_kind_of Array, @rss20.items.first.category
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_array_tag_contains_all_values
|
|
19
|
+
categories = @rss20.items.first.category
|
|
20
|
+
assert_equal 2, categories.size
|
|
21
|
+
assert_includes categories, "Programming"
|
|
22
|
+
assert_includes categories, "Ruby"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def test_single_value_still_returns_array
|
|
26
|
+
# Item with only one category should still return an array
|
|
27
|
+
categories = @rss20.items[2].category
|
|
28
|
+
assert_kind_of Array, categories
|
|
29
|
+
assert_equal 1, categories.size
|
|
30
|
+
assert_equal ["General"], categories
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_without_array_tags_returns_string
|
|
34
|
+
# Default behavior should return just the first/last match as a string
|
|
35
|
+
assert_kind_of String, @rss20_no_array.items.first.category
|
|
36
|
+
end
|
|
37
|
+
end
|
data/test/base/base_test.rb
CHANGED
|
@@ -1,83 +1,82 @@
|
|
|
1
|
-
|
|
2
|
-
require 'test_helper'
|
|
1
|
+
require "test_helper"
|
|
3
2
|
class BaseTest < Test::Unit::TestCase
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
3
|
+
def setup
|
|
4
|
+
@rss09 = SimpleRSS.parse open(File.dirname(__FILE__) + "/../data/rss09.rdf")
|
|
5
|
+
@rss20 = SimpleRSS.parse open(File.dirname(__FILE__) + "/../data/rss20.xml")
|
|
6
|
+
@rss20_utf8 = SimpleRSS.parse open(File.dirname(__FILE__) + "/../data/rss20_utf8.xml")
|
|
7
|
+
@media_rss = SimpleRSS.parse open(File.dirname(__FILE__) + "/../data/media_rss.xml")
|
|
8
|
+
@atom = SimpleRSS.parse open(File.dirname(__FILE__) + "/../data/atom.xml")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_channel
|
|
12
|
+
assert_equal @rss09, @rss09.channel
|
|
13
|
+
assert_equal @rss20, @rss20.channel
|
|
14
|
+
assert_equal @atom, @atom.feed
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_items
|
|
18
|
+
assert_kind_of Array, @rss09.items
|
|
19
|
+
assert_kind_of Array, @rss20.items
|
|
20
|
+
assert_kind_of Array, @atom.entries
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_rss09
|
|
24
|
+
assert_equal 10, @rss09.items.size
|
|
25
|
+
assert_equal "Slashdot", @rss09.title
|
|
26
|
+
assert_equal "http://slashdot.org/", @rss09.channel.link
|
|
27
|
+
assert_equal "http://books.slashdot.org/article.pl?sid=05/08/29/1319236&from=rss", @rss09.items.first.link
|
|
28
|
+
assert_equal "http://books.slashdot.org/article.pl?sid=05/08/29/1319236&from=rss", @rss09.items.first[:link]
|
|
30
29
|
assert_equal Time.parse("Wed Aug 24 13:33:34 UTC 2005"), @rss20.items.first.pubDate
|
|
31
30
|
assert_equal Time.parse("Fri Sep 09 02:52:31 PDT 2005"), @rss09.channel.dc_date
|
|
32
|
-
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_media_rss
|
|
34
|
+
assert_equal 20, @media_rss.items.size
|
|
35
|
+
assert_equal "Uploads from herval", @media_rss.title
|
|
36
|
+
assert_equal "http://www.flickr.com/photos/herval/", @media_rss.channel.link
|
|
37
|
+
assert_equal "http://www.flickr.com/photos/herval/4671960608/", @media_rss.items.first.link
|
|
38
|
+
assert_equal "http://www.flickr.com/photos/herval/4671960608/", @media_rss.items.first[:link]
|
|
39
|
+
assert_equal "http://farm5.static.flickr.com/4040/4671960608_10cb945d5c_o.jpg", @media_rss.items.first.media_content_url
|
|
40
|
+
assert_equal "image/jpeg", @media_rss.items.first.media_content_type
|
|
41
|
+
assert_equal "3168", @media_rss.items.first.media_content_height
|
|
42
|
+
assert_equal "4752", @media_rss.items.first.media_content_width
|
|
43
|
+
assert_equal "Woof?", @media_rss.items.first.media_title
|
|
44
|
+
assert_equal "http://farm5.static.flickr.com/4040/4671960608_954d2297bc_s.jpg", @media_rss.items.first.media_thumbnail_url
|
|
45
|
+
assert_equal "75", @media_rss.items.first.media_thumbnail_height
|
|
46
|
+
assert_equal "75", @media_rss.items.first.media_thumbnail_width
|
|
47
|
+
assert_equal "herval", @media_rss.items.first.media_credit
|
|
48
|
+
assert_equal "photographer", @media_rss.items.first.media_credit_role
|
|
49
|
+
assert_equal "pets frodo", @media_rss.items.first.media_category
|
|
50
|
+
assert_equal "urn:flickr:tags", @media_rss.items.first.media_category_scheme
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_rss20
|
|
54
|
+
assert_equal 10, @rss20.items.size
|
|
55
|
+
assert_equal "Technoblog", @rss20.title
|
|
56
|
+
assert_equal "http://tech.rufy.com", @rss20.channel.link
|
|
57
|
+
assert_equal "http://feeds.feedburner.com/rufytech?m=68", @rss20.items.first.link
|
|
58
|
+
assert_equal "http://feeds.feedburner.com/rufytech?m=68", @rss20.items.first[:link]
|
|
59
|
+
assert_equal "This is an XML content feed. It is intended to be viewed in a newsreader or syndicated to another site.", @rss20.channel.feedburner_browserFriendly
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_atom
|
|
63
|
+
assert_equal 1, @atom.entries.size
|
|
64
|
+
assert_equal "dive into mark", @atom.title
|
|
65
|
+
assert_equal "http://example.org/", @atom.feed.link
|
|
66
|
+
assert_equal "http://example.org/2005/04/02/atom", @atom.entries.first.link
|
|
67
|
+
assert_equal "http://example.org/2005/04/02/atom", @atom.entries.first[:link]
|
|
68
|
+
end
|
|
33
69
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
assert_equal "http://www.flickr.com/photos/herval/", @media_rss.channel.link
|
|
38
|
-
assert_equal "http://www.flickr.com/photos/herval/4671960608/", @media_rss.items.first.link
|
|
39
|
-
assert_equal "http://www.flickr.com/photos/herval/4671960608/", @media_rss.items.first[:link]
|
|
40
|
-
assert_equal "http://farm5.static.flickr.com/4040/4671960608_10cb945d5c_o.jpg", @media_rss.items.first.media_content_url
|
|
41
|
-
assert_equal "image/jpeg", @media_rss.items.first.media_content_type
|
|
42
|
-
assert_equal "3168", @media_rss.items.first.media_content_height
|
|
43
|
-
assert_equal "4752", @media_rss.items.first.media_content_width
|
|
44
|
-
assert_equal "Woof?", @media_rss.items.first.media_title
|
|
45
|
-
assert_equal "http://farm5.static.flickr.com/4040/4671960608_954d2297bc_s.jpg", @media_rss.items.first.media_thumbnail_url
|
|
46
|
-
assert_equal "75", @media_rss.items.first.media_thumbnail_height
|
|
47
|
-
assert_equal "75", @media_rss.items.first.media_thumbnail_width
|
|
48
|
-
assert_equal "herval", @media_rss.items.first.media_credit
|
|
49
|
-
assert_equal "photographer", @media_rss.items.first.media_credit_role
|
|
50
|
-
assert_equal "pets frodo", @media_rss.items.first.media_category
|
|
51
|
-
assert_equal "urn:flickr:tags", @media_rss.items.first.media_category_scheme
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def test_rss20
|
|
55
|
-
assert_equal 10, @rss20.items.size
|
|
56
|
-
assert_equal "Technoblog", @rss20.title
|
|
57
|
-
assert_equal "http://tech.rufy.com", @rss20.channel.link
|
|
58
|
-
assert_equal "http://feeds.feedburner.com/rufytech?m=68", @rss20.items.first.link
|
|
59
|
-
assert_equal "http://feeds.feedburner.com/rufytech?m=68", @rss20.items.first[:link]
|
|
60
|
-
assert_equal "This is an XML content feed. It is intended to be viewed in a newsreader or syndicated to another site.", @rss20.channel.feedburner_browserFriendly
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def test_atom
|
|
64
|
-
assert_equal 1, @atom.entries.size
|
|
65
|
-
assert_equal "dive into mark", @atom.title
|
|
66
|
-
assert_equal "http://example.org/", @atom.feed.link
|
|
67
|
-
assert_equal "http://example.org/2005/04/02/atom", @atom.entries.first.link
|
|
68
|
-
assert_equal "http://example.org/2005/04/02/atom", @atom.entries.first[:link]
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def test_bad_feed
|
|
72
|
-
assert_raise(SimpleRSSError) { SimpleRSS.parse(open(File.dirname(__FILE__) + '/../data/not-rss.xml')) }
|
|
73
|
-
end
|
|
70
|
+
def test_bad_feed
|
|
71
|
+
assert_raise(SimpleRSSError) { SimpleRSS.parse(open(File.dirname(__FILE__) + "/../data/not-rss.xml")) }
|
|
72
|
+
end
|
|
74
73
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
74
|
+
def test_rss_utf8
|
|
75
|
+
assert_equal 2, @rss20_utf8.items.size
|
|
76
|
+
assert_equal "SC5 Blog", @rss20_utf8.title
|
|
77
|
+
assert_equal Encoding::UTF_8, @rss20_utf8.title.encoding
|
|
78
|
+
item = @rss20_utf8.items.first
|
|
79
|
+
assert_equal "Mitä asiakkaamme ajattelevat meistä?", item.title
|
|
80
|
+
assert_equal Encoding::UTF_8, item.title.encoding
|
|
81
|
+
end
|
|
83
82
|
end
|