rss-client 2.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +21 -0
- data/README +79 -0
- data/Rakefile +83 -0
- data/bin/rssclient +206 -0
- data/install.rb +84 -0
- data/lib/rss-client.rb +109 -0
- data/lib/rss-client/http-access2.rb +2023 -0
- data/lib/rss-client/http-access2/cookie.rb +541 -0
- data/lib/rss-client/http-access2/http.rb +602 -0
- data/rake_tasks/annotations.rake +84 -0
- data/spec/helper.rb +10 -0
- data/spec/spec_http_access2.rb +13 -0
- data/spec/spec_rss_client.rb +22 -0
- metadata +88 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2007, 2008 Stoyan Zhekov
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
= RSSClient: Fetching and parsing RSS feeds with easy
|
2
|
+
|
3
|
+
== Download
|
4
|
+
|
5
|
+
The latest version of rake can be found at
|
6
|
+
|
7
|
+
* http://rubyforge.org/project/...
|
8
|
+
|
9
|
+
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
=== Normal Installation
|
13
|
+
|
14
|
+
You can install rake with the following command.
|
15
|
+
|
16
|
+
% ruby install.rb
|
17
|
+
|
18
|
+
from its distribution directory.
|
19
|
+
|
20
|
+
=== GEM Installation
|
21
|
+
|
22
|
+
Download and install rss-client with the following.
|
23
|
+
|
24
|
+
gem install --remote rss-client
|
25
|
+
|
26
|
+
|
27
|
+
== Synopsis
|
28
|
+
|
29
|
+
Fetching RSS feeds.
|
30
|
+
|
31
|
+
|
32
|
+
== Examples
|
33
|
+
|
34
|
+
rssclient -q http://example.com/atom.xml
|
35
|
+
rssclient -r -f http://test:test@example.com/atom.xml
|
36
|
+
rssclient --verbose http://example.com/atom.xml
|
37
|
+
rssclient --giveup 20 -f http://example.com/atom.xml
|
38
|
+
rssclient --since 600 http://example.com/atom.xml
|
39
|
+
rssclient -f -p http://aa:bb@localhost:8088 http://example.com/atom.xml
|
40
|
+
|
41
|
+
|
42
|
+
== Usage
|
43
|
+
|
44
|
+
rssclient [options] feed_url
|
45
|
+
|
46
|
+
=== Options
|
47
|
+
|
48
|
+
-h, --help Displays help message
|
49
|
+
-V, --version Display the version, then exit
|
50
|
+
-q, --quiet Output as little as possible, overrides verbose
|
51
|
+
-v, --verbose Verbose output
|
52
|
+
-p, --proxy Use proxy (format http://user:pass@address:port )
|
53
|
+
-r, --raw Use raw RSS fetch (no error code processing)
|
54
|
+
-g, --giveup Giveup on fetching after timeout (seconds)
|
55
|
+
-f, --force Force fresh feed fetch (no 304 code processing)
|
56
|
+
-s, --since Only changes from {since} seconds ago
|
57
|
+
|
58
|
+
|
59
|
+
== Credits
|
60
|
+
|
61
|
+
[<b>Hiroshi Nakamura</b>] For the http-access2 library.
|
62
|
+
|
63
|
+
[<b>Todd Werth</b>] For the ruby command-line application skeleton
|
64
|
+
|
65
|
+
|
66
|
+
== License
|
67
|
+
|
68
|
+
rss-client is available under an MIT-style license.
|
69
|
+
|
70
|
+
:include: MIT-LICENSE
|
71
|
+
|
72
|
+
|
73
|
+
== Other stuff
|
74
|
+
|
75
|
+
Author:: Stoyan Zhekov <stoyan@gmail.com>
|
76
|
+
Requires:: Ruby 1.8.0 or later
|
77
|
+
License:: Copyright (c) 2007, 2008 Stoyan Zhekov
|
78
|
+
Released under an MIT-style license. See the MIT-LICENSE file
|
79
|
+
included in the distribution.
|
data/Rakefile
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
Gem::manage_gems
|
3
|
+
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/clean'
|
6
|
+
require 'rake/packagetask'
|
7
|
+
require 'rake/gempackagetask'
|
8
|
+
require 'rake/rdoctask'
|
9
|
+
require 'fileutils'
|
10
|
+
include FileUtils
|
11
|
+
|
12
|
+
$:.unshift File.join(File.dirname(__FILE__), "lib")
|
13
|
+
|
14
|
+
NAME = "rss-client"
|
15
|
+
|
16
|
+
RDOC_FILES = %w[
|
17
|
+
lib
|
18
|
+
README
|
19
|
+
MIT-LICENSE
|
20
|
+
]
|
21
|
+
|
22
|
+
RDOC_OPTS = %w[
|
23
|
+
--line-numbers
|
24
|
+
--inline-source
|
25
|
+
--all
|
26
|
+
--quiet
|
27
|
+
--title RSSClient:\ Fetching\ and\ parsing\ RSS\ feeds\ with\ easy
|
28
|
+
--main README
|
29
|
+
--exclude "^(_darcs|.hg|.svn|spec|bin|pkg)/"
|
30
|
+
]
|
31
|
+
|
32
|
+
load 'rake_tasks/annotations.rake'
|
33
|
+
|
34
|
+
spec = Gem::Specification.new do |s|
|
35
|
+
s.name = NAME
|
36
|
+
s.version = "2.0.9"
|
37
|
+
s.author = "Stoyan Zhekov"
|
38
|
+
s.email = "stoyan@gmail.com"
|
39
|
+
s.rubyforge_project = NAME
|
40
|
+
s.homepage = "http://www.assembla.com/wiki/show/#{NAME}"
|
41
|
+
s.platform = Gem::Platform::RUBY
|
42
|
+
s.summary = "Fetching and parsing RSS feeds with easy"
|
43
|
+
s.files = (RDOC_FILES + %w[install.rb] + %w[Rakefile] + Dir["{bin,lib,rake_tasks,spec}/**/*"]).uniq
|
44
|
+
s.test_files = Dir['spec/spec_*.rb']
|
45
|
+
s.require_path = "lib"
|
46
|
+
s.bindir = "bin"
|
47
|
+
s.executables = ['rssclient']
|
48
|
+
s.default_executable = 'rssclient'
|
49
|
+
s.has_rdoc = true
|
50
|
+
s.extra_rdoc_files = RDOC_FILES
|
51
|
+
s.rdoc_options = RDOC_OPTS
|
52
|
+
s.add_dependency("feed-normalizer", ">= 1.4.0")
|
53
|
+
end
|
54
|
+
|
55
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
56
|
+
pkg.need_tar = true
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "package and the gem"
|
60
|
+
task :install do
|
61
|
+
name = "#{spec.name}-#{spec.version}.gem"
|
62
|
+
sh %{rake package}
|
63
|
+
sh %{sudo gem install pkg/#{spec.name}-#{spec.version}.gem}
|
64
|
+
end
|
65
|
+
|
66
|
+
desc "uninstall the gem"
|
67
|
+
task :uninstall => [:clean] do
|
68
|
+
sh %{sudo gem uninstall #{spec.name}}
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "generate rdoc"
|
72
|
+
task :rdoc => [:clean] do
|
73
|
+
sh "rdoc #{(RDOC_OPTS + RDOC_FILES).join(' ')}"
|
74
|
+
end
|
75
|
+
|
76
|
+
desc "Run all the tests"
|
77
|
+
task :test do
|
78
|
+
sh "specrb -Ilib:spec -w #{ENV['TEST'] || '-a'} #{ENV['TESTOPTS']}"
|
79
|
+
end
|
80
|
+
|
81
|
+
task :default => "pkg/#{spec.name}-#{spec.version}.gem" do
|
82
|
+
puts "generated latest version"
|
83
|
+
end
|
data/bin/rssclient
ADDED
@@ -0,0 +1,206 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# == Synopsis
|
3
|
+
# Fetching RSS feeds.
|
4
|
+
#
|
5
|
+
# == Examples
|
6
|
+
#
|
7
|
+
# rssclient -q http://example.com/atom.xml
|
8
|
+
# rssclient -r -f http://test:test@example.com/atom.xml
|
9
|
+
# rssclient --verbose http://example.com/atom.xml
|
10
|
+
# rssclient --giveup 20 -f http://example.com/atom.xml
|
11
|
+
# rssclient --since 600 http://example.com/atom.xml
|
12
|
+
# rssclient -f -p http://aa:bb@localhost:8088 http://example.com/atom.xml
|
13
|
+
#
|
14
|
+
# == Usage
|
15
|
+
# rssclient [options] feed_url
|
16
|
+
#
|
17
|
+
# == Options
|
18
|
+
# -h, --help Displays help message
|
19
|
+
# -V, --version Display the version, then exit
|
20
|
+
# -q, --quiet Output as little as possible, overrides verbose
|
21
|
+
# -v, --verbose Verbose output
|
22
|
+
# -p, --proxy Use proxy (format http://user:pass@address:port )
|
23
|
+
# -r, --raw Use raw RSS fetch (no error code processing)
|
24
|
+
# -g, --giveup Giveup on fetching after timeout (seconds)
|
25
|
+
# -f, --force Force fresh feed fetch (no 304 code processing)
|
26
|
+
# -s, --since Only changes from {since} seconds ago
|
27
|
+
#
|
28
|
+
# == Author
|
29
|
+
# Stoyan Zhekov <stoyan@gmail.com>
|
30
|
+
#
|
31
|
+
# == Copyright
|
32
|
+
# Copyright (c) 2007 Stoyan Zhekov <stoyan@gmail.com>. Licensed under the MIT License:
|
33
|
+
# http://www.opensource.org/licenses/mit-license.php
|
34
|
+
|
35
|
+
begin
|
36
|
+
require 'rss-client'
|
37
|
+
rescue LoadError
|
38
|
+
require 'rubygems'
|
39
|
+
require 'rss-client'
|
40
|
+
end
|
41
|
+
|
42
|
+
require 'optparse'
|
43
|
+
require 'rdoc/usage'
|
44
|
+
|
45
|
+
class App
|
46
|
+
VERSION = '2.0.9'
|
47
|
+
|
48
|
+
attr_reader :options
|
49
|
+
|
50
|
+
def initialize(arguments, stdin)
|
51
|
+
@arguments = arguments
|
52
|
+
@stdin = stdin
|
53
|
+
|
54
|
+
# Set defaults
|
55
|
+
@options = OpenStruct.new
|
56
|
+
@options.verbose = false
|
57
|
+
@options.quiet = false
|
58
|
+
@options.forceUpdate = false
|
59
|
+
@options.raw = false
|
60
|
+
@options.since = Time.new - (10*60) # only changes from 10 min ago
|
61
|
+
@options.giveup = 10 # giveup after 10 sec timeout
|
62
|
+
@options.proxy = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
# Parse options, check arguments, then process the command
|
66
|
+
def run
|
67
|
+
|
68
|
+
if parsed_options? && arguments_valid?
|
69
|
+
|
70
|
+
puts "Start at #{Time.new.to_s}\n\n" if @options.verbose
|
71
|
+
|
72
|
+
output_options if @options.verbose # [Optional]
|
73
|
+
|
74
|
+
process_arguments
|
75
|
+
process_command
|
76
|
+
|
77
|
+
puts "\nFinished at #{Time.new.to_s}" if @options.verbose
|
78
|
+
|
79
|
+
else
|
80
|
+
output_usage
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def parsed_options?
|
88
|
+
|
89
|
+
# Specify options
|
90
|
+
opts = OptionParser.new
|
91
|
+
opts.on('-V', '--version') { output_version ; exit 0 }
|
92
|
+
opts.on('-h', '--help') { output_help }
|
93
|
+
opts.on('-v', '--verbose') { @options.verbose = true }
|
94
|
+
opts.on('-q', '--quiet') { @options.quiet = true }
|
95
|
+
opts.on('-r', '--raw') { @options.raw = true }
|
96
|
+
opts.on('-f', '--force') { @options.forceUpdate = true }
|
97
|
+
opts.on('-p', '--proxy PROXY') { |v| @options.proxy = v }
|
98
|
+
opts.on('-g', '--giveup SEC') { |v| @options.giveup = v.to_i }
|
99
|
+
opts.on('-s', '--since SEC') { |v| @options.since = Time.new - v.to_i }
|
100
|
+
|
101
|
+
opts.parse!(@arguments) rescue return false
|
102
|
+
|
103
|
+
process_options
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
# Performs post-parse processing on options
|
108
|
+
def process_options
|
109
|
+
@options.verbose = false if @options.quiet
|
110
|
+
@options.forceUpdate = true unless Time.parse(@options.since.to_s)
|
111
|
+
end
|
112
|
+
|
113
|
+
def output_options
|
114
|
+
puts "Options:\n"
|
115
|
+
|
116
|
+
@options.marshal_dump.each do |name, val|
|
117
|
+
puts " #{name} = #{val}"
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# True if required arguments were provided
|
122
|
+
def arguments_valid?
|
123
|
+
# TO DO - implement some real logic here
|
124
|
+
true if @arguments.length == 1
|
125
|
+
end
|
126
|
+
|
127
|
+
# Setup the arguments
|
128
|
+
def process_arguments
|
129
|
+
# TO DO - place in local vars, etc
|
130
|
+
end
|
131
|
+
|
132
|
+
def output_help
|
133
|
+
output_version
|
134
|
+
RDoc::usage() #exits app
|
135
|
+
end
|
136
|
+
|
137
|
+
def output_usage
|
138
|
+
RDoc::usage('usage') # gets usage from comments above
|
139
|
+
end
|
140
|
+
|
141
|
+
def output_version
|
142
|
+
puts "#{File.basename(__FILE__)} version #{VERSION}"
|
143
|
+
end
|
144
|
+
|
145
|
+
def process_command
|
146
|
+
feed = RSSFeed.new
|
147
|
+
if @options.raw
|
148
|
+
rss = feed.fetch_raw(@arguments[0], @options)
|
149
|
+
else
|
150
|
+
rss = feed.fetch(@arguments[0], @options)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
# My Modules, Classes, etc
|
157
|
+
|
158
|
+
class RSSFeed
|
159
|
+
include RSSClient
|
160
|
+
|
161
|
+
def fetch_raw(uri, options)
|
162
|
+
options.extra = Hash.new
|
163
|
+
options.extra["Connection"] = "close"
|
164
|
+
options.extra["User-Agent"] = "RSSClient/2.0.9"
|
165
|
+
# options.since is Time::
|
166
|
+
if not options.forceUpdate and options.since
|
167
|
+
time = Time.parse(options.since.to_s)
|
168
|
+
options.extra["If-Modified-Since"] = time.httpdate() if time
|
169
|
+
end
|
170
|
+
begin
|
171
|
+
rss = get_url(uri, options)
|
172
|
+
if rss
|
173
|
+
puts "Status code: #{rss.status}"
|
174
|
+
puts "Headers:"
|
175
|
+
rss.header.dump.each do |h|
|
176
|
+
puts "- " + h.to_s
|
177
|
+
end
|
178
|
+
end
|
179
|
+
rescue RuntimeError => error
|
180
|
+
puts "[E] #{error}"
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def fetch(uri, options)
|
185
|
+
# always fetch fresh feed
|
186
|
+
rss = get_feed(uri, options)
|
187
|
+
puts "Status code: #{@rssc_raw.status}" if @rssc_raw
|
188
|
+
if rss && rss != "304"
|
189
|
+
puts "Title: #{rss.channel.title.to_s}"
|
190
|
+
puts "Description: #{rss.channel.description}" if rss.channel.description
|
191
|
+
puts "Link: #{rss.channel.urls.first}" if rss.channel.urls
|
192
|
+
puts "News:"
|
193
|
+
rss.entries.each do |i|
|
194
|
+
puts "- " + i.title.to_s
|
195
|
+
end
|
196
|
+
else
|
197
|
+
puts "[E] #{@rssc_error}" if @rssc_error
|
198
|
+
exit
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
# Create and run the application
|
205
|
+
app = App.new(ARGV, STDIN)
|
206
|
+
app.run
|
data/install.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
require 'find'
|
3
|
+
require 'ftools'
|
4
|
+
|
5
|
+
include Config
|
6
|
+
|
7
|
+
$ruby = CONFIG['ruby_install_name']
|
8
|
+
|
9
|
+
##
|
10
|
+
# Install a binary file. We patch in on the way through to
|
11
|
+
# insert a #! line. If this is a Unix install, we name
|
12
|
+
# the command (for example) 'rssclient' and let the shebang line
|
13
|
+
# handle running it. Under windows, we add a '.rb' extension
|
14
|
+
# and let file associations to their stuff
|
15
|
+
#
|
16
|
+
|
17
|
+
def installBIN(from, opfile)
|
18
|
+
|
19
|
+
tmp_dir = nil
|
20
|
+
for t in [".", "/tmp", "c:/temp", $bindir]
|
21
|
+
stat = File.stat(t) rescue next
|
22
|
+
if stat.directory? and stat.writable?
|
23
|
+
tmp_dir = t
|
24
|
+
break
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
fail "Cannot find a temporary directory" unless tmp_dir
|
29
|
+
tmp_file = File.join(tmp_dir, "_tmp")
|
30
|
+
|
31
|
+
File.open(from) do |ip|
|
32
|
+
File.open(tmp_file, "w") do |op|
|
33
|
+
ruby = File.join($realbindir, $ruby)
|
34
|
+
op.puts "#!#{ruby} -w"
|
35
|
+
op.write ip.read
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
opfile += ".rb" if CONFIG["target_os"] =~ /mswin/i
|
40
|
+
File::install(tmp_file, File.join($bindir, opfile), 0755, true)
|
41
|
+
File::unlink(tmp_file)
|
42
|
+
end
|
43
|
+
|
44
|
+
$sitedir = CONFIG["sitelibdir"]
|
45
|
+
unless $sitedir
|
46
|
+
version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
|
47
|
+
$libdir = File.join(CONFIG["libdir"], "ruby", version)
|
48
|
+
$sitedir = $:.find {|x| x =~ /site_ruby/}
|
49
|
+
if !$sitedir
|
50
|
+
$sitedir = File.join($libdir, "site_ruby")
|
51
|
+
elsif $sitedir !~ Regexp.quote(version)
|
52
|
+
$sitedir = File.join($sitedir, version)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
$bindir = CONFIG["bindir"]
|
57
|
+
|
58
|
+
$realbindir = $bindir
|
59
|
+
|
60
|
+
bindir = CONFIG["bindir"]
|
61
|
+
if (destdir = ENV['DESTDIR'])
|
62
|
+
$bindir = destdir + $bindir
|
63
|
+
$sitedir = destdir + $sitedir
|
64
|
+
|
65
|
+
File::makedirs($bindir)
|
66
|
+
File::makedirs($sitedir)
|
67
|
+
end
|
68
|
+
|
69
|
+
# The library files
|
70
|
+
|
71
|
+
files = Dir.chdir('lib') { Dir['**/*.rb'] }
|
72
|
+
|
73
|
+
for fn in files
|
74
|
+
fn_dir = File.dirname(fn)
|
75
|
+
target_dir = File.join($sitedir, fn_dir)
|
76
|
+
if ! File.exist?(target_dir)
|
77
|
+
File.makedirs(target_dir)
|
78
|
+
end
|
79
|
+
File::install(File.join('lib', fn), File.join($sitedir, fn), 0644, true)
|
80
|
+
end
|
81
|
+
|
82
|
+
# and the executable
|
83
|
+
|
84
|
+
installBIN("bin/rssclient", "rssclient")
|