bridgeutopia-simple-rss 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,212 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/gempackagetask'
6
+ require 'rake/contrib/rubyforgepublisher'
7
+ require File.dirname(__FILE__) + '/lib/simple-rss'
8
+
9
+ PKG_VERSION = SimpleRSS::VERSION
10
+ PKG_NAME = "simple-rss"
11
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
12
+ RUBY_FORGE_PROJECT = "simple-rss"
13
+ RUBY_FORGE_USER = ENV['RUBY_FORGE_USER'] || "cardmagic"
14
+ RELEASE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
15
+
16
+ PKG_FILES = FileList[
17
+ "lib/*", "bin/*", "test/**/*", "[A-Z]*", "Rakefile", "html/**/*"
18
+ ]
19
+
20
+ desc "Default Task"
21
+ task :default => [ :test ]
22
+
23
+ # Run the unit tests
24
+ desc "Run all unit tests"
25
+ Rake::TestTask.new("test") { |t|
26
+ t.libs << "lib"
27
+ t.pattern = 'test/*/*_test.rb'
28
+ t.verbose = true
29
+ }
30
+
31
+ # Make a console, useful when working on tests
32
+ desc "Generate a test console"
33
+ task :console do
34
+ verbose( false ) { sh "irb -I lib/ -r 'simple-rss'" }
35
+ end
36
+
37
+ # Genereate the RDoc documentation
38
+ desc "Create documentation"
39
+ Rake::RDocTask.new("doc") { |rdoc|
40
+ rdoc.title = "Simple RSS - A Flexible RSS and Atom reader for Ruby"
41
+ rdoc.rdoc_dir = 'html'
42
+ rdoc.rdoc_files.include('README')
43
+ rdoc.rdoc_files.include('lib/*.rb')
44
+ }
45
+
46
+ # Genereate the package
47
+ spec = Gem::Specification.new do |s|
48
+
49
+ #### Basic information.
50
+
51
+ s.name = 'simple-rss'
52
+ s.version = PKG_VERSION
53
+ s.summary = <<-EOF
54
+ 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.
55
+ EOF
56
+ s.description = <<-EOF
57
+ 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.
58
+ EOF
59
+
60
+ #### Which files are to be included in this gem? Everything! (Except CVS directories.)
61
+
62
+ s.files = PKG_FILES
63
+
64
+ #### Load-time details: library and application (you will need one or both).
65
+
66
+ s.require_path = 'lib'
67
+
68
+ #### Documentation and testing.
69
+
70
+ s.has_rdoc = true
71
+
72
+ #### Author and project details.
73
+
74
+ s.author = "Lucas Carlson"
75
+ s.email = "lucas@rufy.com"
76
+ s.homepage = "http://simple-rss.rubyforge.org/"
77
+ end
78
+
79
+ Rake::GemPackageTask.new(spec) do |pkg|
80
+ pkg.need_zip = true
81
+ pkg.need_tar = true
82
+ end
83
+
84
+ desc "Report code statistics (KLOCs, etc) from the application"
85
+ task :stats do
86
+ require 'code_statistics'
87
+ CodeStatistics.new(
88
+ ["Library", "lib"],
89
+ ["Units", "test"]
90
+ ).to_s
91
+ end
92
+
93
+ desc "Publish new documentation"
94
+ task :publish do
95
+ Rake::RubyForgePublisher.new('simple-rss', 'cardmagic').upload
96
+ end
97
+
98
+
99
+ desc "Publish the release files to RubyForge."
100
+ task :upload => [:package] do
101
+ files = ["gem", "tar.gz", "zip"].map { |ext| "pkg/#{PKG_FILE_NAME}.#{ext}" }
102
+
103
+ if RUBY_FORGE_PROJECT then
104
+ require 'net/http'
105
+ require 'open-uri'
106
+
107
+ project_uri = "http://rubyforge.org/projects/#{RUBY_FORGE_PROJECT}/"
108
+ project_data = open(project_uri) { |data| data.read }
109
+ group_id = project_data[/[?&]group_id=(\d+)/, 1]
110
+ raise "Couldn't get group id" unless group_id
111
+
112
+ # This echos password to shell which is a bit sucky
113
+ if ENV["RUBY_FORGE_PASSWORD"]
114
+ password = ENV["RUBY_FORGE_PASSWORD"]
115
+ else
116
+ print "#{RUBY_FORGE_USER}@rubyforge.org's password: "
117
+ password = STDIN.gets.chomp
118
+ end
119
+
120
+ login_response = Net::HTTP.start("rubyforge.org", 80) do |http|
121
+ data = [
122
+ "login=1",
123
+ "form_loginname=#{RUBY_FORGE_USER}",
124
+ "form_pw=#{password}"
125
+ ].join("&")
126
+ http.post("/account/login.php", data)
127
+ end
128
+
129
+ cookie = login_response["set-cookie"]
130
+ raise "Login failed" unless cookie
131
+ headers = { "Cookie" => cookie }
132
+
133
+ release_uri = "http://rubyforge.org/frs/admin/?group_id=#{group_id}"
134
+ release_data = open(release_uri, headers) { |data| data.read }
135
+ package_id = release_data[/[?&]package_id=(\d+)/, 1]
136
+ raise "Couldn't get package id" unless package_id
137
+
138
+ first_file = true
139
+ release_id = ""
140
+
141
+ files.each do |filename|
142
+ basename = File.basename(filename)
143
+ file_ext = File.extname(filename)
144
+ file_data = File.open(filename, "rb") { |file| file.read }
145
+
146
+ puts "Releasing #{basename}..."
147
+
148
+ release_response = Net::HTTP.start("rubyforge.org", 80) do |http|
149
+ release_date = Time.now.strftime("%Y-%m-%d %H:%M")
150
+ type_map = {
151
+ ".zip" => "3000",
152
+ ".tgz" => "3110",
153
+ ".gz" => "3110",
154
+ ".gem" => "1400"
155
+ }; type_map.default = "9999"
156
+ type = type_map[file_ext]
157
+ boundary = "rubyqMY6QN9bp6e4kS21H4y0zxcvoor"
158
+
159
+ query_hash = if first_file then
160
+ {
161
+ "group_id" => group_id,
162
+ "package_id" => package_id,
163
+ "release_name" => RELEASE_NAME,
164
+ "release_date" => release_date,
165
+ "type_id" => type,
166
+ "processor_id" => "8000", # Any
167
+ "release_notes" => "",
168
+ "release_changes" => "",
169
+ "preformatted" => "1",
170
+ "submit" => "1"
171
+ }
172
+ else
173
+ {
174
+ "group_id" => group_id,
175
+ "release_id" => release_id,
176
+ "package_id" => package_id,
177
+ "step2" => "1",
178
+ "type_id" => type,
179
+ "processor_id" => "8000", # Any
180
+ "submit" => "Add This File"
181
+ }
182
+ end
183
+
184
+ query = "?" + query_hash.map do |(name, value)|
185
+ [name, URI.encode(value)].join("=")
186
+ end.join("&")
187
+
188
+ data = [
189
+ "--" + boundary,
190
+ "Content-Disposition: form-data; name=\"userfile\"; filename=\"#{basename}\"",
191
+ "Content-Type: application/octet-stream",
192
+ "Content-Transfer-Encoding: binary",
193
+ "", file_data, ""
194
+ ].join("\x0D\x0A")
195
+
196
+ release_headers = headers.merge(
197
+ "Content-Type" => "multipart/form-data; boundary=#{boundary}"
198
+ )
199
+
200
+ target = first_file ? "/frs/admin/qrs.php" : "/frs/admin/editrelease.php"
201
+ http.post(target + query, data, release_headers)
202
+ end
203
+
204
+ if first_file then
205
+ release_id = release_response.body[/release_id=(\d+)/, 1]
206
+ raise("Couldn't get release id") unless release_id
207
+ end
208
+
209
+ first_file = false
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,40 @@
1
+ require 'rbconfig'
2
+ require 'find'
3
+ require 'ftools'
4
+
5
+ include Config
6
+
7
+ # this was adapted from rdoc's install.rb by ways of Log4r
8
+
9
+ $sitedir = CONFIG["sitelibdir"]
10
+ unless $sitedir
11
+ version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
12
+ $libdir = File.join(CONFIG["libdir"], "ruby", version)
13
+ $sitedir = $:.find {|x| x =~ /site_ruby/ }
14
+ if !$sitedir
15
+ $sitedir = File.join($libdir, "site_ruby")
16
+ elsif $sitedir !~ Regexp.quote(version)
17
+ $sitedir = File.join($sitedir, version)
18
+ end
19
+ end
20
+
21
+ makedirs = %w{ shipping }
22
+ makedirs.each {|f| File::makedirs(File.join($sitedir, *f.split(/\//)))}
23
+
24
+ Dir.chdir("lib")
25
+ begin
26
+ require 'rubygems'
27
+ require 'rake'
28
+ rescue LoadError
29
+ puts
30
+ puts "Please install Gem and Rake from http://rubyforge.org/projects/rubygems and http://rubyforge.org/projects/rake"
31
+ puts
32
+ exit(-1)
33
+ end
34
+
35
+ files = FileList["**/*"]
36
+
37
+ # File::safe_unlink *deprecated.collect{|f| File.join($sitedir, f.split(/\//))}
38
+ files.each {|f|
39
+ File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
40
+ }
@@ -0,0 +1,164 @@
1
+ require 'cgi'
2
+ require 'time'
3
+
4
+ class SimpleRSS
5
+ VERSION = "1.2.3"
6
+
7
+ attr_reader :items, :source
8
+ alias :entries :items
9
+
10
+ @@feed_tags = [
11
+ :id,
12
+ :title, :subtitle, :link,
13
+ :description,
14
+ :author, :webMaster, :managingEditor, :contributor,
15
+ :pubDate, :lastBuildDate, :updated, :'dc:date',
16
+ :generator, :language, :docs, :cloud,
17
+ :ttl, :skipHours, :skipDays,
18
+ :image, :logo, :icon, :rating,
19
+ :rights, :copyright,
20
+ :textInput, :'feedburner:browserFriendly',
21
+ :'itunes:author', :'itunes:category'
22
+ ]
23
+
24
+ @@item_tags = [
25
+ :id,
26
+ :title, :link, :'link+alternate', :'link+self', :'link+edit', :'link+replies',
27
+ :author, :contributor,
28
+ :description, :summary, :content, :'content:encoded', :comments,
29
+ :pubDate, :published, :updated, :expirationDate, :modified, :'dc:date',
30
+ :category, :guid,
31
+ :'trackback:ping', :'trackback:about',
32
+ :'dc:creator', :'dc:title', :'dc:subject', :'dc:rights', :'dc:publisher',
33
+ :'feedburner:origLink',
34
+ :'media:content#url', :'media:content#type', :'media:content#height', :'media:content#width',
35
+ :'media:title', :'media:thumbnail#url', :'media:thumbnail#height', :'media:thumbnail#width',
36
+ :'media:credit', :'media:credit#role',
37
+ :'media:category', :'media:category#scheme'
38
+ ]
39
+
40
+ def initialize(source, options={})
41
+ @source = source.respond_to?(:read) ? source.read : source.to_s
42
+ @items = Array.new
43
+ @options = Hash.new.update(options)
44
+
45
+ parse
46
+ end
47
+
48
+ def channel() self end
49
+ alias :feed :channel
50
+
51
+ class << self
52
+ def feed_tags
53
+ @@feed_tags
54
+ end
55
+ def feed_tags=(ft)
56
+ @@feed_tags = ft
57
+ end
58
+
59
+ def item_tags
60
+ @@item_tags
61
+ end
62
+ def item_tags=(it)
63
+ @@item_tags = it
64
+ end
65
+
66
+ # The strict attribute is for compatibility with Ruby's standard RSS parser
67
+ def parse(source, options={})
68
+ new source, options
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def parse
75
+ raise SimpleRSSError, "Poorly formatted feed" unless @source =~ %r{<(channel|feed).*?>.*?</(channel|feed)>}mi
76
+
77
+ # Feed's title and link
78
+ feed_content = $1 if @source =~ %r{(.*?)<(rss:|atom:)?(item|entry).*?>.*?</(rss:|atom:)?(item|entry)>}mi
79
+
80
+ @@feed_tags.each do |tag|
81
+ if feed_content && feed_content =~ %r{<(rss:|atom:)?#{tag}(.*?)>(.*?)</(rss:|atom:)?#{tag}>}mi
82
+ nil
83
+ elsif feed_content && feed_content =~ %r{<(rss:|atom:)?#{tag}(.*?)\/\s*>}mi
84
+ nil
85
+ elsif @source =~ %r{<(rss:|atom:)?#{tag}(.*?)>(.*?)</(rss:|atom:)?#{tag}>}mi
86
+ nil
87
+ elsif @source =~ %r{<(rss:|atom:)?#{tag}(.*?)\/\s*>}mi
88
+ nil
89
+ end
90
+
91
+ if $2 || $3
92
+ tag_cleaned = clean_tag(tag)
93
+ instance_variable_set("@#{ tag_cleaned }", clean_content(tag, $2, $3))
94
+ self.class.send(:attr_reader, tag_cleaned)
95
+ end
96
+ end
97
+
98
+ # RSS items' title, link, and description
99
+ @source.scan( %r{<(rss:|atom:)?(item|entry)([\s][^>]*)?>(.*?)</(rss:|atom:)?(item|entry)>}mi ) do |match|
100
+ item = Hash.new
101
+ @@item_tags.each do |tag|
102
+ if tag.to_s.include?("+")
103
+ tag_data = tag.to_s.split("+")
104
+ tag = tag_data[0]
105
+ rel = tag_data[1]
106
+
107
+ if match[3] =~ %r{<(rss:|atom:)?#{tag}(.*?)rel=['"]#{rel}['"](.*?)>(.*?)</(rss:|atom:)?#{tag}>}mi
108
+ nil
109
+ elsif match[3] =~ %r{<(rss:|atom:)?#{tag}(.*?)rel=['"]#{rel}['"](.*?)/\s*>}mi
110
+ nil
111
+ end
112
+ item[clean_tag("#{tag}+#{rel}")] = clean_content(tag, $3, $4) if $3 || $4
113
+ elsif tag.to_s.include?("#")
114
+ tag_data = tag.to_s.split("#")
115
+ tag = tag_data[0]
116
+ attrib = tag_data[1]
117
+ if match[3] =~ %r{<(rss:|atom:)?#{tag}(.*?)#{attrib}=['"](.*?)['"](.*?)>(.*?)</(rss:|atom:)?#{tag}>}mi
118
+ nil
119
+ elsif match[3] =~ %r{<(rss:|atom:)?#{tag}(.*?)#{attrib}=['"](.*?)['"](.*?)/\s*>}mi
120
+ nil
121
+ end
122
+ item[clean_tag("#{tag}_#{attrib}")] = clean_content(tag, attrib, $3) if $3
123
+ else
124
+ if match[3] =~ %r{<(rss:|atom:)?#{tag}(.*?)>(.*?)</(rss:|atom:)?#{tag}>}mi
125
+ nil
126
+ elsif match[3] =~ %r{<(rss:|atom:)?#{tag}(.*?)/\s*>}mi
127
+ nil
128
+ end
129
+ item[clean_tag(tag)] = clean_content(tag, $2, $3) if $2 || $3
130
+ end
131
+ end
132
+ def item.method_missing(name, *args) self[name] end
133
+ @items << item
134
+ end
135
+
136
+ end
137
+
138
+ def clean_content(tag, attrs, content)
139
+ content = content.to_s
140
+ case tag
141
+ when :pubDate, :lastBuildDate, :published, :updated, :expirationDate, :modified, :'dc:date'
142
+ Time.parse(content) rescue unescape(content)
143
+ when :author, :contributor, :skipHours, :skipDays
144
+ unescape(content.gsub(/<.*?>/,''))
145
+ else
146
+ content.empty? && "#{attrs} " =~ /href=['"]?([^'"]*)['" ]/mi ? $1.strip : unescape(content)
147
+ end
148
+ end
149
+
150
+ def clean_tag(tag)
151
+ tag.to_s.gsub(':','_').intern
152
+ end
153
+
154
+ def unescape(content)
155
+ if content =~ /([^-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]%)/u then
156
+ CGI.unescape(content).gsub(/(<!\[CDATA\[|\]\]>)/u,'').strip
157
+ else
158
+ content.gsub(/(<!\[CDATA\[|\]\]>)/u,'').strip
159
+ end
160
+ end
161
+ end
162
+
163
+ class SimpleRSSError < StandardError
164
+ end
@@ -0,0 +1,12 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "bridgeutopia-simple-rss"
3
+ s.version = "1.2.4"
4
+ s.date = "2010-07-06"
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."
6
+ s.email = "lucas@rufy.com"
7
+ s.homepage = "http://github.com/cardmagic/simple-rss"
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."
9
+ s.has_rdoc = true
10
+ s.authors = ["Lucas Carlson", "Katherine Pe"]
11
+ s.files = ["install.rb", "lib", "lib/simple-rss.rb", "LICENSE", "Rakefile", "README", "simple-rss.gemspec", "test", "test/base", "test/base/base_test.rb", "test/data", "test/data/atom.xml", "test/data/not-rss.xml", "test/data/rss09.rdf", "test/data/rss20.xml", "test/test_helper.rb"]
12
+ end
@@ -0,0 +1,72 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ class BaseTest < Test::Unit::TestCase
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
+ @media_rss = SimpleRSS.parse open(File.dirname(__FILE__) + '/../data/media_rss.xml')
7
+ @atom = SimpleRSS.parse open(File.dirname(__FILE__) + '/../data/atom.xml')
8
+ end
9
+
10
+ def test_channel
11
+ assert_equal @rss09, @rss09.channel
12
+ assert_equal @rss20, @rss20.channel
13
+ assert_equal @atom, @atom.feed
14
+ end
15
+
16
+ def test_items
17
+ assert_kind_of Array, @rss09.items
18
+ assert_kind_of Array, @rss20.items
19
+ assert_kind_of Array, @atom.entries
20
+ end
21
+
22
+ def test_rss09
23
+ assert_equal 10, @rss09.items.size
24
+ assert_equal "Slashdot", @rss09.title
25
+ assert_equal "http://slashdot.org/", @rss09.channel.link
26
+ assert_equal "http://books.slashdot.org/article.pl?sid=05/08/29/1319236&amp;from=rss", @rss09.items.first.link
27
+ assert_equal "http://books.slashdot.org/article.pl?sid=05/08/29/1319236&amp;from=rss", @rss09.items.first[:link]
28
+ assert_equal Time.parse("Wed Aug 24 13:33:34 UTC 2005"), @rss20.items.first.pubDate
29
+ assert_equal Time.parse("Fri Sep 09 02:52:31 PDT 2005"), @rss09.channel.dc_date
30
+ end
31
+
32
+ def test_media_rss
33
+ assert_equal 20, @media_rss.items.size
34
+ assert_equal "Uploads from herval", @media_rss.title
35
+ assert_equal "http://www.flickr.com/photos/herval/", @media_rss.channel.link
36
+ assert_equal "http://www.flickr.com/photos/herval/4671960608/", @media_rss.items.first.link
37
+ assert_equal "http://www.flickr.com/photos/herval/4671960608/", @media_rss.items.first[:link]
38
+ assert_equal "http://farm5.static.flickr.com/4040/4671960608_10cb945d5c_o.jpg", @media_rss.items.first.media_content_url
39
+ assert_equal "image/jpeg", @media_rss.items.first.media_content_type
40
+ assert_equal "3168", @media_rss.items.first.media_content_height
41
+ assert_equal "4752", @media_rss.items.first.media_content_width
42
+ assert_equal "Woof?", @media_rss.items.first.media_title
43
+ assert_equal "http://farm5.static.flickr.com/4040/4671960608_954d2297bc_s.jpg", @media_rss.items.first.media_thumbnail_url
44
+ assert_equal "75", @media_rss.items.first.media_thumbnail_height
45
+ assert_equal "75", @media_rss.items.first.media_thumbnail_width
46
+ assert_equal "herval", @media_rss.items.first.media_credit
47
+ assert_equal "photographer", @media_rss.items.first.media_credit_role
48
+ assert_equal "pets frodo", @media_rss.items.first.media_category
49
+ assert_equal "urn:flickr:tags", @media_rss.items.first.media_category_scheme
50
+ end
51
+
52
+ def test_rss20
53
+ assert_equal 10, @rss20.items.size
54
+ assert_equal "Technoblog", @rss20.title
55
+ assert_equal "http://tech.rufy.com", @rss20.channel.link
56
+ assert_equal "http://feeds.feedburner.com/rufytech?m=68", @rss20.items.first.link
57
+ assert_equal "http://feeds.feedburner.com/rufytech?m=68", @rss20.items.first[:link]
58
+ 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
59
+ end
60
+
61
+ def test_atom
62
+ assert_equal 1, @atom.entries.size
63
+ assert_equal "dive into mark", @atom.title
64
+ assert_equal "http://example.org/", @atom.feed.link
65
+ assert_equal "http://example.org/2005/04/02/atom", @atom.entries.first.link
66
+ assert_equal "http://example.org/2005/04/02/atom", @atom.entries.first[:link]
67
+ end
68
+
69
+ def test_bad_feed
70
+ assert_raise(SimpleRSSError) { SimpleRSS.parse(open(File.dirname(__FILE__) + '/../data/not-rss.xml')) }
71
+ end
72
+ end