whistle 0.1
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.
- data/History.txt +3 -0
- data/README.txt +38 -0
- data/bin/whistle +90 -0
- data/lib/config.rb +19 -0
- data/lib/phash.rb +16 -0
- data/lib/relay.rb +24 -0
- data/lib/resource.rb +113 -0
- data/lib/ssl_patch.rb +15 -0
- data/lib/switchbox.rb +54 -0
- data/lib/time_ext.rb +30 -0
- data/lib/version.rb +3 -0
- data/sample/config.yml +12 -0
- data/vendor/rscm-0.5.1-patched-stripped/README +218 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm.rb +14 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/abstract_log_parser.rb +35 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/base.rb +289 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/command_line.rb +146 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/difftool.rb +44 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/line_editor.rb +46 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/mockit.rb +157 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/parser.rb +39 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/path_converter.rb +60 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/platform.rb +26 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/revision.rb +103 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/revision_file.rb +85 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/revision_poller.rb +93 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/revisions.rb +79 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/clearcase.rb +182 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/cvs.rb +374 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/cvs_log_parser.rb +154 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/darcs.rb +120 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/darcs_log_parser.rb +65 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/monotone.rb +338 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/monotone_log_parser.rb +109 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/mooky.rb +6 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/perforce.rb +216 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/star_team.rb +104 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/subversion.rb +397 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/scm/subversion_log_parser.rb +165 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/tempdir.rb +17 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/time_ext.rb +11 -0
- data/vendor/rscm-0.5.1-patched-stripped/lib/rscm/version.rb +13 -0
- data/vendor/ruby-feedparser-0.5-stripped/README +14 -0
- data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser.rb +28 -0
- data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/feedparser.rb +300 -0
- data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/filesizes.rb +12 -0
- data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/html-output.rb +126 -0
- data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/html2text-parser.rb +409 -0
- data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/rexml_patch.rb +28 -0
- data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/sgml-parser.rb +332 -0
- data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/text-output.rb +83 -0
- data/vendor/ruby-feedparser-0.5-stripped/lib/feedparser/textconverters.rb +120 -0
- metadata +132 -0
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'rscm/parser'
|
2
|
+
require 'rscm/revision'
|
3
|
+
require 'rscm/revisions'
|
4
|
+
require 'rscm/revision_file'
|
5
|
+
require 'time'
|
6
|
+
|
7
|
+
module RSCM
|
8
|
+
|
9
|
+
class SubversionLogParser
|
10
|
+
def initialize(io, url, exclude_below_and_including=nil, exclude_above_and_including=nil, path=nil)
|
11
|
+
@io = io
|
12
|
+
@revision_parser = SubversionLogEntryParser.new(url, path)
|
13
|
+
@exclude_below_and_including = exclude_below_and_including
|
14
|
+
@exclude_above_and_including = exclude_above_and_including
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_revisions(&line_proc)
|
18
|
+
# skip over the first ------
|
19
|
+
@revision_parser.parse(@io, true, &line_proc)
|
20
|
+
revisions = Revisions.new
|
21
|
+
while(!@io.eof?)
|
22
|
+
revision = @revision_parser.parse(@io, &line_proc)
|
23
|
+
unless(revision.nil?)
|
24
|
+
# Filter out the lower bound to avoid inclusiveness of the lower bound (see contract)
|
25
|
+
# We're doing this instead of increasing the from_identifer with 1, since that causes an error.
|
26
|
+
too_low = false
|
27
|
+
too_high = false
|
28
|
+
next if revision.time.nil?
|
29
|
+
if(@exclude_below_and_including.is_a? Time)
|
30
|
+
too_low = revision.time <= @exclude_below_and_including
|
31
|
+
elsif(@exclude_below_and_including.is_a? Numeric)
|
32
|
+
too_low = revision.identifier <= @exclude_below_and_including
|
33
|
+
end
|
34
|
+
|
35
|
+
if(@exclude_above_and_including.is_a? Time)
|
36
|
+
too_high = revision.time >= @exclude_above_and_including
|
37
|
+
elsif(@exclude_above_and_including.is_a? Numeric)
|
38
|
+
too_high = revision.identifier >= @exclude_above_and_including
|
39
|
+
end
|
40
|
+
revisions.add(revision) unless too_low || too_high
|
41
|
+
end
|
42
|
+
end
|
43
|
+
revisions
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class SubversionLogEntryParser < Parser
|
48
|
+
|
49
|
+
def initialize(url, path=nil)
|
50
|
+
super(/^------------------------------------------------------------------------/)
|
51
|
+
@url = url
|
52
|
+
@path = path
|
53
|
+
end
|
54
|
+
|
55
|
+
def parse(io, skip_line_parsing=false, &line_proc)
|
56
|
+
# We have to trim off the last newline - it's not meant to be part of the message
|
57
|
+
revision = super
|
58
|
+
revision.message = revision.message[0..-2] if revision
|
59
|
+
revision
|
60
|
+
end
|
61
|
+
|
62
|
+
def relative_path(url, path_from_root)
|
63
|
+
path_from_root = path_from_root.chomp
|
64
|
+
url_tokens = url.split('/')
|
65
|
+
path_from_root_tokens = path_from_root.split('/')
|
66
|
+
|
67
|
+
max_similar = path_from_root_tokens.length
|
68
|
+
while(max_similar > 0)
|
69
|
+
url = url_tokens[-max_similar..-1]
|
70
|
+
path = path_from_root_tokens[0..max_similar-1]
|
71
|
+
if(url == path)
|
72
|
+
break
|
73
|
+
end
|
74
|
+
max_similar -= 1
|
75
|
+
end
|
76
|
+
|
77
|
+
if(max_similar == 0)
|
78
|
+
if(@path.nil? || @path == "")
|
79
|
+
path_from_root
|
80
|
+
else
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
else
|
84
|
+
path_from_root_tokens[max_similar..-1].join("/")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
def parse_line(line)
|
91
|
+
if(@revision.nil?)
|
92
|
+
parse_header(line)
|
93
|
+
elsif(line.strip == "")
|
94
|
+
@parse_state = :parse_message
|
95
|
+
elsif(line =~ /Changed paths/)
|
96
|
+
@parse_state = :parse_files
|
97
|
+
elsif(@parse_state == :parse_files)
|
98
|
+
file = parse_file(line)
|
99
|
+
if(file && file.path)
|
100
|
+
previously_added_file = @revision[-1]
|
101
|
+
if(previously_added_file)
|
102
|
+
# remove previous revision_file if it's a dir
|
103
|
+
previous_tokens = previously_added_file.path.split("/")
|
104
|
+
current_tokens = file.path.split("/")
|
105
|
+
current_tokens.pop
|
106
|
+
if(previous_tokens == current_tokens)
|
107
|
+
@revision.pop
|
108
|
+
end
|
109
|
+
end
|
110
|
+
@revision.add file
|
111
|
+
end
|
112
|
+
elsif(@parse_state == :parse_message)
|
113
|
+
@revision.message << line.chomp << "\n"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def next_result
|
118
|
+
result = @revision
|
119
|
+
@revision = nil
|
120
|
+
result
|
121
|
+
end
|
122
|
+
|
123
|
+
private
|
124
|
+
|
125
|
+
STATES = {"M" => RevisionFile::MODIFIED, "A" => RevisionFile::ADDED, "D" => RevisionFile::DELETED} unless defined? STATES
|
126
|
+
|
127
|
+
def parse_header(line)
|
128
|
+
@revision = Revision.new
|
129
|
+
@revision.message = ""
|
130
|
+
revision, developer, time, the_rest = line.split("|")
|
131
|
+
@revision.identifier = revision.strip[1..-1].to_i unless revision.nil?
|
132
|
+
developer.strip!
|
133
|
+
@revision.developer = developer unless developer.nil? || developer == "(no author)"
|
134
|
+
time.strip!
|
135
|
+
@revision.time = Time.parse(time).utc unless time.nil? || time == "(no date)"
|
136
|
+
end
|
137
|
+
|
138
|
+
def parse_file(line)
|
139
|
+
file = RevisionFile.new
|
140
|
+
path_from_root = nil
|
141
|
+
if(line =~ /^ [M|A|D|R] ([^\s]+) \(from (.*)\)/)
|
142
|
+
path_from_root = $1
|
143
|
+
file.status = RevisionFile::MOVED
|
144
|
+
elsif(line =~ /^ ([M|A|D|R]) (.+)$/)
|
145
|
+
status = $1
|
146
|
+
path_from_root = $2
|
147
|
+
file.status = STATES[status]
|
148
|
+
else
|
149
|
+
raise "could not parse file line: '#{line}'"
|
150
|
+
end
|
151
|
+
|
152
|
+
path_from_root.gsub!(/\\/, "/")
|
153
|
+
path_from_root = path_from_root[1..-1]
|
154
|
+
rp = relative_path(@url, path_from_root)
|
155
|
+
return if rp.nil?
|
156
|
+
|
157
|
+
file.path = rp
|
158
|
+
file.native_revision_identifier = @revision.identifier
|
159
|
+
# http://jira.codehaus.org/browse/DC-204
|
160
|
+
file.previous_native_revision_identifier = file.native_revision_identifier.to_i - 1;
|
161
|
+
file
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module RSCM
|
4
|
+
|
5
|
+
def method_name
|
6
|
+
/\`([^\']+)\'/.match(caller.first)[1]
|
7
|
+
end
|
8
|
+
|
9
|
+
def new_temp_dir(suffix="", basedir=File.dirname(__FILE__) + "/../../target")
|
10
|
+
identifier = identifier.to_s
|
11
|
+
identifier.gsub!(/\(|:|\)/, '_')
|
12
|
+
dir = "#{basedir}/temp_#{identifier}_#{Time.new.to_i}#{suffix}"
|
13
|
+
FileUtils.mkdir_p(dir)
|
14
|
+
dir
|
15
|
+
end
|
16
|
+
module_function :new_temp_dir
|
17
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Ruby-Feedparser
|
2
|
+
-----------------
|
3
|
+
by Lucas Nussbaum <lucas@lucas-nussbaum.net>
|
4
|
+
|
5
|
+
Currently, all the information is provided on
|
6
|
+
|
7
|
+
http://home.gna.org/ruby-feedparser/
|
8
|
+
|
9
|
+
If you need to ask questions, feel free to ask them on the
|
10
|
+
ruby-feedparser-devel@gna.org mailing list.
|
11
|
+
|
12
|
+
Ruby-Feedparser is released under the Ruby license (see the LICENSE file),
|
13
|
+
which is compatible with the GNU GPL (see the COPYING file) via an explicit
|
14
|
+
dual-licensing clause.
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# =Ruby-feedparser - ATOM/RSS feed parser for Ruby
|
2
|
+
# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
|
3
|
+
# Website::http://home.gna.org/ruby-feedparser/
|
4
|
+
#
|
5
|
+
# ==Introduction
|
6
|
+
#
|
7
|
+
# Ruby-Feedparser is an RSS and Atom parser for Ruby.
|
8
|
+
# Ruby-feedparser is :
|
9
|
+
# * based on REXML
|
10
|
+
# * built for robustness : most feeds are not valid, a parser can't ignore that
|
11
|
+
# * fully unit-tested
|
12
|
+
# * easy to use (it can output text or HTML easily)
|
13
|
+
#
|
14
|
+
# ==Example
|
15
|
+
# require 'net/http'
|
16
|
+
# require 'feedparser'
|
17
|
+
# require 'uri'
|
18
|
+
# s = Net::HTTP::get URI::parse('http://rss.slashdot.org/Slashdot/slashdot')
|
19
|
+
# f = FeedParser::Feed::new(s)
|
20
|
+
# f.title
|
21
|
+
# => "Slashdot"
|
22
|
+
# f.items.each { |i| puts i.title }
|
23
|
+
# [...]
|
24
|
+
# require 'feedparser/html-output'
|
25
|
+
# f.items.each { |i| puts i.to_html }
|
26
|
+
#
|
27
|
+
|
28
|
+
require 'feedparser/feedparser'
|
@@ -0,0 +1,300 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'time'
|
3
|
+
require 'feedparser/textconverters'
|
4
|
+
require 'feedparser/rexml_patch'
|
5
|
+
require 'feedparser/text-output'
|
6
|
+
require 'base64'
|
7
|
+
|
8
|
+
module FeedParser
|
9
|
+
|
10
|
+
VERSION = "0.5"
|
11
|
+
|
12
|
+
class UnknownFeedTypeException < RuntimeError
|
13
|
+
end
|
14
|
+
|
15
|
+
# an RSS/Atom feed
|
16
|
+
class Feed
|
17
|
+
attr_reader :type, :title, :link, :description, :creator, :encoding, :items
|
18
|
+
|
19
|
+
# REXML::Element for this feed.
|
20
|
+
attr_reader :xml
|
21
|
+
|
22
|
+
# parse str to build a Feed
|
23
|
+
def initialize(str = nil)
|
24
|
+
parse(str) if str
|
25
|
+
end
|
26
|
+
|
27
|
+
# Determines all the fields using a string containing an
|
28
|
+
# XML document
|
29
|
+
def parse(str)
|
30
|
+
# Dirty hack: some feeds contain the & char. It must be changed to &
|
31
|
+
str.gsub!(/&(\s+)/, '&\1')
|
32
|
+
doc = REXML::Document.new(str)
|
33
|
+
@xml = doc.root
|
34
|
+
# get feed info
|
35
|
+
@encoding = doc.encoding
|
36
|
+
@title,@link,@description,@creator = nil
|
37
|
+
@items = []
|
38
|
+
if doc.root.elements['channel'] || doc.root.elements['rss:channel']
|
39
|
+
@type = "rss"
|
40
|
+
# We have a RSS feed!
|
41
|
+
# Title
|
42
|
+
if (e = doc.root.elements['channel/title'] ||
|
43
|
+
doc.root.elements['rss:channel/rss:title']) && e.text
|
44
|
+
@title = e.text.unescape_html.toUTF8(@encoding).rmWhiteSpace!
|
45
|
+
end
|
46
|
+
# Link
|
47
|
+
if (e = doc.root.elements['channel/link'] ||
|
48
|
+
doc.root.elements['rss:channel/rss:link']) && e.text
|
49
|
+
@link = e.text.rmWhiteSpace!
|
50
|
+
end
|
51
|
+
# Description
|
52
|
+
if (e = doc.root.elements['channel/description'] ||
|
53
|
+
doc.root.elements['rss:channel/rss:description']) && e.text
|
54
|
+
@description = e.text.toUTF8(@encoding).rmWhiteSpace!
|
55
|
+
end
|
56
|
+
# Creator
|
57
|
+
if ((e = doc.root.elements['channel/dc:creator']) && e.text) ||
|
58
|
+
((e = doc.root.elements['channel/author'] ||
|
59
|
+
doc.root.elements['rss:channel/rss:author']) && e.text)
|
60
|
+
@creator = e.text.unescape_html.toUTF8(@encoding).rmWhiteSpace!
|
61
|
+
end
|
62
|
+
# Items
|
63
|
+
if doc.root.elements['channel/item']
|
64
|
+
query = 'channel/item'
|
65
|
+
elsif doc.root.elements['item']
|
66
|
+
query = 'item'
|
67
|
+
elsif doc.root.elements['rss:channel/rss:item']
|
68
|
+
query = 'rss:channel/rss:item'
|
69
|
+
else
|
70
|
+
query = 'rss:item'
|
71
|
+
end
|
72
|
+
doc.root.each_element(query) { |e| @items << RSSItem::new(e, self) }
|
73
|
+
|
74
|
+
elsif doc.root.elements['/feed']
|
75
|
+
# We have an ATOM feed!
|
76
|
+
@type = "atom"
|
77
|
+
# Title
|
78
|
+
if (e = doc.root.elements['/feed/title']) && e.text
|
79
|
+
@title = e.text.unescape_html.toUTF8(@encoding).rmWhiteSpace!
|
80
|
+
end
|
81
|
+
# Link
|
82
|
+
doc.root.each_element('/feed/link') do |e|
|
83
|
+
if e.attribute('type') and (
|
84
|
+
e.attribute('type').value == 'text/html' or
|
85
|
+
e.attribute('type').value == 'application/xhtml' or
|
86
|
+
e.attribute('type').value == 'application/xhtml+xml')
|
87
|
+
if (h = e.attribute('href')) && h
|
88
|
+
@link = h.value.rmWhiteSpace!
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
# Description
|
93
|
+
if e = doc.root.elements['/feed/info']
|
94
|
+
e = e.elements['div'] || e
|
95
|
+
@description = e.to_s.toUTF8(@encoding).rmWhiteSpace!
|
96
|
+
end
|
97
|
+
# Items
|
98
|
+
doc.root.each_element('/feed/entry') do |e|
|
99
|
+
@items << AtomItem::new(e, self)
|
100
|
+
end
|
101
|
+
else
|
102
|
+
raise UnknownFeedTypeException::new
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def to_s(localtime = true)
|
107
|
+
s = ''
|
108
|
+
s += "Type: #{@type}\n"
|
109
|
+
s += "Encoding: #{@encoding}\n"
|
110
|
+
s += "Title: #{@title}\n"
|
111
|
+
s += "Link: #{@link}\n"
|
112
|
+
s += "Description: #{@description}\n"
|
113
|
+
s += "Creator: #{@creator}\n"
|
114
|
+
s += "\n"
|
115
|
+
@items.each { |i| s += i.to_s(localtime) }
|
116
|
+
s
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# an Item from a feed
|
121
|
+
class FeedItem
|
122
|
+
attr_accessor :title, :link, :content, :date, :creator, :subject,
|
123
|
+
:category, :cacheditem
|
124
|
+
|
125
|
+
# The item's enclosures childs. An array of (url, length, type) triplets.
|
126
|
+
attr_accessor :enclosures
|
127
|
+
|
128
|
+
attr_reader :feed
|
129
|
+
|
130
|
+
# REXML::Element for this item
|
131
|
+
attr_reader :xml
|
132
|
+
|
133
|
+
def initialize(item = nil, feed = nil)
|
134
|
+
@xml = item
|
135
|
+
@feed = feed
|
136
|
+
@title, @link, @content, @date, @creator, @subject, @category = nil
|
137
|
+
@enclosures = []
|
138
|
+
parse(item) if item
|
139
|
+
end
|
140
|
+
|
141
|
+
def parse(item)
|
142
|
+
raise "parse() should be implemented by subclasses!"
|
143
|
+
end
|
144
|
+
|
145
|
+
def to_s(localtime = true)
|
146
|
+
s = "--------------------------------\n" +
|
147
|
+
"Title: #{@title}\nLink: #{@link}\n"
|
148
|
+
if localtime or @date.nil?
|
149
|
+
s += "Date: #{@date.to_s}\n"
|
150
|
+
else
|
151
|
+
s += "Date: #{@date.getutc.to_s}\n"
|
152
|
+
end
|
153
|
+
s += "Creator: #{@creator}\n" +
|
154
|
+
"Subject: #{@subject}\nCategory: #{@category}\nContent:\n#{content}\n"
|
155
|
+
if defined?(@enclosures) and @enclosures.length > 0
|
156
|
+
s2 = "Enclosures:\n"
|
157
|
+
@enclosures.each do |e|
|
158
|
+
s2 += e.join(' ') + "\n"
|
159
|
+
end
|
160
|
+
s += s2
|
161
|
+
end
|
162
|
+
return s
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
class RSSItem < FeedItem
|
167
|
+
|
168
|
+
|
169
|
+
def parse(item)
|
170
|
+
# Title. If no title, use the pubDate as fallback.
|
171
|
+
if ((e = item.elements['title'] || item.elements['rss:title']) &&
|
172
|
+
e.text) ||
|
173
|
+
((e = item.elements['pubDate'] || item.elements['rss:pubDate']) &&
|
174
|
+
e.text)
|
175
|
+
@title = e.text.unescape_html.toUTF8(@feed.encoding).html2text.rmWhiteSpace!
|
176
|
+
end
|
177
|
+
# Link
|
178
|
+
if ((e = item.elements['link'] || item.elements['rss:link']) && e.text)||
|
179
|
+
(e = item.elements['guid'] || item.elements['rss:guid'] and
|
180
|
+
not (e.attribute('isPermaLink') and
|
181
|
+
e.attribute('isPermaLink').value == 'false'))
|
182
|
+
@link = e.text.rmWhiteSpace!
|
183
|
+
end
|
184
|
+
# Content
|
185
|
+
if (e = item.elements['content:encoded']) ||
|
186
|
+
(e = item.elements['description'] || item.elements['rss:description'])
|
187
|
+
@content = FeedParser::getcontent(e, @feed)
|
188
|
+
end
|
189
|
+
# Date
|
190
|
+
if e = item.elements['dc:date'] || item.elements['pubDate'] ||
|
191
|
+
item.elements['rss:pubDate']
|
192
|
+
begin
|
193
|
+
@date = Time::xmlschema(e.text)
|
194
|
+
rescue
|
195
|
+
begin
|
196
|
+
@date = Time::rfc2822(e.text)
|
197
|
+
rescue
|
198
|
+
begin
|
199
|
+
@date = Time::parse(e.text)
|
200
|
+
rescue
|
201
|
+
@date = nil
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
# Creator
|
207
|
+
@creator = @feed.creator
|
208
|
+
if (e = item.elements['dc:creator'] || item.elements['author'] ||
|
209
|
+
item.elements['rss:author']) && e.text
|
210
|
+
@creator = e.text.unescape_html.toUTF8(@feed.encoding).rmWhiteSpace!
|
211
|
+
end
|
212
|
+
# Subject
|
213
|
+
if (e = item.elements['dc:subject']) && e.text
|
214
|
+
@subject = e.text.unescape_html.toUTF8(@feed.encoding).rmWhiteSpace!
|
215
|
+
end
|
216
|
+
# Category
|
217
|
+
if (e = item.elements['dc:category'] || item.elements['category'] ||
|
218
|
+
item.elements['rss:category']) && e.text
|
219
|
+
@category = e.text.unescape_html.toUTF8(@feed.encoding).rmWhiteSpace!
|
220
|
+
end
|
221
|
+
# Enclosures
|
222
|
+
item.each_element('enclosure') do |e|
|
223
|
+
url = e.attribute('url').value if e.attribute('url')
|
224
|
+
length = e.attribute('length').value if e.attribute('length')
|
225
|
+
type = e.attribute('type').value if e.attribute('type')
|
226
|
+
@enclosures << [ url, length, type ] if url
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
class AtomItem < FeedItem
|
232
|
+
def parse(item)
|
233
|
+
# Title
|
234
|
+
if (e = item.elements['title']) && e.text
|
235
|
+
@title = e.text.unescape_html.toUTF8(@feed.encoding).html2text.rmWhiteSpace!
|
236
|
+
end
|
237
|
+
# Link
|
238
|
+
item.each_element('link') do |e|
|
239
|
+
if (h = e.attribute('href')) && h.value
|
240
|
+
@link = h.value
|
241
|
+
end
|
242
|
+
end
|
243
|
+
# Content
|
244
|
+
if e = item.elements['content'] || item.elements['summary']
|
245
|
+
if (e.attribute('mode') and e.attribute('mode').value == 'escaped') &&
|
246
|
+
e.text
|
247
|
+
@content = e.text.toUTF8(@feed.encoding).rmWhiteSpace!
|
248
|
+
else
|
249
|
+
@content = FeedParser::getcontent(e, @feed)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
# Date
|
253
|
+
if (e = item.elements['issued'] || e = item.elements['created'] || e = item.elements['updated'] || e = item.elements['published']) && e.text
|
254
|
+
begin
|
255
|
+
@date = Time::xmlschema(e.text)
|
256
|
+
rescue
|
257
|
+
begin
|
258
|
+
@date = Time::rfc2822(e.text)
|
259
|
+
rescue
|
260
|
+
begin
|
261
|
+
@date = Time::parse(e.text)
|
262
|
+
rescue
|
263
|
+
@date = nil
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
# Creator
|
269
|
+
@creator = @feed.creator
|
270
|
+
if (e = item.elements['author/name']) && e.text
|
271
|
+
@creator = e.text.unescape_html.toUTF8(@feed.encoding).rmWhiteSpace!
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def FeedParser::getcontent(e, feed = nil)
|
277
|
+
encoding = feed ? feed.encoding : 'utf-8'
|
278
|
+
children = e.children.reject do |i|
|
279
|
+
i.class == REXML::Text and i.to_s.chomp == ''
|
280
|
+
end
|
281
|
+
if children.length > 1
|
282
|
+
s = ''
|
283
|
+
children.each do |c|
|
284
|
+
s += c.to_s if c.class != REXML::Comment
|
285
|
+
end
|
286
|
+
return s.toUTF8(encoding).rmWhiteSpace!.text2html(feed)
|
287
|
+
elsif children.length == 1
|
288
|
+
c = children[0]
|
289
|
+
if c.class == REXML::Text
|
290
|
+
return e.text.toUTF8(encoding).rmWhiteSpace!.text2html(feed)
|
291
|
+
else
|
292
|
+
if c.class == REXML::CData
|
293
|
+
return c.to_s.toUTF8(encoding).rmWhiteSpace!.text2html(feed)
|
294
|
+
elsif c.text
|
295
|
+
return c.text.toUTF8(encoding).text2html(feed)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|