feed-abstract 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Rakefile +2 -18
- data/feed-abstract.gemspec +30 -11
- data/lib/feed-abstract.rb +0 -11
- data/lib/feed-abstract/channel/rss.rb +3 -1
- data/lib/feed-abstract/feed.rb +3 -3
- data/lib/feed-abstract/item/atom.rb +3 -2
- data/lib/feed-abstract/item/rss.rb +13 -3
- data/lib/feed-abstract/items/atom.rb +3 -2
- data/lib/feed-abstract/items/rdf.rb +3 -2
- data/lib/feed-abstract/items/rss.rb +3 -2
- data/lib/feed-abstract/version.rb +1 -1
- data/spec/feed_abstract_channel_spec.rb +18 -0
- data/spec/feed_abstract_item_spec.rb +16 -1
- data/spec/feed_abstract_spec.rb +1 -0
- data/spec/spec_helper.rb +5 -1
- data/spec/test_data/djcp_twitter.rss +191 -0
- metadata +77 -11
data/.gitignore
CHANGED
data/Rakefile
CHANGED
@@ -10,7 +10,7 @@ begin
|
|
10
10
|
s.name = "feed-abstract"
|
11
11
|
s.version = FeedAbstract::VERSION
|
12
12
|
s.homepage = "https://github.com/berkmancenter/feed-abstract"
|
13
|
-
s.summary = %q{Abstracts RSS/Atom/RDF parsing
|
13
|
+
s.summary = %q{Abstracts RSS/Atom/RDF stdlib parsing into a common object graph.}
|
14
14
|
s.description = %q{This library creates a common object graph for the RSS/Atom/RDF parsing classes in the ruby standard library. This allows you parse different feed formats and get back the same (or at least a very similar) set of results - item authors are accessible under an "author(s)" attribute, categories/tags/subjects are accessible under "category(ies)" attributes, etc. We do our best to make sure the data makes sense, too - RSS items lack an "updated" attribute, so we use "pubDate" to populate it. }
|
15
15
|
s.authors = ["Daniel Collis-Puro"]
|
16
16
|
s.email = ["djcp@cyber.law.harvard.edu"]
|
@@ -20,6 +20,7 @@ begin
|
|
20
20
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
21
21
|
s.require_paths = ["lib"]
|
22
22
|
s.add_development_dependency("rspec",['>= 0'])
|
23
|
+
s.add_development_dependency("simplecov",['>= 0'])
|
23
24
|
s.extra_rdoc_files = [
|
24
25
|
"README.rdoc"
|
25
26
|
]
|
@@ -30,23 +31,6 @@ end
|
|
30
31
|
|
31
32
|
Jeweler::RubygemsDotOrgTasks.new
|
32
33
|
|
33
|
-
require 'rake/testtask'
|
34
|
-
Rake::TestTask.new(:test) do |test|
|
35
|
-
test.libs << 'lib' << 'test'
|
36
|
-
test.pattern = 'test/**/test_*.rb'
|
37
|
-
test.verbose = true
|
38
|
-
end
|
39
|
-
|
40
|
-
require 'rcov/rcovtask'
|
41
|
-
Rcov::RcovTask.new do |test|
|
42
|
-
test.libs << 'test'
|
43
|
-
test.pattern = 'test/**/test_*.rb'
|
44
|
-
test.verbose = true
|
45
|
-
test.rcov_opts << '--exclude "gems/*"'
|
46
|
-
end
|
47
|
-
|
48
|
-
task :default => :test
|
49
|
-
|
50
34
|
require 'rake/rdoctask'
|
51
35
|
Rake::RDocTask.new do |rdoc|
|
52
36
|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
data/feed-abstract.gemspec
CHANGED
@@ -4,14 +4,14 @@
|
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
|
-
s.name =
|
8
|
-
s.version = "0.0.
|
7
|
+
s.name = "feed-abstract"
|
8
|
+
s.version = "0.0.8"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date =
|
13
|
-
s.description =
|
14
|
-
s.email = [
|
11
|
+
s.authors = ["Daniel Collis-Puro"]
|
12
|
+
s.date = "2012-02-19"
|
13
|
+
s.description = "This library creates a common object graph for the RSS/Atom/RDF parsing classes in the ruby standard library. This allows you parse different feed formats and get back the same (or at least a very similar) set of results - item authors are accessible under an \"author(s)\" attribute, categories/tags/subjects are accessible under \"category(ies)\" attributes, etc. We do our best to make sure the data makes sense, too - RSS items lack an \"updated\" attribute, so we use \"pubDate\" to populate it. "
|
14
|
+
s.email = ["djcp@cyber.law.harvard.edu"]
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"README.rdoc"
|
17
17
|
]
|
@@ -43,6 +43,7 @@ Gem::Specification.new do |s|
|
|
43
43
|
"spec/test_data/djcp.rss92",
|
44
44
|
"spec/test_data/djcp_code.rss",
|
45
45
|
"spec/test_data/djcp_delicious.rss",
|
46
|
+
"spec/test_data/djcp_twitter.rss",
|
46
47
|
"spec/test_data/doc.atom",
|
47
48
|
"spec/test_data/feedburner.rss",
|
48
49
|
"spec/test_data/katanapg.atom",
|
@@ -50,11 +51,11 @@ Gem::Specification.new do |s|
|
|
50
51
|
"spec/test_data/pyblosxom.atom",
|
51
52
|
"spec/test_data/zotero.rss"
|
52
53
|
]
|
53
|
-
s.homepage =
|
54
|
-
s.rdoc_options = [
|
55
|
-
s.require_paths = [
|
56
|
-
s.rubygems_version =
|
57
|
-
s.summary =
|
54
|
+
s.homepage = "https://github.com/berkmancenter/feed-abstract"
|
55
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
56
|
+
s.require_paths = ["lib"]
|
57
|
+
s.rubygems_version = "1.8.10"
|
58
|
+
s.summary = "Abstracts RSS/Atom/RDF stdlib parsing into a common object graph."
|
58
59
|
|
59
60
|
if s.respond_to? :specification_version then
|
60
61
|
s.specification_version = 3
|
@@ -63,15 +64,33 @@ Gem::Specification.new do |s|
|
|
63
64
|
s.add_runtime_dependency(%q<feed-abstract>, [">= 0"])
|
64
65
|
s.add_development_dependency(%q<rspec>, [">= 0"])
|
65
66
|
s.add_development_dependency(%q<rspec>, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
68
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
69
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
70
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
71
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
72
|
+
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
66
73
|
else
|
67
74
|
s.add_dependency(%q<feed-abstract>, [">= 0"])
|
68
75
|
s.add_dependency(%q<rspec>, [">= 0"])
|
69
76
|
s.add_dependency(%q<rspec>, [">= 0"])
|
77
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
78
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
79
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
80
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
81
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
82
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
70
83
|
end
|
71
84
|
else
|
72
85
|
s.add_dependency(%q<feed-abstract>, [">= 0"])
|
73
86
|
s.add_dependency(%q<rspec>, [">= 0"])
|
74
87
|
s.add_dependency(%q<rspec>, [">= 0"])
|
88
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
89
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
90
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
91
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
92
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
93
|
+
s.add_dependency(%q<simplecov>, [">= 0"])
|
75
94
|
end
|
76
95
|
end
|
77
96
|
|
data/lib/feed-abstract.rb
CHANGED
@@ -21,14 +21,3 @@ require 'feed-abstract/item/atom'
|
|
21
21
|
require 'feed-abstract/item/rdf'
|
22
22
|
|
23
23
|
require 'feed-abstract/feed'
|
24
|
-
|
25
|
-
|
26
|
-
#RSS::Rss::Channel::Item.class_eval{
|
27
|
-
# def foobar
|
28
|
-
# 'asdfasdf'
|
29
|
-
# end
|
30
|
-
#}
|
31
|
-
|
32
|
-
#RSS::Rss::Channel::Item.instance_eval{
|
33
|
-
# install_get_attribute('category', '', false )
|
34
|
-
#}
|
@@ -24,12 +24,14 @@ module FeedAbstract
|
|
24
24
|
end
|
25
25
|
alias :subtitle :description
|
26
26
|
|
27
|
-
# The generator of this feed as a string. Sometimes a URL, sometimes a string (e.g. the application name).
|
27
|
+
# The generator of this feed as a string. Sometimes a URL, sometimes a string (e.g. the application name). We will "sniff" out the generator for some feed sources - wordpress, delicious, twitter, etc.
|
28
28
|
def generator
|
29
29
|
if ! @feed.channel.generator.nil? && @feed.channel.generator.match(/wordpress\.org/i)
|
30
30
|
return 'WordPress'
|
31
31
|
elsif @feed.channel.link.match(/www\.delicious\.com/i)
|
32
32
|
return 'Delicious'
|
33
|
+
elsif @feed.channel.link.match(/https?:\/\/twitter\.com/i)
|
34
|
+
return 'Twitter'
|
33
35
|
end
|
34
36
|
return '' if @feed.channel.generator.nil?
|
35
37
|
@feed.channel.generator
|
data/lib/feed-abstract/feed.rb
CHANGED
@@ -97,13 +97,13 @@ module FeedAbstract
|
|
97
97
|
def negotiate_channel_class
|
98
98
|
if @raw_feed.class == RSS::Atom::Feed
|
99
99
|
@channel = Channel::Atom.new(@raw_feed)
|
100
|
-
@items = Items::Atom.new(@raw_feed)
|
100
|
+
@items = Items::Atom.new(@raw_feed,@channel)
|
101
101
|
elsif @raw_feed.class == RSS::RDF
|
102
102
|
@channel = Channel::RDF.new(@raw_feed)
|
103
|
-
@items = Items::RDF.new(@raw_feed)
|
103
|
+
@items = Items::RDF.new(@raw_feed,@channel)
|
104
104
|
else
|
105
105
|
@channel = Channel::RSS.new(@raw_feed)
|
106
|
-
@items = Items::RSS.new(@raw_feed)
|
106
|
+
@items = Items::RSS.new(@raw_feed,@channel)
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
@@ -8,10 +8,11 @@ module FeedAbstract
|
|
8
8
|
# See FeedAbstractMixins::Atom for more instance methods.
|
9
9
|
class Atom
|
10
10
|
include FeedAbstractMixins::Atom
|
11
|
-
attr_reader :item, :source
|
11
|
+
attr_reader :item, :source, :channel
|
12
12
|
|
13
|
-
def initialize(item)
|
13
|
+
def initialize(item,channel)
|
14
14
|
@item = @source = item
|
15
|
+
@channel = channel
|
15
16
|
end
|
16
17
|
|
17
18
|
# The full content of the item, most likely html.
|
@@ -5,10 +5,11 @@ module FeedAbstract
|
|
5
5
|
class RSS
|
6
6
|
|
7
7
|
include FeedAbstractMixins::RSS
|
8
|
-
attr_reader :item, :source
|
8
|
+
attr_reader :item, :source, :channel
|
9
9
|
|
10
|
-
def initialize(item)
|
10
|
+
def initialize(item,channel)
|
11
11
|
@item = @source = item
|
12
|
+
@channel = channel
|
12
13
|
end
|
13
14
|
|
14
15
|
def title
|
@@ -33,6 +34,9 @@ module FeedAbstract
|
|
33
34
|
|
34
35
|
# The author list (a merge of the RSS author and dc:creator elements) as an array.
|
35
36
|
def authors
|
37
|
+
if self.channel.generator == 'Twitter'
|
38
|
+
return [@item.title.split(':')[0]]
|
39
|
+
end
|
36
40
|
[@item.author, ((@item.dc_creators.empty?) ? nil : @item.dc_creators.collect{|c| c.content})].flatten.uniq.compact.reject{|au| au == '' || au.match(/^\s+$/)}
|
37
41
|
end
|
38
42
|
|
@@ -53,13 +57,19 @@ module FeedAbstract
|
|
53
57
|
|
54
58
|
# The category list as an array.
|
55
59
|
def categories
|
60
|
+
if self.channel.generator == 'Twitter'
|
61
|
+
return @item.title.scan(/#([^#\s]+)/).flatten
|
62
|
+
end
|
56
63
|
return [] if @item.categories.empty?
|
57
64
|
@item.categories.collect{|c| c.content}.reject{|c| c == '' || c.match(/^\s+$/)}
|
58
65
|
end
|
59
66
|
|
60
67
|
# The category list as a string, joined with a comma.
|
61
68
|
def category
|
62
|
-
|
69
|
+
if self.channel.generator == 'Twitter'
|
70
|
+
return self.categories.join(', ')
|
71
|
+
end
|
72
|
+
return '' if @item.categories.empty?
|
63
73
|
@item.categories.collect{|c| c.content}.join(', ')
|
64
74
|
end
|
65
75
|
|
@@ -7,11 +7,12 @@ module FeedAbstract
|
|
7
7
|
class Atom < Array
|
8
8
|
attr_reader :feed, :items
|
9
9
|
|
10
|
-
def initialize(feed)
|
10
|
+
def initialize(feed,channel)
|
11
11
|
@feed = feed
|
12
|
+
@channel = channel
|
12
13
|
return [] if @feed.items.empty?
|
13
14
|
@feed.items.each do|item|
|
14
|
-
self << ::FeedAbstract::Item::Atom.new(item)
|
15
|
+
self << ::FeedAbstract::Item::Atom.new(item,channel)
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
@@ -5,11 +5,12 @@ module FeedAbstract
|
|
5
5
|
class RDF < Array
|
6
6
|
attr_reader :feed, :items
|
7
7
|
|
8
|
-
def initialize(feed)
|
8
|
+
def initialize(feed,channel)
|
9
9
|
@feed = feed
|
10
|
+
@channel = channel
|
10
11
|
return [] if @feed.items.empty?
|
11
12
|
@feed.items.each do|item|
|
12
|
-
self << ::FeedAbstract::Item::RDF.new(item)
|
13
|
+
self << ::FeedAbstract::Item::RDF.new(item,channel)
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
@@ -5,11 +5,12 @@ module FeedAbstract
|
|
5
5
|
class RSS < Array
|
6
6
|
attr_reader :feed, :items
|
7
7
|
|
8
|
-
def initialize(feed)
|
8
|
+
def initialize(feed,channel)
|
9
9
|
@feed = feed
|
10
|
+
@channel = channel
|
10
11
|
return [] if @feed.items.empty?
|
11
12
|
@feed.items.each do|item|
|
12
|
-
self << ::FeedAbstract::Item::RSS.new(item)
|
13
|
+
self << ::FeedAbstract::Item::RSS.new(item,channel)
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
@@ -19,6 +19,7 @@ module FeedAbstract
|
|
19
19
|
it { @feedburner.channel.should respond_to att}
|
20
20
|
it { @pyblosxom.channel.should respond_to att}
|
21
21
|
it { @chill.channel.should respond_to att}
|
22
|
+
it { @twitter.channel.should respond_to att}
|
22
23
|
|
23
24
|
it { @docatom.channel.send(att).should_not == false}
|
24
25
|
it { @kpgatom.channel.send(att).should_not == false}
|
@@ -30,6 +31,7 @@ module FeedAbstract
|
|
30
31
|
it { @feedburner.channel.send(att).should_not == false}
|
31
32
|
it { @pyblosxom.channel.send(att).should_not == false}
|
32
33
|
it { @chill.channel.send(att).should_not == false}
|
34
|
+
it { @twitter.channel.send(att).should_not == false}
|
33
35
|
end
|
34
36
|
|
35
37
|
it "should have the correct title" do
|
@@ -43,6 +45,7 @@ module FeedAbstract
|
|
43
45
|
@feedburner.channel.title.should == 'CNN.com'
|
44
46
|
@pyblosxom.channel.title.should == 'Copyrighteous'
|
45
47
|
@chill.channel.title.should == 'Chilling Effects Clearinghouse Weather Reports'
|
48
|
+
@twitter.channel.title.should == 'Twitter / djcp'
|
46
49
|
end
|
47
50
|
|
48
51
|
it "should have the correct subtitle and description" do
|
@@ -75,6 +78,9 @@ module FeedAbstract
|
|
75
78
|
|
76
79
|
@chill.channel.description.should == 'Monitoring the legal climate for Internet activity (database of annotated cease and desist letters)'
|
77
80
|
@chill.channel.subtitle.should == 'Monitoring the legal climate for Internet activity (database of annotated cease and desist letters)'
|
81
|
+
|
82
|
+
@twitter.channel.description.should == 'Twitter updates from Daniel Collis Puro / djcp.'
|
83
|
+
@twitter.channel.subtitle.should == 'Twitter updates from Daniel Collis Puro / djcp.'
|
78
84
|
end
|
79
85
|
|
80
86
|
it "should have the correct link" do
|
@@ -88,6 +94,7 @@ module FeedAbstract
|
|
88
94
|
@feedburner.channel.link.should == 'http://www.cnn.com/?eref=rss_topstories'
|
89
95
|
@pyblosxom.channel.link.should == 'http://mako.cc/copyrighteous'
|
90
96
|
@chill.channel.link.should == 'http://www.chillingeffects.org'
|
97
|
+
@twitter.channel.link.should == 'http://twitter.com/djcp'
|
91
98
|
end
|
92
99
|
|
93
100
|
it "should have the correct generator" do
|
@@ -101,6 +108,7 @@ module FeedAbstract
|
|
101
108
|
@feedburner.channel.generator.should == ''
|
102
109
|
@pyblosxom.channel.generator.should == "\nPyBlosxom http://pyblosxom.sourceforge.net/ 1.5rc2 20100803\n"
|
103
110
|
@chill.channel.generator.should == ''
|
111
|
+
@twitter.channel.generator.should == 'Twitter'
|
104
112
|
end
|
105
113
|
|
106
114
|
it "should have the correct language" do
|
@@ -114,6 +122,7 @@ module FeedAbstract
|
|
114
122
|
@feedburner.channel.language.should == 'en-us'
|
115
123
|
@pyblosxom.channel.language.should == 'en'
|
116
124
|
@chill.channel.language.should == 'en-us'
|
125
|
+
@twitter.channel.language.should == 'en-us'
|
117
126
|
end
|
118
127
|
|
119
128
|
it "should have the correct authors" do
|
@@ -127,6 +136,7 @@ module FeedAbstract
|
|
127
136
|
@feedburner.channel.authors.should == []
|
128
137
|
@pyblosxom.channel.authors.should == ['Benjamin Mako Hill']
|
129
138
|
@chill.channel.authors.should == ['Wendy Seltzer, wseltzer@chillingeffects.org']
|
139
|
+
@twitter.channel.authors.should == []
|
130
140
|
|
131
141
|
@docatom.channel.author.should == 'Doc Searls'
|
132
142
|
@kpgatom.channel.author.should == 'Nick Pappas'
|
@@ -138,6 +148,7 @@ module FeedAbstract
|
|
138
148
|
@feedburner.channel.author.should == ''
|
139
149
|
@pyblosxom.channel.author.should == 'Benjamin Mako Hill'
|
140
150
|
@chill.channel.author.should == 'Wendy Seltzer, wseltzer@chillingeffects.org'
|
151
|
+
@twitter.channel.author.should == ''
|
141
152
|
|
142
153
|
end
|
143
154
|
|
@@ -152,6 +163,7 @@ module FeedAbstract
|
|
152
163
|
@feedburner.channel.categories.should == []
|
153
164
|
@pyblosxom.channel.categories.should == []
|
154
165
|
@chill.channel.categories.should == ['Your rights online']
|
166
|
+
@twitter.channel.categories.should == []
|
155
167
|
|
156
168
|
@docatom.channel.category.should == ''
|
157
169
|
@kpgatom.channel.category.should == 'photos'
|
@@ -163,6 +175,7 @@ module FeedAbstract
|
|
163
175
|
@feedburner.channel.category.should == ''
|
164
176
|
@pyblosxom.channel.category.should == ''
|
165
177
|
@chill.channel.category.should == 'Your rights online'
|
178
|
+
@twitter.channel.category.should == ''
|
166
179
|
end
|
167
180
|
|
168
181
|
it "should have the correct icon" do
|
@@ -176,6 +189,7 @@ module FeedAbstract
|
|
176
189
|
@feedburner.channel.icon.should == 'http://i2.cdn.turner.com/cnn/.element/img/1.0/logo/cnn.logo.rss.gif'
|
177
190
|
@pyblosxom.channel.icon.should == ''
|
178
191
|
@chill.channel.icon.should == 'http://images.chillingeffects.org/chilling_effects.gif'
|
192
|
+
@twitter.channel.icon.should == ''
|
179
193
|
end
|
180
194
|
|
181
195
|
it "should have the correct logo" do
|
@@ -189,6 +203,7 @@ module FeedAbstract
|
|
189
203
|
@feedburner.channel.logo.should == 'http://i2.cdn.turner.com/cnn/.element/img/1.0/logo/cnn.logo.rss.gif'
|
190
204
|
@pyblosxom.channel.logo.should == ''
|
191
205
|
@chill.channel.logo.should == 'http://images.chillingeffects.org/chilling_effects.gif'
|
206
|
+
@twitter.channel.logo.should == ''
|
192
207
|
end
|
193
208
|
|
194
209
|
it "should have the correct rights" do
|
@@ -202,6 +217,7 @@ module FeedAbstract
|
|
202
217
|
@feedburner.channel.rights.should == ' 2011 Cable News Network LP, LLLP.'
|
203
218
|
@pyblosxom.channel.rights.should == 'Creative Commons Attribution-ShareAlike'
|
204
219
|
@chill.channel.rights.should == ''
|
220
|
+
@twitter.channel.rights.should == ''
|
205
221
|
end
|
206
222
|
|
207
223
|
it "should have the correct updated value" do
|
@@ -215,6 +231,7 @@ module FeedAbstract
|
|
215
231
|
@feedburner.channel.updated.should == Time.parse('Sat, 03 Sep 2011 16:14:16 EDT')
|
216
232
|
@pyblosxom.channel.updated.should == Time.parse('2011-09-15T05:21:00Z')
|
217
233
|
@chill.channel.updated.should == Time.parse('2002-02-25T12:00+00:00')
|
234
|
+
@twitter.channel.updated.should == ''
|
218
235
|
end
|
219
236
|
|
220
237
|
it "should have the correct guid" do
|
@@ -228,6 +245,7 @@ module FeedAbstract
|
|
228
245
|
@feedburner.channel.guid.should == 'http://www.cnn.com/?eref=rss_topstories'
|
229
246
|
@pyblosxom.channel.guid.should == 'http://mako.cc/copyrighteous/'
|
230
247
|
@chill.channel.guid.should == 'http://www.chillingeffects.org'
|
248
|
+
@twitter.channel.guid.should == 'http://twitter.com/djcp'
|
231
249
|
end
|
232
250
|
end
|
233
251
|
end
|
@@ -13,7 +13,7 @@ module FeedAbstract
|
|
13
13
|
|
14
14
|
it "responds to all attributes without causing an error" do
|
15
15
|
@all_feeds.each do |feed|
|
16
|
-
[:title, :summary, :content, :link, :authors, :author, :contributor, :contributors, :categories, :category, :rights, :updated, :guid, :published].each do|att|
|
16
|
+
[:title, :summary, :content, :link, :authors, :author, :contributor, :contributors, :categories, :category, :rights, :updated, :guid, :published, :channel].each do|att|
|
17
17
|
feed.items.each do|item|
|
18
18
|
item.should respond_to att
|
19
19
|
item.send(att).should_not == false
|
@@ -32,6 +32,7 @@ module FeedAbstract
|
|
32
32
|
@feedburneritem.title.should == 'Did Libya help CIA with renditions of terror suspects?'
|
33
33
|
@pyblosxomitem.title.should == "Anxiety\n"
|
34
34
|
@chillitem.title.should == "Takedown Complaints in the Android Marketplace"
|
35
|
+
@twitteritem.title.should == "djcp: @csoghoian @BrookingsInst and the clipboard as well, presumably. #security #keylogging"
|
35
36
|
|
36
37
|
end
|
37
38
|
|
@@ -50,6 +51,8 @@ module FeedAbstract
|
|
50
51
|
|
51
52
|
@pyblosxomitem.summary.should == ""
|
52
53
|
@chillitem.summary.should == %Q$<br>\n<img src=\"//images.chillingeffects.org/thermometer.gif\" alt=\"thermometer\" width=\"35\" height=\"120\"align=left> <h2>Takedown Complaints in the Android Marketplace</h2><p>Wendy Seltzer, <i>Chilling Effects Clearinghouse</i>, March 3, 2011\n<p><i>Abstract:</i> Earlier this year, Google began sending to Chilling Effects the requests it received for takedown from the Android Marketplace. Since this represents a new source of data, we take a look at the first month's input, February 2011.<hr size=1>\n<p><hr size=1 width=\"75%\"><p>Since Apple launched its iPhone App Store, applications marketplaces have popped up with increasing prominence. Google, unlike Apple<a href=\"#note1\" name=\"back\">*</a>, does not lock Android users into purchasing from its <a href=\"https://market.android.com/\">Android Market</a>, but it does make that marketplace a convenient place to find Android applications (<a href=\"http://mashable.com/2010/10/25/android-100000-apps/\">passing 100,000 apps</a> late last year). <br><p><br>In February, Chilling Effects <a href=\"https://www.chillingeffects.org/search.cgi?search=Android\">saw</a> 206 complaints to Google regarding Android Market apps, almost evenly split between trademark and copyright.<a href=\"#note2\" name=\"back2\">*</a> Because the Android Market offers commercial transactions, its context differs somewhat from the search and blog hosting in which DMCA -- copyright -- complaints predominate. At the same time, many apps are offered free of charge.<p><br><img src=\"https://chart.googleapis.com/chart?cht=pc&chtt=Complaint+Subjects+to+Android+Market+(Feb+2011)&chs=750x400&chd=t:111,97,3|0.59,0.31,0.05,0.03,0.02,0.02,0.02,0.38,0.11,0.1,0.07,0.03,0.03,0.02,0.02,0.02,0.01,0.01,0.22&chl=[TM]|[C]||Trademark%20%20(59)|Facebook%20Trademark%20%20(31)|Bank%20Trademark%20%20(5)|SXSW%20Trademark%20%20(3)|Trademarks%20and%20Videos%20Copyright%20%20(2)|Game%20Trademark%20%20(2)|Disney%20Trademark%20and%20Copyright%20%20(2)|Game%20Copyright%20%20(38)|Software%20Copyright%20%20(11)|Logo%20Copyright%20%20(10)|Copyright%20%20(7)|Books%20Copyright%20%20(3)|Images%20Copyright%20%20(3)|Articles%20Copyright%20%20(2)|Television%20Copyright%20%20(2)|UFC%20Copyright%20%20(2)|Photo%20Copyright%20%20(1)|Website%20Copyright%20%20(1)|Other/Unidentified%20(22)&chco=0000FF,003333&chp=3.14\" title=\"Complaint Subjects to Google's Android Market, Feb. 2011\"><p>Slightly more than half of the complaints here claim trademark infringement: misleading use of a mark (word or logo) to cause consumer confusion about the application's source or sponsorship. Facebook led the way, challenging 30+ apps that borrowed its name or \"f\" logo (e.g. <a href=\"/trademark/notice.cgi?NoticeID=56735\">Facebook Trademark Complaint to Google: Android Market</a>). Banks and financial institutions challenged applications that used their logos, even to offer a mobile version of the bank's own site. Here is trademark as the law of consumer protection: you want to be sure an unauthorized Wells Fargo application won't channel your deposit into its coffers rather than your savings account. Google even filed several complaints against apps in its marketplace that were a bit too free with the Google name. Starbucks used copyright in its logo to similar end, requesting takedown or alteration of apps that used the coffee company's logo as their icon (e.g. <a href=\"/dmca512c/notice.cgi?NoticeID=60149\">Logo DMCA (Copyright) Complaint to Google: Android Market</a>). Some, including <a href=\"https://market.android.com/details?id=com.birbeck.starbuckscard\">My Coffee Card</a> (formerly Starbucks Card Widget) changed their name and icon in response.<p>Sometimes, these complaints didn't do much to allege \"likelihood of consumer confusion,\" the traditional hallmark of trademark infringement. South By SouthWest's complaint against the \"Unauthorized SXSW\" party scheduler app -- <a href=\"/trademark/notice.cgi?NoticeID=60951\">SXSW Trademark Complaint to Google: Android Market</a> -- sounds like an overstep, into nominative fair use. There's no good alternative way to refer to the big conference/music festival in Austin later in March, and \"unauthorized\" plainly indicates it's not an officially endorsed product. (Neither Unofficial SXSW nor the official \"SXSW® GO\" gets particularly high marks from reviewers yet, but that may be because the event hasn't yet kicked off to fill them with data.)<p>Another 82 complaints (less 11 \"logo\" complaints) alleged copyright infringement -- copying of code, of graphical elements or \"look and feel\" (Nintendo complained about many \"Super Mario\" derivatives), of characters (Columbia's \"Qbert\"), or of audiovisual elements including video clips, wallpapers, and eBooks. Most of these were phrased in DMCA terms, since like a blog platform, the marketplace hosts \"information residing on systems or networks at direction of users\" <a href=\"http://static.chillingeffects.org/512.html#c\">512(c)</a>. <p>Unique among the copyright complaints in its indirection, the RIAA filed three complaints naming dozens of apps that allegedly \"facilitate the unauthorized streaming and downloading of popular sound recordings, the vast majority of which are owned or controlled by RIAA members.\" Its <a href=\"/dmca512c/notice.cgi?NoticeID=61677\">complaints</a>, complete with screenshots, asked for the removal of apps for ringtone creation and MP3 listening. This is an attenuated claim. RIAA isn't alleging that Google, or even the apps, are direct infringers of member copyrights. Rather, it claims that because Google hosts apps (or in some cases, serves ads inside them) that enable end-users to make infringing copies of music, Google should be held responsible for the users' infringing conduct -- a sort of once-removed contributory or vicarious liability claim. Is there a law of contributory inducement, after <a href=\"http://w2.eff.org/IP/P2P/MGM_v_Grokster/\">Grokster</a>? <p><br>I haven't followed all of the URLs to see how Google has responded to each of these complaints, but to its credit, it does not appear to have pulled the \"Unauthorized SXSW,\" for example. Others accused of trademark infringement appear to have changed their names or logos. Because the <a href=\"https://www.chillingeffects.org/dmca512/faq\">DMCA</a> applies only to copyright, not trademark, there is less settled procedure around the trademark claims. There's no safe-harbor, but neither is there an assumption that the service provider will be liable for its users' activity (see <a href=\"http://www.eff.org/deeplinks/2010/04/tiffany-v-ebay-what-about-put-back\">Tiffany v. eBay</a>, where the Second Circuit Court of Appeals held that \"for contributory trademark infringement liability to lie, a service provider must have more than a general knowledge or reason to know that its service is being used to sell counterfeit goods.\"). <p><br><img src=\"https://chart.googleapis.com/chart?cht=p&chtt=Complaint+Senders+to+Android+Market+(Feb+2011)&chs=750x400&chd=t:0.3,0.2,0.08,0.06,0.04,0.03,0.03,0.03,0.03,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,0.02,1.09&chl=Facebook,%20Inc.%20(30)|Nintendo%20of%20America%20Inc.%20(20)|Google%20Inc.%20(8)|Starbucks%20Corporation%20(6)|Hatched%20Games%20(4)|RIAA%20(3)|Photo%20Hunt%20(3)|Atari,%20Inc.%20(3)|ESPN%20Inc.%20(3)|Playboy%20Enterprises%20International,%20Inc.%20(2)|Electronic%20Arts%20Inc.%20(2)|The%20Walt%20Disney%20Company%20(2)|Monotype%20Imaging%20Inc.%20(2)|Kodansha%20Ltd.%20(2)|Metro,%20a%20division%20of%20Associated%20Newspapers%20Limited%20(2)|Dorna%20Sports%20(2)|Justin.tv,%20Inc.%20(2)|Rocket%20Radar%20(2)|eBay%20Inc.%20(2)|Promevo,%20LLC%20(2)|Other/Unidentified%20(109)&chco=0000FF&chp=3.14\" title=\"Takedown Senders\"><p><a name=\"note1\" href=\"#back\">*</a> Copyright geeks will recall that the last <a href=\"http://www.loc.gov/today/pr/2010/10-169.html\">anticircumvention rulemaking</a> exempted phone jailbreaking from the circumvention rule, so iPhone owners can confidently unlock their phones to use the <a href=\"http://cydia.saurik.com/\">cydia</a> marketplace or others.<br><a name=\"note2\" href=\"#back2\">*</a> Note that this is a simple count of distinct complaint submissions, each of which may target one or multiple allegedly infringing applications. $
|
54
|
+
|
55
|
+
@twitteritem.summary.should == "djcp: @csoghoian @BrookingsInst and the clipboard as well, presumably. #security #keylogging"
|
53
56
|
end
|
54
57
|
|
55
58
|
it "should have the correct content" do
|
@@ -101,6 +104,7 @@ module FeedAbstract
|
|
101
104
|
@feedburneritem.content.should == ''
|
102
105
|
@pyblosxomitem.content.should == %Q|<div><a href='http://www.flickr.com/photos/nffcnnr/401047557/' target='_blank'><img src='http://farm1.static.flickr.com/137/401047557_1dda26e16f.jpg' width=350 alt='MailBoxes by nffcnnr, on Flickr' title='MailBoxes by nffcnnr, on Flickr' border='0'/></a><br/><a href='http://creativecommons.org/licenses/by/2.0/' target='_blank'><img src='http://i.creativecommons.org/l/by/2.0/80x15.png' alt='Creative Commons Attribution 2.0 Generic License' title='Creative Commons Attribution 2.0 Generic License' border='0' align='left'></a> by <a href='http://www.flickr.com/people/nffcnnr/' target='_blank'> nffcnnr</a><a href='http://www.imagecodr.org/' target='_blank'> </a></div><p>I am haunted by the nagging fear that I have mailboxes, tucked into a\ndark corner of an office somewhere, and perhaps even full of checks\nand important documents, that I don't know exist.</p>\n|
|
103
106
|
@chillitem.content.should == ''
|
107
|
+
@twitteritem.content.should == ""
|
104
108
|
end
|
105
109
|
|
106
110
|
it "should have the correct link" do
|
@@ -113,6 +117,7 @@ module FeedAbstract
|
|
113
117
|
@feedburneritem.link.should == 'http://rss.cnn.com/~r/rss/cnn_topstories/~3/sV5N-C76mOM/index.html'
|
114
118
|
@pyblosxomitem.link.should == 'http://mako.cc/copyrighteous/20110913-00'
|
115
119
|
@chillitem.link.should == 'https://www.chillingeffects.org/weather.cgi?WeatherID=648'
|
120
|
+
@twitteritem.link.should == 'http://twitter.com/djcp/statuses/168381896559046656'
|
116
121
|
end
|
117
122
|
|
118
123
|
it "should have the correct author" do
|
@@ -125,6 +130,7 @@ module FeedAbstract
|
|
125
130
|
@feedburneritem.author.should == ''
|
126
131
|
@pyblosxomitem.author.should == ''
|
127
132
|
@chillitem.author.should == ''
|
133
|
+
@twitteritem.author.should == 'djcp'
|
128
134
|
end
|
129
135
|
|
130
136
|
it "should have the correct authors" do
|
@@ -137,6 +143,7 @@ module FeedAbstract
|
|
137
143
|
@feedburneritem.authors.should == []
|
138
144
|
@pyblosxomitem.authors.should == []
|
139
145
|
@chillitem.authors.should == []
|
146
|
+
@twitteritem.authors.should == ['djcp']
|
140
147
|
end
|
141
148
|
|
142
149
|
it "should have the correct contributor" do
|
@@ -149,6 +156,7 @@ module FeedAbstract
|
|
149
156
|
@feedburneritem.contributor.should == ''
|
150
157
|
@pyblosxomitem.contributor.should == ''
|
151
158
|
@chillitem.contributor.should == ''
|
159
|
+
@twitteritem.contributor.should == ''
|
152
160
|
end
|
153
161
|
|
154
162
|
it "should have the correct contributors" do
|
@@ -161,6 +169,7 @@ module FeedAbstract
|
|
161
169
|
@feedburneritem.contributors.should == []
|
162
170
|
@pyblosxomitem.contributors.should == []
|
163
171
|
@chillitem.contributors.should == []
|
172
|
+
@twitteritem.contributors.should == []
|
164
173
|
end
|
165
174
|
|
166
175
|
it "should have the correct category" do
|
@@ -173,6 +182,7 @@ module FeedAbstract
|
|
173
182
|
@feedburneritem.category.should == ''
|
174
183
|
@pyblosxomitem.category.should == ''
|
175
184
|
@chillitem.category.should == ''
|
185
|
+
@twitteritem.category.should == 'security, keylogging'
|
176
186
|
end
|
177
187
|
|
178
188
|
it "should have the correct categories" do
|
@@ -185,6 +195,7 @@ module FeedAbstract
|
|
185
195
|
@feedburneritem.categories.should == []
|
186
196
|
@pyblosxomitem.categories.should == []
|
187
197
|
@chillitem.categories.should == []
|
198
|
+
@twitteritem.categories.should == ['security', 'keylogging']
|
188
199
|
end
|
189
200
|
|
190
201
|
it "should have the correct rights" do
|
@@ -197,6 +208,7 @@ module FeedAbstract
|
|
197
208
|
@feedburneritem.rights.should == ''
|
198
209
|
@pyblosxomitem.rights.should == ''
|
199
210
|
@chillitem.rights.should == ''
|
211
|
+
@twitteritem.rights.should == ''
|
200
212
|
end
|
201
213
|
|
202
214
|
it "should have been updated at the correct time" do
|
@@ -209,6 +221,7 @@ module FeedAbstract
|
|
209
221
|
@feedburneritem.updated.should == Time.parse('Sat, 03 Sep 2011 16:11:39 EDT')
|
210
222
|
@pyblosxomitem.updated.should == Time.parse('2011-09-15T05:21:00Z')
|
211
223
|
@chillitem.updated.should == ""
|
224
|
+
@twitteritem.updated.should == Time.parse('2012-02-11 12:12:27 -0500')
|
212
225
|
end
|
213
226
|
|
214
227
|
it "should have been published at the proper time" do
|
@@ -221,6 +234,7 @@ module FeedAbstract
|
|
221
234
|
@feedburneritem.published.should == Time.parse('Sat, 03 Sep 2011 16:11:39 EDT')
|
222
235
|
@pyblosxomitem.published.should == Time.parse('2011-09-15T05:21:00Z')
|
223
236
|
@chillitem.published.should == ''
|
237
|
+
@twitteritem.published.should == Time.parse('2012-02-11 12:12:27 -0500')
|
224
238
|
end
|
225
239
|
|
226
240
|
it "should have the proper guid" do
|
@@ -233,6 +247,7 @@ module FeedAbstract
|
|
233
247
|
@feedburneritem.guid.should == 'http://www.cnn.com/2011/WORLD/africa/09/03/libya.west.spies/index.html?eref=rss_topstories'
|
234
248
|
@pyblosxomitem.guid.should == 'http://mako.cc/copyrighteous/2011/09/15/20110913-00'
|
235
249
|
@chillitem.guid.should == 'https://www.chillingeffects.org/weather.cgi?WeatherID=648'
|
250
|
+
@twitteritem.guid.should == 'http://twitter.com/djcp/statuses/168381896559046656'
|
236
251
|
end
|
237
252
|
|
238
253
|
end
|
data/spec/feed_abstract_spec.rb
CHANGED
@@ -22,6 +22,7 @@ module FeedAbstract
|
|
22
22
|
@djcprss2.channel.class.should == Channel::RSS
|
23
23
|
@djcprss92.channel.class.should == Channel::RSS
|
24
24
|
@delicious.channel.class.should == Channel::RSS
|
25
|
+
@twitter.channel.class.should == Channel::RSS
|
25
26
|
end
|
26
27
|
|
27
28
|
it "should recognize rdf feeds properly" do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start
|
2
4
|
|
3
5
|
$:.unshift File.dirname(__FILE__) + '/../lib'
|
4
6
|
require 'feed-abstract'
|
@@ -14,8 +16,9 @@ def instantiate_feeds
|
|
14
16
|
@feedburner = FeedAbstract::Feed.new(File.open('spec/test_data/feedburner.rss'))
|
15
17
|
@pyblosxom = FeedAbstract::Feed.new(File.open('spec/test_data/pyblosxom.atom'))
|
16
18
|
@chill = FeedAbstract::Feed.new(File.open('spec/test_data/chillingeffects.xml'))
|
19
|
+
@twitter = FeedAbstract::Feed.new(File.open('spec/test_data/djcp_twitter.rss'))
|
17
20
|
|
18
|
-
@all_feeds = [@docatom, @kpgatom, @djcprss2, @djcprss92, @oa, @delicious, @zotero, @feedburner
|
21
|
+
@all_feeds = [@docatom, @kpgatom, @djcprss2, @djcprss92, @oa, @delicious, @zotero, @feedburner, @pyblosxom, @chill, @twitter]
|
19
22
|
end
|
20
23
|
|
21
24
|
def instantiate_example_items
|
@@ -28,4 +31,5 @@ def instantiate_example_items
|
|
28
31
|
@feedburneritem = @feedburner.items.first
|
29
32
|
@pyblosxomitem = @pyblosxom.items.first
|
30
33
|
@chillitem = @chill.items.first
|
34
|
+
@twitteritem = @twitter.items.first
|
31
35
|
end
|
@@ -0,0 +1,191 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<rss xmlns:georss="http://www.georss.org/georss" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:twitter="http://api.twitter.com" version="2.0">
|
3
|
+
<channel>
|
4
|
+
<title>Twitter / djcp</title>
|
5
|
+
<link>http://twitter.com/djcp</link>
|
6
|
+
<atom:link type="application/rss+xml" rel="self" href="https://twitter.com/statuses/user_timeline/djcp.rss"/>
|
7
|
+
<description>Twitter updates from Daniel Collis Puro / djcp.</description>
|
8
|
+
<language>en-us</language>
|
9
|
+
<ttl>40</ttl>
|
10
|
+
<item>
|
11
|
+
<title>djcp: @csoghoian @BrookingsInst and the clipboard as well, presumably. #security #keylogging</title>
|
12
|
+
<description>djcp: @csoghoian @BrookingsInst and the clipboard as well, presumably. #security #keylogging</description>
|
13
|
+
<pubDate>Sat, 11 Feb 2012 17:12:27 +0000</pubDate>
|
14
|
+
<guid>http://twitter.com/djcp/statuses/168381896559046656</guid>
|
15
|
+
<link>http://twitter.com/djcp/statuses/168381896559046656</link>
|
16
|
+
<twitter:source>web</twitter:source>
|
17
|
+
<twitter:place/>
|
18
|
+
</item>
|
19
|
+
<item>
|
20
|
+
<title>djcp: @slicehost STL migration got me disks that are twice as fast and a CPU upgrade to boot. Nice. Almost no down-time.</title>
|
21
|
+
<description>djcp: @slicehost STL migration got me disks that are twice as fast and a CPU upgrade to boot. Nice. Almost no down-time.</description>
|
22
|
+
<pubDate>Sat, 11 Feb 2012 17:09:43 +0000</pubDate>
|
23
|
+
<guid>http://twitter.com/djcp/statuses/168381208810618880</guid>
|
24
|
+
<link>http://twitter.com/djcp/statuses/168381208810618880</link>
|
25
|
+
<twitter:source>web</twitter:source>
|
26
|
+
<twitter:place/>
|
27
|
+
</item>
|
28
|
+
<item>
|
29
|
+
<title>djcp: If you walk slow and take wide turns, you are annoying.</title>
|
30
|
+
<description>djcp: If you walk slow and take wide turns, you are annoying.</description>
|
31
|
+
<pubDate>Thu, 09 Feb 2012 13:42:58 +0000</pubDate>
|
32
|
+
<guid>http://twitter.com/djcp/statuses/167604404399251456</guid>
|
33
|
+
<link>http://twitter.com/djcp/statuses/167604404399251456</link>
|
34
|
+
<twitter:source><a href="http://seesmic.com/" rel="nofollow">Seesmic</a></twitter:source>
|
35
|
+
<twitter:place/>
|
36
|
+
</item>
|
37
|
+
<item>
|
38
|
+
<title>djcp: As expected: Defendant Ordered to Decrypt Laptop May Have Forgotten Password http://t.co/CPQ8Zl2t</title>
|
39
|
+
<description>djcp: As expected: Defendant Ordered to Decrypt Laptop May Have Forgotten Password http://t.co/CPQ8Zl2t</description>
|
40
|
+
<pubDate>Tue, 07 Feb 2012 02:55:44 +0000</pubDate>
|
41
|
+
<guid>http://twitter.com/djcp/statuses/166716746378641408</guid>
|
42
|
+
<link>http://twitter.com/djcp/statuses/166716746378641408</link>
|
43
|
+
<twitter:source><a href="http://twitter.com/tweetbutton" rel="nofollow">Tweet Button</a></twitter:source>
|
44
|
+
<twitter:place/>
|
45
|
+
</item>
|
46
|
+
<item>
|
47
|
+
<title>djcp: Trying to cheat the system so I can check out my geek cred http://t.co/GpWRdu71 via @coderwall</title>
|
48
|
+
<description>djcp: Trying to cheat the system so I can check out my geek cred http://t.co/GpWRdu71 via @coderwall</description>
|
49
|
+
<pubDate>Mon, 06 Feb 2012 20:30:27 +0000</pubDate>
|
50
|
+
<guid>http://twitter.com/djcp/statuses/166619786040717312</guid>
|
51
|
+
<link>http://twitter.com/djcp/statuses/166619786040717312</link>
|
52
|
+
<twitter:source><a href="http://twitter.com/tweetbutton" rel="nofollow">Tweet Button</a></twitter:source>
|
53
|
+
<twitter:place/>
|
54
|
+
</item>
|
55
|
+
<item>
|
56
|
+
<title>djcp: The "shocker" example COMPLETELY turns me off #whiskey_disk. Frickin' #brogrammers. https://t.co/8KccqFLx</title>
|
57
|
+
<description>djcp: The "shocker" example COMPLETELY turns me off #whiskey_disk. Frickin' #brogrammers. https://t.co/8KccqFLx</description>
|
58
|
+
<pubDate>Mon, 06 Feb 2012 14:08:42 +0000</pubDate>
|
59
|
+
<guid>http://twitter.com/djcp/statuses/166523714396110848</guid>
|
60
|
+
<link>http://twitter.com/djcp/statuses/166523714396110848</link>
|
61
|
+
<twitter:source>web</twitter:source>
|
62
|
+
<twitter:place/>
|
63
|
+
</item>
|
64
|
+
<item>
|
65
|
+
<title>djcp: RT @csoghoian: Microsoft takes out full page ad in newspapers to tout privacy benefits of Bing+Hotmail. Yet Bing still doesn't work over ...</title>
|
66
|
+
<description>djcp: RT @csoghoian: Microsoft takes out full page ad in newspapers to tout privacy benefits of Bing+Hotmail. Yet Bing still doesn't work over ...</description>
|
67
|
+
<pubDate>Sat, 04 Feb 2012 00:16:06 +0000</pubDate>
|
68
|
+
<guid>http://twitter.com/djcp/statuses/165589410103033856</guid>
|
69
|
+
<link>http://twitter.com/djcp/statuses/165589410103033856</link>
|
70
|
+
<twitter:source><a href="http://seesmic.com/" rel="nofollow">Seesmic</a></twitter:source>
|
71
|
+
<twitter:place/>
|
72
|
+
</item>
|
73
|
+
<item>
|
74
|
+
<title>djcp: @berkmancenter now hosts 1,010 active blogs under our #wordpress #multisite install at http://t.co/NLzbUn6W</title>
|
75
|
+
<description>djcp: @berkmancenter now hosts 1,010 active blogs under our #wordpress #multisite install at http://t.co/NLzbUn6W</description>
|
76
|
+
<pubDate>Fri, 03 Feb 2012 14:56:40 +0000</pubDate>
|
77
|
+
<guid>http://twitter.com/djcp/statuses/165448625588146176</guid>
|
78
|
+
<link>http://twitter.com/djcp/statuses/165448625588146176</link>
|
79
|
+
<twitter:source>web</twitter:source>
|
80
|
+
<twitter:place/>
|
81
|
+
</item>
|
82
|
+
<item>
|
83
|
+
<title>djcp: @mirell not bragging, it's just great software. Totally worth the investment.</title>
|
84
|
+
<description>djcp: @mirell not bragging, it's just great software. Totally worth the investment.</description>
|
85
|
+
<pubDate>Thu, 02 Feb 2012 00:41:37 +0000</pubDate>
|
86
|
+
<guid>http://twitter.com/djcp/statuses/164871056916615172</guid>
|
87
|
+
<link>http://twitter.com/djcp/statuses/164871056916615172</link>
|
88
|
+
<twitter:source><a href="http://seesmic.com/" rel="nofollow">Seesmic</a></twitter:source>
|
89
|
+
<twitter:place/>
|
90
|
+
</item>
|
91
|
+
<item>
|
92
|
+
<title>djcp: @mirell I use agent forwarding, multiplexing, chaining and proxying every day. #ssh / fuse gives you easy remote file mounts too.</title>
|
93
|
+
<description>djcp: @mirell I use agent forwarding, multiplexing, chaining and proxying every day. #ssh / fuse gives you easy remote file mounts too.</description>
|
94
|
+
<pubDate>Thu, 02 Feb 2012 00:40:48 +0000</pubDate>
|
95
|
+
<guid>http://twitter.com/djcp/statuses/164870850011602944</guid>
|
96
|
+
<link>http://twitter.com/djcp/statuses/164870850011602944</link>
|
97
|
+
<twitter:source><a href="http://seesmic.com/" rel="nofollow">Seesmic</a></twitter:source>
|
98
|
+
<twitter:place/>
|
99
|
+
</item>
|
100
|
+
<item>
|
101
|
+
<title>djcp: RT @ApocalypseHow: You have to admit, "Lunchables" is a better name than "Yes, Technically You Could Eat These."</title>
|
102
|
+
<description>djcp: RT @ApocalypseHow: You have to admit, "Lunchables" is a better name than "Yes, Technically You Could Eat These."</description>
|
103
|
+
<pubDate>Wed, 01 Feb 2012 23:24:26 +0000</pubDate>
|
104
|
+
<guid>http://twitter.com/djcp/statuses/164851633497448448</guid>
|
105
|
+
<link>http://twitter.com/djcp/statuses/164851633497448448</link>
|
106
|
+
<twitter:source><a href="http://seesmic.com/" rel="nofollow">Seesmic</a></twitter:source>
|
107
|
+
<twitter:place/>
|
108
|
+
</item>
|
109
|
+
<item>
|
110
|
+
<title>djcp: RT @TwopTwips: TEACH your child about the importance of punctuation by making them some "No, More Tears" shampoo out of lemon juice and ...</title>
|
111
|
+
<description>djcp: RT @TwopTwips: TEACH your child about the importance of punctuation by making them some "No, More Tears" shampoo out of lemon juice and ...</description>
|
112
|
+
<pubDate>Mon, 30 Jan 2012 13:18:11 +0000</pubDate>
|
113
|
+
<guid>http://twitter.com/djcp/statuses/163974286573441024</guid>
|
114
|
+
<link>http://twitter.com/djcp/statuses/163974286573441024</link>
|
115
|
+
<twitter:source><a href="http://seesmic.com/" rel="nofollow">Seesmic</a></twitter:source>
|
116
|
+
<twitter:place/>
|
117
|
+
</item>
|
118
|
+
<item>
|
119
|
+
<title>djcp: More than a little psyched for the @Raspberry_Pi . Can think of a bunch of uses, replacing http://t.co/heC2AiVN for one.</title>
|
120
|
+
<description>djcp: More than a little psyched for the @Raspberry_Pi . Can think of a bunch of uses, replacing http://t.co/heC2AiVN for one.</description>
|
121
|
+
<pubDate>Mon, 30 Jan 2012 13:06:57 +0000</pubDate>
|
122
|
+
<guid>http://twitter.com/djcp/statuses/163971463404195842</guid>
|
123
|
+
<link>http://twitter.com/djcp/statuses/163971463404195842</link>
|
124
|
+
<twitter:source><a href="http://seesmic.com/" rel="nofollow">Seesmic</a></twitter:source>
|
125
|
+
<twitter:place/>
|
126
|
+
</item>
|
127
|
+
<item>
|
128
|
+
<title>djcp: RT @pmphlt: Don't the Megaupload indictments show that SOPA isn't needed to achieve what SOPA was supposedly needed to achieve?</title>
|
129
|
+
<description>djcp: RT @pmphlt: Don't the Megaupload indictments show that SOPA isn't needed to achieve what SOPA was supposedly needed to achieve?</description>
|
130
|
+
<pubDate>Fri, 27 Jan 2012 23:16:13 +0000</pubDate>
|
131
|
+
<guid>http://twitter.com/djcp/statuses/163037622833393665</guid>
|
132
|
+
<link>http://twitter.com/djcp/statuses/163037622833393665</link>
|
133
|
+
<twitter:source><a href="http://seesmic.com/" rel="nofollow">Seesmic</a></twitter:source>
|
134
|
+
<twitter:place/>
|
135
|
+
</item>
|
136
|
+
<item>
|
137
|
+
<title>djcp: RT @shortcomment: Newt Gingrich believes marriage should be between one man and one woman. And one more woman. #GOP #p2 #tcot</title>
|
138
|
+
<description>djcp: RT @shortcomment: Newt Gingrich believes marriage should be between one man and one woman. And one more woman. #GOP #p2 #tcot</description>
|
139
|
+
<pubDate>Fri, 20 Jan 2012 22:52:12 +0000</pubDate>
|
140
|
+
<guid>http://twitter.com/djcp/statuses/160494866743300097</guid>
|
141
|
+
<link>http://twitter.com/djcp/statuses/160494866743300097</link>
|
142
|
+
<twitter:source><a href="http://seesmic.com/" rel="nofollow">Seesmic</a></twitter:source>
|
143
|
+
<twitter:place/>
|
144
|
+
</item>
|
145
|
+
<item>
|
146
|
+
<title>djcp: RT @darrenelmore: Go ahead America, picture Newt strolling through a 1970s key party, naked with an open kimono and a snifter of brandy. ...</title>
|
147
|
+
<description>djcp: RT @darrenelmore: Go ahead America, picture Newt strolling through a 1970s key party, naked with an open kimono and a snifter of brandy. ...</description>
|
148
|
+
<pubDate>Fri, 20 Jan 2012 14:17:15 +0000</pubDate>
|
149
|
+
<guid>http://twitter.com/djcp/statuses/160365272979603456</guid>
|
150
|
+
<link>http://twitter.com/djcp/statuses/160365272979603456</link>
|
151
|
+
<twitter:source><a href="http://seesmic.com/" rel="nofollow">Seesmic</a></twitter:source>
|
152
|
+
<twitter:place/>
|
153
|
+
</item>
|
154
|
+
<item>
|
155
|
+
<title>djcp: RT @fightfortheftr: The Oatmeal's goes on #SOPAstrike with a hilarious take on #SOPA and #PIPA http://t.co/7hvUlyfd</title>
|
156
|
+
<description>djcp: RT @fightfortheftr: The Oatmeal's goes on #SOPAstrike with a hilarious take on #SOPA and #PIPA http://t.co/7hvUlyfd</description>
|
157
|
+
<pubDate>Wed, 18 Jan 2012 15:02:07 +0000</pubDate>
|
158
|
+
<guid>http://twitter.com/djcp/statuses/159651788226691072</guid>
|
159
|
+
<link>http://twitter.com/djcp/statuses/159651788226691072</link>
|
160
|
+
<twitter:source>web</twitter:source>
|
161
|
+
<twitter:place/>
|
162
|
+
</item>
|
163
|
+
<item>
|
164
|
+
<title>djcp: @rebekahredux @morninj Thanks, y'all! It's a mashup of http://t.co/kmgQcOmw and http://t.co/ScGmVFxm</title>
|
165
|
+
<description>djcp: @rebekahredux @morninj Thanks, y'all! It's a mashup of http://t.co/kmgQcOmw and http://t.co/ScGmVFxm</description>
|
166
|
+
<pubDate>Wed, 18 Jan 2012 14:58:02 +0000</pubDate>
|
167
|
+
<guid>http://twitter.com/djcp/statuses/159650762622910464</guid>
|
168
|
+
<link>http://twitter.com/djcp/statuses/159650762622910464</link>
|
169
|
+
<twitter:source>web</twitter:source>
|
170
|
+
<twitter:place/>
|
171
|
+
</item>
|
172
|
+
<item>
|
173
|
+
<title>djcp: RT @michaelianblack: Am I the only one who's worried that once Wikipedia goes black it'll never go back?</title>
|
174
|
+
<description>djcp: RT @michaelianblack: Am I the only one who's worried that once Wikipedia goes black it'll never go back?</description>
|
175
|
+
<pubDate>Wed, 18 Jan 2012 04:48:51 +0000</pubDate>
|
176
|
+
<guid>http://twitter.com/djcp/statuses/159497458110578688</guid>
|
177
|
+
<link>http://twitter.com/djcp/statuses/159497458110578688</link>
|
178
|
+
<twitter:source><a href="http://seesmic.com/" rel="nofollow">Seesmic</a></twitter:source>
|
179
|
+
<twitter:place/>
|
180
|
+
</item>
|
181
|
+
<item>
|
182
|
+
<title>djcp: Take action against #SOPA and #PIPA - Support the #Wikipediablackout on Jan 18 http://t.co/lGWJyhnS</title>
|
183
|
+
<description>djcp: Take action against #SOPA and #PIPA - Support the #Wikipediablackout on Jan 18 http://t.co/lGWJyhnS</description>
|
184
|
+
<pubDate>Tue, 17 Jan 2012 14:32:34 +0000</pubDate>
|
185
|
+
<guid>http://twitter.com/djcp/statuses/159281963608453120</guid>
|
186
|
+
<link>http://twitter.com/djcp/statuses/159281963608453120</link>
|
187
|
+
<twitter:source><a href="http://twitter.com/tweetbutton" rel="nofollow">Tweet Button</a></twitter:source>
|
188
|
+
<twitter:place/>
|
189
|
+
</item>
|
190
|
+
</channel>
|
191
|
+
</rss>
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: feed-abstract
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-02-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: feed-abstract
|
16
|
-
requirement: &
|
16
|
+
requirement: &26584200 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *26584200
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &26583020 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *26583020
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &26582460 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
@@ -43,7 +43,73 @@ dependencies:
|
|
43
43
|
version: '0'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *26582460
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: rspec
|
49
|
+
requirement: &26581020 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *26581020
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: rspec
|
60
|
+
requirement: &26580180 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *26580180
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: &26579000 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *26579000
|
80
|
+
- !ruby/object:Gem::Dependency
|
81
|
+
name: simplecov
|
82
|
+
requirement: &29053020 !ruby/object:Gem::Requirement
|
83
|
+
none: false
|
84
|
+
requirements:
|
85
|
+
- - ! '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: *29053020
|
91
|
+
- !ruby/object:Gem::Dependency
|
92
|
+
name: rspec
|
93
|
+
requirement: &29052500 !ruby/object:Gem::Requirement
|
94
|
+
none: false
|
95
|
+
requirements:
|
96
|
+
- - ! '>='
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: '0'
|
99
|
+
type: :development
|
100
|
+
prerelease: false
|
101
|
+
version_requirements: *29052500
|
102
|
+
- !ruby/object:Gem::Dependency
|
103
|
+
name: simplecov
|
104
|
+
requirement: &29052020 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: *29052020
|
47
113
|
description: ! 'This library creates a common object graph for the RSS/Atom/RDF parsing
|
48
114
|
classes in the ruby standard library. This allows you parse different feed formats
|
49
115
|
and get back the same (or at least a very similar) set of results - item authors
|
@@ -85,6 +151,7 @@ files:
|
|
85
151
|
- spec/test_data/djcp.rss92
|
86
152
|
- spec/test_data/djcp_code.rss
|
87
153
|
- spec/test_data/djcp_delicious.rss
|
154
|
+
- spec/test_data/djcp_twitter.rss
|
88
155
|
- spec/test_data/doc.atom
|
89
156
|
- spec/test_data/feedburner.rss
|
90
157
|
- spec/test_data/katanapg.atom
|
@@ -112,9 +179,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
179
|
version: '0'
|
113
180
|
requirements: []
|
114
181
|
rubyforge_project:
|
115
|
-
rubygems_version: 1.8.
|
182
|
+
rubygems_version: 1.8.10
|
116
183
|
signing_key:
|
117
184
|
specification_version: 3
|
118
|
-
summary: Abstracts RSS/Atom/RDF parsing
|
119
|
-
common object graph.
|
185
|
+
summary: Abstracts RSS/Atom/RDF stdlib parsing into a common object graph.
|
120
186
|
test_files: []
|