bridgeutopia-simple-rss 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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