feed-abstract 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +3 -3
- data/feed-abstract.gemspec +1 -1
- data/lib/feed-abstract/channel/atom.rb +30 -32
- data/lib/feed-abstract/channel/rdf.rb +47 -49
- data/lib/feed-abstract/channel/rss.rb +69 -71
- data/lib/feed-abstract/feed.rb +58 -61
- data/lib/feed-abstract/item/atom.rb +41 -43
- data/lib/feed-abstract/item/rdf.rb +38 -40
- data/lib/feed-abstract/item/rss.rb +77 -79
- data/lib/feed-abstract/items/atom.rb +11 -13
- data/lib/feed-abstract/items/rdf.rb +10 -12
- data/lib/feed-abstract/items/rss.rb +10 -12
- data/lib/feed-abstract/mixins.rb +47 -49
- data/lib/feed-abstract/version.rb +2 -4
- data/spec/feed_abstract_channel_spec.rb +1 -1
- data/spec/feed_abstract_item_spec.rb +1 -1
- data/spec/feed_abstract_spec.rb +1 -1
- metadata +2 -2
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
=
|
1
|
+
= FeedAbstract
|
2
2
|
|
3
|
-
|
3
|
+
FeedAbstract creates a common object graph for RSS, Atom, and RDF feeds using the classes returned by RSS::Parser
|
4
4
|
|
5
5
|
== Installation
|
6
6
|
|
@@ -8,7 +8,7 @@ Feed::Abstract creates a common object graph for RSS, Atom, and RDF feeds using
|
|
8
8
|
|
9
9
|
== Usage
|
10
10
|
|
11
|
-
See
|
11
|
+
See FeedAbstract::Feed for basic examples. Also see the FeedAbstract::Channel and FeedAbstract::Item namespaces.
|
12
12
|
|
13
13
|
== Author
|
14
14
|
|
data/feed-abstract.gemspec
CHANGED
@@ -4,7 +4,7 @@ require "feed-abstract/version"
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = "feed-abstract"
|
7
|
-
s.version =
|
7
|
+
s.version = FeedAbstract::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
9
|
s.authors = ["Daniel Collis-Puro"]
|
10
10
|
s.email = ["djcp@cyber.law.harvard.edu"]
|
@@ -1,47 +1,45 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module
|
4
|
-
class Abstract
|
3
|
+
module FeedAbstract
|
5
4
|
|
6
|
-
|
7
|
-
|
5
|
+
# You don't want this class. You want FeedAbstract::Channel::Atom, FeedAbstract::Channel::RSS or FeedAbstract::Channel::RDF.
|
6
|
+
class Channel
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
# See FeedAbstractMixins::Atom for more instance methods.
|
9
|
+
class Atom
|
10
|
+
include FeedAbstractMixins::Atom
|
12
11
|
|
13
|
-
|
12
|
+
attr_reader :feed, :source
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def description
|
20
|
-
@feed.subtitle.content
|
21
|
-
end
|
22
|
-
alias :subtitle :description
|
14
|
+
def initialize(feed)
|
15
|
+
@feed = @source = feed
|
16
|
+
end
|
23
17
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
18
|
+
def description
|
19
|
+
@feed.subtitle.content
|
20
|
+
end
|
21
|
+
alias :subtitle :description
|
29
22
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
# A string representing the application that created this feed.
|
24
|
+
def generator
|
25
|
+
return '' if @feed.generator.nil?
|
26
|
+
@feed.generator.content
|
27
|
+
end
|
35
28
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
29
|
+
# A URL (perhaps with domain, depending on input) representing an icon for the feed.
|
30
|
+
def icon
|
31
|
+
return '' if @feed.icon.nil?
|
32
|
+
@feed.icon.content
|
33
|
+
end
|
41
34
|
|
35
|
+
# A URL (perhaps with domain, depending on input) representing a logo for the feed.
|
36
|
+
def logo
|
37
|
+
return '' if @feed.logo.nil?
|
38
|
+
@feed.logo.content
|
42
39
|
end
|
40
|
+
|
43
41
|
end
|
42
|
+
end
|
44
43
|
|
45
44
|
|
46
|
-
end
|
47
45
|
end
|
@@ -1,63 +1,61 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module
|
4
|
-
class
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
# The authors list as a string, joined with a comma.
|
15
|
-
def author
|
16
|
-
return '' if self.authors.empty?
|
17
|
-
self.authors.join(', ')
|
18
|
-
end
|
3
|
+
module FeedAbstract
|
4
|
+
class Channel
|
5
|
+
class RDF < RSS
|
6
|
+
|
7
|
+
# The authors list as an array.
|
8
|
+
def authors
|
9
|
+
return [] if @feed.channel.dc_publishers.empty?
|
10
|
+
@feed.channel.dc_publishers
|
11
|
+
end
|
19
12
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
''
|
27
|
-
end
|
13
|
+
# The authors list as a string, joined with a comma.
|
14
|
+
def author
|
15
|
+
return '' if self.authors.empty?
|
16
|
+
self.authors.join(', ')
|
17
|
+
end
|
28
18
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
19
|
+
# The generator of this feed.
|
20
|
+
def generator
|
21
|
+
return '' unless @feed.channel.respond_to?(:about)
|
22
|
+
if @feed.channel.about.match(/connotea/i)
|
23
|
+
return 'Connotea'
|
33
24
|
end
|
25
|
+
''
|
26
|
+
end
|
34
27
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
28
|
+
# The category list as an array.
|
29
|
+
def categories
|
30
|
+
return [] if @feed.channel.dc_subjects.empty?
|
31
|
+
@feed.channel.dc_subjects.collect{|c| c.content}
|
32
|
+
end
|
40
33
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
alias :logo :icon
|
34
|
+
# The category list as a string, joined with a comma.
|
35
|
+
def category
|
36
|
+
return '' if self.categories.empty?
|
37
|
+
self.categories.join(', ')
|
38
|
+
end
|
47
39
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
40
|
+
# A URL (with or without domain depending on input) to a icon representing this feed.
|
41
|
+
def icon
|
42
|
+
return '' if @feed.channel.image.nil?
|
43
|
+
@feed.channel.image.resource
|
44
|
+
end
|
45
|
+
alias :logo :icon
|
53
46
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
47
|
+
# Copyright info.
|
48
|
+
def rights
|
49
|
+
return '' if @feed.channel.dc_rights.nil?
|
50
|
+
@feed.channel.dc_rights
|
51
|
+
end
|
59
52
|
|
53
|
+
# A Time object representing when this feed was updated, or at least "dated" according to the RDF spec.
|
54
|
+
def updated
|
55
|
+
return '' if @feed.channel.dc_date.nil?
|
56
|
+
@feed.channel.dc_date
|
60
57
|
end
|
58
|
+
|
61
59
|
end
|
62
60
|
end
|
63
61
|
end
|
@@ -1,91 +1,89 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module
|
4
|
-
class
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
attr_reader :feed, :source
|
3
|
+
module FeedAbstract
|
4
|
+
class Channel
|
5
|
+
class RSS
|
6
|
+
include FeedAbstractMixins::RSS
|
7
|
+
attr_reader :feed, :source
|
9
8
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def title
|
15
|
-
@feed.channel.title
|
16
|
-
end
|
9
|
+
def initialize(feed)
|
10
|
+
@feed = @source = feed
|
11
|
+
end
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
alias :subtitle :description
|
13
|
+
def title
|
14
|
+
@feed.channel.title
|
15
|
+
end
|
22
16
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
elsif @feed.channel.link.match(/www\.delicious\.com/i)
|
28
|
-
return 'Delicious'
|
29
|
-
end
|
30
|
-
return '' if @feed.channel.generator.nil?
|
31
|
-
@feed.channel.generator
|
32
|
-
end
|
17
|
+
def description
|
18
|
+
@feed.channel.description
|
19
|
+
end
|
20
|
+
alias :subtitle :description
|
33
21
|
|
34
|
-
|
35
|
-
|
36
|
-
|
22
|
+
# The generator of this feed as a string. Sometimes a URL, sometimes a string (e.g. the application name).
|
23
|
+
def generator
|
24
|
+
if ! @feed.channel.generator.nil? && @feed.channel.generator.match(/wordpress\.org/i)
|
25
|
+
return 'WordPress'
|
26
|
+
elsif @feed.channel.link.match(/www\.delicious\.com/i)
|
27
|
+
return 'Delicious'
|
37
28
|
end
|
29
|
+
return '' if @feed.channel.generator.nil?
|
30
|
+
@feed.channel.generator
|
31
|
+
end
|
38
32
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
33
|
+
def link
|
34
|
+
return '' if @feed.channel.link.nil?
|
35
|
+
@feed.channel.link
|
36
|
+
end
|
44
37
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
38
|
+
# Copyright info.
|
39
|
+
def rights
|
40
|
+
return '' if @feed.channel.copyright.nil? && @feed.channel.dc_rights.nil?
|
41
|
+
[@feed.channel.copyright,@feed.channel.dc_rights].compact.join(' ')
|
42
|
+
end
|
50
43
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
44
|
+
# A Time object.
|
45
|
+
def updated
|
46
|
+
return '' if @feed.channel.lastBuildDate.nil?
|
47
|
+
@feed.channel.lastBuildDate
|
48
|
+
end
|
56
49
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
50
|
+
# A globally unique ID for this feed. A URL in this case.
|
51
|
+
def guid
|
52
|
+
return '' if @feed.channel.link.nil?
|
53
|
+
@feed.channel.link
|
54
|
+
end
|
62
55
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
56
|
+
# The authors (a merge of the RSS managingEditor and dc:publisher elements) as an array.
|
57
|
+
def authors
|
58
|
+
return [] if @feed.channel.managingEditor.nil? && @feed.channel.dc_publishers.empty?
|
59
|
+
[@feed.channel.managingEditor, @feed.channel.dc_publishers].flatten.uniq
|
60
|
+
end
|
68
61
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
62
|
+
# The author list joined with a comma.
|
63
|
+
def author
|
64
|
+
return '' if self.authors.empty?
|
65
|
+
self.authors.join(', ')
|
66
|
+
end
|
74
67
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
68
|
+
# The category list (a merge of the RSS category and dc:subject elements) as an array.
|
69
|
+
def categories
|
70
|
+
return [] if @feed.channel.categories.empty? && @feed.channel.dc_subjects.empty?
|
71
|
+
[@feed.channel.categories, @feed.channel.dc_subjects].flatten.uniq.collect{|c| c.content}
|
72
|
+
end
|
80
73
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
alias :logo :icon
|
74
|
+
# The category list as a string, joined with a comma.
|
75
|
+
def category
|
76
|
+
return '' if @feed.channel.categories.empty?
|
77
|
+
@feed.channel.categories.collect{|c| c.content}.join(', ')
|
78
|
+
end
|
87
79
|
|
80
|
+
# A URL to an icon representing this feed.
|
81
|
+
def icon
|
82
|
+
return '' if @feed.channel.image.nil?
|
83
|
+
@feed.channel.image.url
|
88
84
|
end
|
85
|
+
alias :logo :icon
|
86
|
+
|
89
87
|
end
|
90
88
|
end
|
91
89
|
end
|
data/lib/feed-abstract/feed.rb
CHANGED
@@ -1,74 +1,71 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module
|
3
|
+
module FeedAbstract
|
4
4
|
class ParserError < Exception
|
5
5
|
end
|
6
6
|
|
7
|
-
class
|
7
|
+
# FeedAbstract:::Feed is the main class. It invokes RSS::Parser and negotiates which of the FeedAbstract::Channel and FeedAbstract::Item classes get dispatched to normalize the object graph of the feed you're parsing.
|
8
|
+
class Feed
|
9
|
+
attr_reader :channel, :raw_feed, :items
|
8
10
|
|
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
|
-
input = (xml.respond_to?(:read)) ? xml.read : xml
|
49
|
-
@raw_feed = RSS::Parser.parse(input,options[:do_validate], options[:ignore_unknown_element])
|
50
|
-
if @raw_feed == nil
|
51
|
-
raise Feed::ParserError
|
52
|
-
end
|
53
|
-
negotiate_channel_class
|
11
|
+
# === Parameters
|
12
|
+
# * xml - a string or object instance that responds to <b>read</b>
|
13
|
+
# * :do_validate - whether or not the feed should be validated. Passed through to RSS::Parser
|
14
|
+
# * :ignore_unknown_element - passed through to RSS::Parser
|
15
|
+
#
|
16
|
+
# === Returns
|
17
|
+
# An object with three attributes:
|
18
|
+
# * channel - an instance of FeedAbstract::Channel matching the type of feed we recognized
|
19
|
+
# * items - an array of items matching the type of feed we recognized.
|
20
|
+
# * raw_feed - the raw feed object returned by RSS::Parser, which might include RSS::Atom::Feed, RSS::RDF, or RSS::Rss
|
21
|
+
# You will most likely be using the <b>channel</b> and <b>items</b> attributes.
|
22
|
+
#
|
23
|
+
# === Notes
|
24
|
+
# If a feed can't be parsed, we'll throw a FeedAbstract::ParserError.
|
25
|
+
#
|
26
|
+
# == Examples
|
27
|
+
#
|
28
|
+
# f = FeedAbstract::Feed.new(File.open('/home/foo/xml/feed.rss2'))
|
29
|
+
# puts f.channel.title
|
30
|
+
# puts f.channel.description
|
31
|
+
#
|
32
|
+
# f.items.each do|item|
|
33
|
+
# puts item.title
|
34
|
+
# puts item.link
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# f = FeedAbstract::Feed.new(File.open('/home/foo/xml/feed.atom'))
|
38
|
+
# puts f.channel.generator
|
39
|
+
#
|
40
|
+
# puts "All tags / categories / subjects in this feed: " + f.items.collect{|i| i.categories}.flatten.uniq.sort.join(', ')
|
41
|
+
#
|
42
|
+
# f = FeedAbstract::Feed.new(Net::HTTP.get(URI.parse('http://rss.slashdot.org/Slashdot/slashdot')))
|
43
|
+
# puts f.items.collect{|i| i.link}
|
44
|
+
#
|
45
|
+
def initialize(xml = nil, options = {:do_validate => false, :ignore_unknown_element => true})
|
46
|
+
input = (xml.respond_to?(:read)) ? xml.read : xml
|
47
|
+
@raw_feed = RSS::Parser.parse(input,options[:do_validate], options[:ignore_unknown_element])
|
48
|
+
if @raw_feed == nil
|
49
|
+
raise FeedAbstract::ParserError
|
54
50
|
end
|
51
|
+
negotiate_channel_class
|
52
|
+
end
|
55
53
|
|
56
|
-
|
54
|
+
private
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
end
|
56
|
+
#Here's an easy extension point for custom parsers.
|
57
|
+
def negotiate_channel_class
|
58
|
+
if @raw_feed.class == RSS::Atom::Feed
|
59
|
+
@channel = Channel::Atom.new(@raw_feed)
|
60
|
+
@items = Items::Atom.new(@raw_feed)
|
61
|
+
elsif @raw_feed.class == RSS::RDF
|
62
|
+
@channel = Channel::RDF.new(@raw_feed)
|
63
|
+
@items = Items::RDF.new(@raw_feed)
|
64
|
+
else
|
65
|
+
@channel = Channel::RSS.new(@raw_feed)
|
66
|
+
@items = Items::RSS.new(@raw_feed)
|
70
67
|
end
|
71
|
-
|
72
68
|
end
|
69
|
+
|
73
70
|
end
|
74
71
|
end
|
@@ -1,50 +1,48 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module
|
4
|
-
class Abstract
|
5
|
-
|
6
|
-
# You don't want this class. You want Feed::Abstract::Item::Atom, Feed::Abstract::Item::RSS or Feed::Abstract::Item::RDF.
|
7
|
-
class Item
|
8
|
-
|
9
|
-
# See Feed::AbstractMixins::Atom for more instance methods.
|
10
|
-
class Atom
|
11
|
-
include Feed::AbstractMixins::Atom
|
12
|
-
attr_reader :item, :source
|
13
|
-
|
14
|
-
def initialize(item)
|
15
|
-
@item = @source = item
|
16
|
-
end
|
17
|
-
|
18
|
-
# The full content of the item, most likely html.
|
19
|
-
def content
|
20
|
-
return '' if @item.content.nil?
|
21
|
-
@item.content.content
|
22
|
-
end
|
23
|
-
|
24
|
-
# The contributor list as an array.
|
25
|
-
def contributors
|
26
|
-
return [] if @item.contributors.empty?
|
27
|
-
@item.contributors.collect{|c| c.name.content}
|
28
|
-
end
|
29
|
-
|
30
|
-
#The contributor list as a strong joined with a comma.
|
31
|
-
def contributor
|
32
|
-
return '' if @item.contributors.empty?
|
33
|
-
@item.contributors.collect{|c| c.name.content}.join(', ')
|
34
|
-
end
|
35
|
-
|
36
|
-
def summary
|
37
|
-
return '' if @item.summary.nil?
|
38
|
-
@item.summary.content
|
39
|
-
end
|
40
|
-
|
41
|
-
# A Time object
|
42
|
-
def published
|
43
|
-
return '' if @item.published.nil?
|
44
|
-
@item.published.content
|
45
|
-
end
|
3
|
+
module FeedAbstract
|
46
4
|
|
5
|
+
# You don't want this class. You want FeedAbstract::Item::Atom, FeedAbstract::Item::RSS or FeedAbstract::Item::RDF.
|
6
|
+
class Item
|
7
|
+
|
8
|
+
# See FeedAbstractMixins::Atom for more instance methods.
|
9
|
+
class Atom
|
10
|
+
include FeedAbstractMixins::Atom
|
11
|
+
attr_reader :item, :source
|
12
|
+
|
13
|
+
def initialize(item)
|
14
|
+
@item = @source = item
|
15
|
+
end
|
16
|
+
|
17
|
+
# The full content of the item, most likely html.
|
18
|
+
def content
|
19
|
+
return '' if @item.content.nil?
|
20
|
+
@item.content.content
|
47
21
|
end
|
22
|
+
|
23
|
+
# The contributor list as an array.
|
24
|
+
def contributors
|
25
|
+
return [] if @item.contributors.empty?
|
26
|
+
@item.contributors.collect{|c| c.name.content}
|
27
|
+
end
|
28
|
+
|
29
|
+
#The contributor list as a strong joined with a comma.
|
30
|
+
def contributor
|
31
|
+
return '' if @item.contributors.empty?
|
32
|
+
@item.contributors.collect{|c| c.name.content}.join(', ')
|
33
|
+
end
|
34
|
+
|
35
|
+
def summary
|
36
|
+
return '' if @item.summary.nil?
|
37
|
+
@item.summary.content
|
38
|
+
end
|
39
|
+
|
40
|
+
# A Time object
|
41
|
+
def published
|
42
|
+
return '' if @item.published.nil?
|
43
|
+
@item.published.content
|
44
|
+
end
|
45
|
+
|
48
46
|
end
|
49
47
|
end
|
50
48
|
end
|
@@ -1,48 +1,46 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module
|
4
|
-
class
|
5
|
-
class
|
6
|
-
class RDF < RSS
|
7
|
-
|
8
|
-
# The author list (from the dc:creator element) as an array.
|
9
|
-
def authors
|
10
|
-
(@item.dc_creators.empty?) ? [] : @item.dc_creators.collect{|c| c.content}
|
11
|
-
end
|
12
|
-
|
13
|
-
# The author list as a string, joined with a comma.
|
14
|
-
def author
|
15
|
-
return '' if self.authors.empty?
|
16
|
-
self.authors.join(', ')
|
17
|
-
end
|
18
|
-
|
19
|
-
# The category list (parsed from the dc:subject element) as an array.
|
20
|
-
def categories
|
21
|
-
return [] if @item.dc_subjects.empty?
|
22
|
-
@item.dc_subjects.collect{|c| c.content}
|
23
|
-
end
|
24
|
-
|
25
|
-
# The category list as a string, joined with a comma.
|
26
|
-
def category
|
27
|
-
return '' if self.categories.empty?
|
28
|
-
self.categories.join(', ')
|
29
|
-
end
|
30
|
-
|
31
|
-
# A Time object.
|
32
|
-
def updated
|
33
|
-
return '' if @item.dc_date.nil?
|
34
|
-
@item.dc_date
|
35
|
-
end
|
36
|
-
alias :published :updated
|
37
|
-
|
38
|
-
# A globally unique id, in this case probably a URL.
|
39
|
-
def guid
|
40
|
-
return '' if @item.about.nil?
|
41
|
-
@item.about
|
42
|
-
end
|
3
|
+
module FeedAbstract
|
4
|
+
class Item
|
5
|
+
class RDF < RSS
|
43
6
|
|
7
|
+
# The author list (from the dc:creator element) as an array.
|
8
|
+
def authors
|
9
|
+
(@item.dc_creators.empty?) ? [] : @item.dc_creators.collect{|c| c.content}
|
10
|
+
end
|
11
|
+
|
12
|
+
# The author list as a string, joined with a comma.
|
13
|
+
def author
|
14
|
+
return '' if self.authors.empty?
|
15
|
+
self.authors.join(', ')
|
16
|
+
end
|
44
17
|
|
18
|
+
# The category list (parsed from the dc:subject element) as an array.
|
19
|
+
def categories
|
20
|
+
return [] if @item.dc_subjects.empty?
|
21
|
+
@item.dc_subjects.collect{|c| c.content}
|
45
22
|
end
|
23
|
+
|
24
|
+
# The category list as a string, joined with a comma.
|
25
|
+
def category
|
26
|
+
return '' if self.categories.empty?
|
27
|
+
self.categories.join(', ')
|
28
|
+
end
|
29
|
+
|
30
|
+
# A Time object.
|
31
|
+
def updated
|
32
|
+
return '' if @item.dc_date.nil?
|
33
|
+
@item.dc_date
|
34
|
+
end
|
35
|
+
alias :published :updated
|
36
|
+
|
37
|
+
# A globally unique id, in this case probably a URL.
|
38
|
+
def guid
|
39
|
+
return '' if @item.about.nil?
|
40
|
+
@item.about
|
41
|
+
end
|
42
|
+
|
43
|
+
|
46
44
|
end
|
47
45
|
end
|
48
46
|
end
|
@@ -1,86 +1,84 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module
|
4
|
-
class
|
5
|
-
class
|
6
|
-
class RSS
|
7
|
-
|
8
|
-
include Feed::AbstractMixins::RSS
|
9
|
-
attr_reader :item, :source
|
10
|
-
|
11
|
-
def initialize(item)
|
12
|
-
@item = @source = item
|
13
|
-
end
|
14
|
-
|
15
|
-
def title
|
16
|
-
return '' if @item.title.nil?
|
17
|
-
@item.title
|
18
|
-
end
|
19
|
-
|
20
|
-
# The full content of this item, theoretically.
|
21
|
-
def content
|
22
|
-
return '' if @item.content_encoded.nil?
|
23
|
-
@item.content_encoded
|
24
|
-
end
|
25
|
-
|
26
|
-
def summary
|
27
|
-
return '' if @item.description.nil?
|
28
|
-
@item.description
|
29
|
-
end
|
30
|
-
|
31
|
-
def link
|
32
|
-
@item.link
|
33
|
-
end
|
34
|
-
|
35
|
-
# The author list (a merge of the RSS author and dc:creator elements) as an array.
|
36
|
-
def authors
|
37
|
-
[@item.author, @item.dc_creators.collect{|c| c.content}].flatten.uniq.compact
|
38
|
-
end
|
39
|
-
|
40
|
-
# The author list as a string, joined with a comma.
|
41
|
-
def author
|
42
|
-
(self.authors.empty?) ? '' : self.authors.join(', ')
|
43
|
-
end
|
44
|
-
|
45
|
-
# The contributors (parsed from the dc:contributor element) as an array.
|
46
|
-
def contributors
|
47
|
-
(@item.dc_contributors.empty?) ? [] : @item.dc_contributors
|
48
|
-
end
|
49
|
-
|
50
|
-
# The contributor list as a string joined with a comma.
|
51
|
-
def contributor
|
52
|
-
(self.contributors.empty?) ? '' : self.contributors.join(', ')
|
53
|
-
end
|
54
|
-
|
55
|
-
# The category list as an array.
|
56
|
-
def categories
|
57
|
-
return [] if @item.categories.empty?
|
58
|
-
@item.categories.collect{|c| c.content}
|
59
|
-
end
|
60
|
-
|
61
|
-
# The category list as a string, joined with a comma.
|
62
|
-
def category
|
63
|
-
return '' if @item.categories.empty?
|
64
|
-
@item.categories.collect{|c| c.content}.join(', ')
|
65
|
-
end
|
66
|
-
|
67
|
-
# Copyright info.
|
68
|
-
def rights
|
69
|
-
(@item.dc_rights.nil?) ? '' : @item.dc_rights
|
70
|
-
end
|
71
|
-
|
72
|
-
def updated
|
73
|
-
(@item.pubDate.nil?) ? '' : @item.pubDate
|
74
|
-
end
|
75
|
-
alias :published :updated
|
76
|
-
|
77
|
-
# A globally unique id.
|
78
|
-
def guid
|
79
|
-
return '' if @item.guid.nil?
|
80
|
-
@item.guid.content
|
81
|
-
end
|
3
|
+
module FeedAbstract
|
4
|
+
class Item
|
5
|
+
class RSS
|
82
6
|
|
7
|
+
include FeedAbstractMixins::RSS
|
8
|
+
attr_reader :item, :source
|
9
|
+
|
10
|
+
def initialize(item)
|
11
|
+
@item = @source = item
|
12
|
+
end
|
13
|
+
|
14
|
+
def title
|
15
|
+
return '' if @item.title.nil?
|
16
|
+
@item.title
|
17
|
+
end
|
18
|
+
|
19
|
+
# The full content of this item, theoretically.
|
20
|
+
def content
|
21
|
+
return '' if @item.content_encoded.nil?
|
22
|
+
@item.content_encoded
|
23
|
+
end
|
24
|
+
|
25
|
+
def summary
|
26
|
+
return '' if @item.description.nil?
|
27
|
+
@item.description
|
28
|
+
end
|
29
|
+
|
30
|
+
def link
|
31
|
+
@item.link
|
32
|
+
end
|
33
|
+
|
34
|
+
# The author list (a merge of the RSS author and dc:creator elements) as an array.
|
35
|
+
def authors
|
36
|
+
[@item.author, @item.dc_creators.collect{|c| c.content}].flatten.uniq.compact
|
37
|
+
end
|
38
|
+
|
39
|
+
# The author list as a string, joined with a comma.
|
40
|
+
def author
|
41
|
+
(self.authors.empty?) ? '' : self.authors.join(', ')
|
42
|
+
end
|
43
|
+
|
44
|
+
# The contributors (parsed from the dc:contributor element) as an array.
|
45
|
+
def contributors
|
46
|
+
(@item.dc_contributors.empty?) ? [] : @item.dc_contributors
|
47
|
+
end
|
48
|
+
|
49
|
+
# The contributor list as a string joined with a comma.
|
50
|
+
def contributor
|
51
|
+
(self.contributors.empty?) ? '' : self.contributors.join(', ')
|
52
|
+
end
|
53
|
+
|
54
|
+
# The category list as an array.
|
55
|
+
def categories
|
56
|
+
return [] if @item.categories.empty?
|
57
|
+
@item.categories.collect{|c| c.content}
|
58
|
+
end
|
59
|
+
|
60
|
+
# The category list as a string, joined with a comma.
|
61
|
+
def category
|
62
|
+
return '' if @item.categories.empty?
|
63
|
+
@item.categories.collect{|c| c.content}.join(', ')
|
83
64
|
end
|
65
|
+
|
66
|
+
# Copyright info.
|
67
|
+
def rights
|
68
|
+
(@item.dc_rights.nil?) ? '' : @item.dc_rights
|
69
|
+
end
|
70
|
+
|
71
|
+
def updated
|
72
|
+
(@item.pubDate.nil?) ? '' : @item.pubDate
|
73
|
+
end
|
74
|
+
alias :published :updated
|
75
|
+
|
76
|
+
# A globally unique id.
|
77
|
+
def guid
|
78
|
+
return '' if @item.guid.nil?
|
79
|
+
@item.guid.content
|
80
|
+
end
|
81
|
+
|
84
82
|
end
|
85
83
|
end
|
86
84
|
end
|
@@ -1,22 +1,20 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module
|
4
|
-
class Abstract
|
3
|
+
module FeedAbstract
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
#Nothing interesting. The classes in this namespace map the RSS::Parser object entries/items to the proper FeedAbstract::Item classes. Perhap item-level transformations could be supported through this class in the future.
|
6
|
+
class Items
|
7
|
+
class Atom < Array
|
8
|
+
attr_reader :feed, :items
|
10
9
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
end
|
10
|
+
def initialize(feed)
|
11
|
+
@feed = feed
|
12
|
+
return [] if @feed.items.empty?
|
13
|
+
@feed.items.each do|item|
|
14
|
+
self << ::FeedAbstract::Item::Atom.new(item)
|
17
15
|
end
|
18
|
-
|
19
16
|
end
|
17
|
+
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
@@ -1,20 +1,18 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module
|
4
|
-
class
|
5
|
-
class
|
6
|
-
|
7
|
-
attr_reader :feed, :items
|
3
|
+
module FeedAbstract
|
4
|
+
class Items
|
5
|
+
class RDF < Array
|
6
|
+
attr_reader :feed, :items
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
8
|
+
def initialize(feed)
|
9
|
+
@feed = feed
|
10
|
+
return [] if @feed.items.empty?
|
11
|
+
@feed.items.each do|item|
|
12
|
+
self << ::FeedAbstract::Item::RDF.new(item)
|
15
13
|
end
|
16
|
-
|
17
14
|
end
|
15
|
+
|
18
16
|
end
|
19
17
|
end
|
20
18
|
end
|
@@ -1,20 +1,18 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module
|
4
|
-
class
|
5
|
-
class
|
6
|
-
|
7
|
-
attr_reader :feed, :items
|
3
|
+
module FeedAbstract
|
4
|
+
class Items
|
5
|
+
class RSS < Array
|
6
|
+
attr_reader :feed, :items
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
end
|
8
|
+
def initialize(feed)
|
9
|
+
@feed = feed
|
10
|
+
return [] if @feed.items.empty?
|
11
|
+
@feed.items.each do|item|
|
12
|
+
self << ::FeedAbstract::Item::RSS.new(item)
|
15
13
|
end
|
16
|
-
|
17
14
|
end
|
15
|
+
|
18
16
|
end
|
19
17
|
end
|
20
18
|
end
|
data/lib/feed-abstract/mixins.rb
CHANGED
@@ -1,66 +1,64 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
|
3
|
-
module
|
4
|
-
module AbstractMixins
|
3
|
+
module FeedAbstractMixins
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
# Instance methods shared between Feed::Abstract::Channel::Atom and Feed::Abstract::Item::Atom
|
10
|
-
module Atom
|
5
|
+
module RSS
|
6
|
+
end
|
11
7
|
|
12
|
-
|
13
|
-
|
14
|
-
end
|
8
|
+
# Instance methods shared between FeedAbstract::Channel::Atom and FeedAbstract::Item::Atom
|
9
|
+
module Atom
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
11
|
+
def title
|
12
|
+
@source.title.content
|
13
|
+
end
|
20
14
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
15
|
+
def link
|
16
|
+
return '' if @source.link.nil?
|
17
|
+
@source.link.href
|
18
|
+
end
|
26
19
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
20
|
+
# A globally unique ID to this resource, usually (but not always) a URL.
|
21
|
+
def guid
|
22
|
+
return '' if @source.id.nil?
|
23
|
+
@source.id.content
|
24
|
+
end
|
32
25
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
26
|
+
# A Time object representing when resource was updated.
|
27
|
+
def updated
|
28
|
+
return '' if @source.updated.nil?
|
29
|
+
@source.updated.content
|
30
|
+
end
|
38
31
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
32
|
+
# Copyright info.
|
33
|
+
def rights
|
34
|
+
return '' if @source.rights.nil?
|
35
|
+
@source.rights.content
|
36
|
+
end
|
44
37
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
38
|
+
# An array of author names
|
39
|
+
def authors
|
40
|
+
return [] if @source.authors.empty?
|
41
|
+
@source.authors.collect{|au| au.name.content}
|
42
|
+
end
|
50
43
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
44
|
+
# The authors list as a string, joined with a comma.
|
45
|
+
def author
|
46
|
+
return '' if self.authors.empty?
|
47
|
+
self.authors.join(', ')
|
48
|
+
end
|
56
49
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
50
|
+
# The categories list as an array.
|
51
|
+
def categories
|
52
|
+
return [] if @source.categories.empty?
|
53
|
+
@source.categories.collect{|c| c.term}
|
54
|
+
end
|
62
55
|
|
56
|
+
# The categories list as a string joined with a comma.
|
57
|
+
def category
|
58
|
+
return '' if self.categories.empty?
|
59
|
+
self.categories.join(', ')
|
63
60
|
end
|
64
61
|
|
65
62
|
end
|
63
|
+
|
66
64
|
end
|
data/spec/feed_abstract_spec.rb
CHANGED