rss 0.2.7
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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +6 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +88 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/rss.rb +92 -0
- data/lib/rss/0.9.rb +462 -0
- data/lib/rss/1.0.rb +485 -0
- data/lib/rss/2.0.rb +143 -0
- data/lib/rss/atom.rb +1025 -0
- data/lib/rss/content.rb +34 -0
- data/lib/rss/content/1.0.rb +10 -0
- data/lib/rss/content/2.0.rb +12 -0
- data/lib/rss/converter.rb +171 -0
- data/lib/rss/dublincore.rb +164 -0
- data/lib/rss/dublincore/1.0.rb +13 -0
- data/lib/rss/dublincore/2.0.rb +13 -0
- data/lib/rss/dublincore/atom.rb +17 -0
- data/lib/rss/image.rb +198 -0
- data/lib/rss/itunes.rb +413 -0
- data/lib/rss/maker.rb +79 -0
- data/lib/rss/maker/0.9.rb +509 -0
- data/lib/rss/maker/1.0.rb +436 -0
- data/lib/rss/maker/2.0.rb +224 -0
- data/lib/rss/maker/atom.rb +173 -0
- data/lib/rss/maker/base.rb +945 -0
- data/lib/rss/maker/content.rb +22 -0
- data/lib/rss/maker/dublincore.rb +122 -0
- data/lib/rss/maker/entry.rb +164 -0
- data/lib/rss/maker/feed.rb +427 -0
- data/lib/rss/maker/image.rb +112 -0
- data/lib/rss/maker/itunes.rb +243 -0
- data/lib/rss/maker/slash.rb +34 -0
- data/lib/rss/maker/syndication.rb +19 -0
- data/lib/rss/maker/taxonomy.rb +119 -0
- data/lib/rss/maker/trackback.rb +62 -0
- data/lib/rss/parser.rb +589 -0
- data/lib/rss/rexmlparser.rb +50 -0
- data/lib/rss/rss.rb +1346 -0
- data/lib/rss/slash.rb +52 -0
- data/lib/rss/syndication.rb +69 -0
- data/lib/rss/taxonomy.rb +148 -0
- data/lib/rss/trackback.rb +291 -0
- data/lib/rss/utils.rb +200 -0
- data/lib/rss/xml-stylesheet.rb +106 -0
- data/lib/rss/xml.rb +72 -0
- data/lib/rss/xmlparser.rb +95 -0
- data/lib/rss/xmlscanner.rb +122 -0
- data/rss.gemspec +38 -0
- metadata +138 -0
data/lib/rss/utils.rb
ADDED
@@ -0,0 +1,200 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
module RSS
|
3
|
+
|
4
|
+
##
|
5
|
+
# RSS::Utils is a module that holds various utility functions that are used
|
6
|
+
# across many parts of the rest of the RSS library. Like most modules named
|
7
|
+
# some variant of 'util', its methods are probably not particularly useful
|
8
|
+
# to those who aren't developing the library itself.
|
9
|
+
module Utils
|
10
|
+
module_function
|
11
|
+
|
12
|
+
# Given a +name+ in a name_with_underscores or a name-with-dashes format,
|
13
|
+
# returns the CamelCase version of +name+.
|
14
|
+
#
|
15
|
+
# If the +name+ is already CamelCased, nothing happens.
|
16
|
+
#
|
17
|
+
# Examples:
|
18
|
+
#
|
19
|
+
# require 'rss/utils'
|
20
|
+
#
|
21
|
+
# RSS::Utils.to_class_name("sample_name")
|
22
|
+
# # => "SampleName"
|
23
|
+
# RSS::Utils.to_class_name("with-dashes")
|
24
|
+
# # => "WithDashes"
|
25
|
+
# RSS::Utils.to_class_name("CamelCase")
|
26
|
+
# # => "CamelCase"
|
27
|
+
def to_class_name(name)
|
28
|
+
name.split(/[_\-]/).collect do |part|
|
29
|
+
"#{part[0, 1].upcase}#{part[1..-1]}"
|
30
|
+
end.join("")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Returns an array of two elements: the filename where the calling method
|
34
|
+
# is located, and the line number where it is defined.
|
35
|
+
#
|
36
|
+
# Takes an optional argument +i+, which specifies how many callers up the
|
37
|
+
# stack to look.
|
38
|
+
#
|
39
|
+
# Examples:
|
40
|
+
#
|
41
|
+
# require 'rss/utils'
|
42
|
+
#
|
43
|
+
# def foo
|
44
|
+
# p RSS::Utils.get_file_and_line_from_caller
|
45
|
+
# p RSS::Utils.get_file_and_line_from_caller(1)
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# def bar
|
49
|
+
# foo
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# def baz
|
53
|
+
# bar
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# baz
|
57
|
+
# # => ["test.rb", 5]
|
58
|
+
# # => ["test.rb", 9]
|
59
|
+
#
|
60
|
+
# If +i+ is not given, or is the default value of 0, it attempts to figure
|
61
|
+
# out the correct value. This is useful when in combination with
|
62
|
+
# instance_eval. For example:
|
63
|
+
#
|
64
|
+
# require 'rss/utils'
|
65
|
+
#
|
66
|
+
# def foo
|
67
|
+
# p RSS::Utils.get_file_and_line_from_caller(1)
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# def bar
|
71
|
+
# foo
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# instance_eval <<-RUBY, *RSS::Utils.get_file_and_line_from_caller
|
75
|
+
# def baz
|
76
|
+
# bar
|
77
|
+
# end
|
78
|
+
# RUBY
|
79
|
+
#
|
80
|
+
# baz
|
81
|
+
#
|
82
|
+
# # => ["test.rb", 8]
|
83
|
+
def get_file_and_line_from_caller(i=0)
|
84
|
+
file, line, = caller[i].split(':')
|
85
|
+
line = line.to_i
|
86
|
+
line += 1 if i.zero?
|
87
|
+
[file, line]
|
88
|
+
end
|
89
|
+
|
90
|
+
# Takes a string +s+ with some HTML in it, and escapes '&', '"', '<' and '>', by
|
91
|
+
# replacing them with the appropriate entities.
|
92
|
+
#
|
93
|
+
# This method is also aliased to h, for convenience.
|
94
|
+
#
|
95
|
+
# Examples:
|
96
|
+
#
|
97
|
+
# require 'rss/utils'
|
98
|
+
#
|
99
|
+
# RSS::Utils.html_escape("Dungeons & Dragons")
|
100
|
+
# # => "Dungeons & Dragons"
|
101
|
+
# RSS::Utils.h(">_>")
|
102
|
+
# # => ">_>"
|
103
|
+
def html_escape(s)
|
104
|
+
s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/</, "<")
|
105
|
+
end
|
106
|
+
alias h html_escape
|
107
|
+
|
108
|
+
# If +value+ is an instance of class +klass+, return it, else
|
109
|
+
# create a new instance of +klass+ with value +value+.
|
110
|
+
def new_with_value_if_need(klass, value)
|
111
|
+
if value.is_a?(klass)
|
112
|
+
value
|
113
|
+
else
|
114
|
+
klass.new(value)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# This method is used inside of several different objects to determine
|
119
|
+
# if special behavior is needed in the constructor.
|
120
|
+
#
|
121
|
+
# Special behavior is needed if the array passed in as +args+ has
|
122
|
+
# +true+ or +false+ as its value, and if the second element of +args+
|
123
|
+
# is a hash.
|
124
|
+
def element_initialize_arguments?(args)
|
125
|
+
[true, false].include?(args[0]) and args[1].is_a?(Hash)
|
126
|
+
end
|
127
|
+
|
128
|
+
module ExplicitCleanOther
|
129
|
+
module_function
|
130
|
+
def parse(value)
|
131
|
+
if [true, false, nil].include?(value)
|
132
|
+
value
|
133
|
+
else
|
134
|
+
case value.to_s
|
135
|
+
when /\Aexplicit|yes|true\z/i
|
136
|
+
true
|
137
|
+
when /\Aclean|no|false\z/i
|
138
|
+
false
|
139
|
+
else
|
140
|
+
nil
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
module YesOther
|
147
|
+
module_function
|
148
|
+
def parse(value)
|
149
|
+
if [true, false].include?(value)
|
150
|
+
value
|
151
|
+
else
|
152
|
+
/\Ayes\z/i.match(value.to_s) ? true : false
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
module CSV
|
158
|
+
module_function
|
159
|
+
def parse(value, &block)
|
160
|
+
if value.is_a?(String)
|
161
|
+
value = value.strip.split(/\s*,\s*/)
|
162
|
+
value = value.collect(&block) if block_given?
|
163
|
+
value
|
164
|
+
else
|
165
|
+
value
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
module InheritedReader
|
171
|
+
def inherited_reader(constant_name)
|
172
|
+
base_class = inherited_base
|
173
|
+
result = base_class.const_get(constant_name)
|
174
|
+
found_base_class = false
|
175
|
+
ancestors.reverse_each do |klass|
|
176
|
+
if found_base_class
|
177
|
+
if klass.const_defined?(constant_name)
|
178
|
+
result = yield(result, klass.const_get(constant_name))
|
179
|
+
end
|
180
|
+
else
|
181
|
+
found_base_class = klass == base_class
|
182
|
+
end
|
183
|
+
end
|
184
|
+
result
|
185
|
+
end
|
186
|
+
|
187
|
+
def inherited_array_reader(constant_name)
|
188
|
+
inherited_reader(constant_name) do |result, current|
|
189
|
+
current + result
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def inherited_hash_reader(constant_name)
|
194
|
+
inherited_reader(constant_name) do |result, current|
|
195
|
+
result.merge(current)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
require_relative "utils"
|
3
|
+
|
4
|
+
module RSS
|
5
|
+
|
6
|
+
module XMLStyleSheetMixin
|
7
|
+
attr_accessor :xml_stylesheets
|
8
|
+
def initialize(*args)
|
9
|
+
super
|
10
|
+
@xml_stylesheets = []
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
def xml_stylesheet_pi
|
15
|
+
xsss = @xml_stylesheets.collect do |xss|
|
16
|
+
pi = xss.to_s
|
17
|
+
pi = nil if /\A\s*\z/ =~ pi
|
18
|
+
pi
|
19
|
+
end.compact
|
20
|
+
xsss.push("") unless xsss.empty?
|
21
|
+
xsss.join("\n")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class XMLStyleSheet
|
26
|
+
|
27
|
+
include Utils
|
28
|
+
|
29
|
+
ATTRIBUTES = %w(href type title media charset alternate)
|
30
|
+
|
31
|
+
GUESS_TABLE = {
|
32
|
+
"xsl" => "text/xsl",
|
33
|
+
"css" => "text/css",
|
34
|
+
}
|
35
|
+
|
36
|
+
attr_accessor(*ATTRIBUTES)
|
37
|
+
attr_accessor(:do_validate)
|
38
|
+
def initialize(*attrs)
|
39
|
+
if attrs.size == 1 and
|
40
|
+
(attrs.first.is_a?(Hash) or attrs.first.is_a?(Array))
|
41
|
+
attrs = attrs.first
|
42
|
+
end
|
43
|
+
@do_validate = true
|
44
|
+
ATTRIBUTES.each do |attr|
|
45
|
+
__send__("#{attr}=", nil)
|
46
|
+
end
|
47
|
+
vars = ATTRIBUTES.dup
|
48
|
+
vars.unshift(:do_validate)
|
49
|
+
attrs.each do |name, value|
|
50
|
+
if vars.include?(name.to_s)
|
51
|
+
__send__("#{name}=", value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s
|
57
|
+
rv = ""
|
58
|
+
if @href
|
59
|
+
rv << %Q[<?xml-stylesheet]
|
60
|
+
ATTRIBUTES.each do |name|
|
61
|
+
if __send__(name)
|
62
|
+
rv << %Q[ #{name}="#{h __send__(name)}"]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
rv << %Q[?>]
|
66
|
+
end
|
67
|
+
rv
|
68
|
+
end
|
69
|
+
|
70
|
+
remove_method(:href=)
|
71
|
+
def href=(value)
|
72
|
+
@href = value
|
73
|
+
if @href and @type.nil?
|
74
|
+
@type = guess_type(@href)
|
75
|
+
end
|
76
|
+
@href
|
77
|
+
end
|
78
|
+
|
79
|
+
remove_method(:alternate=)
|
80
|
+
def alternate=(value)
|
81
|
+
if value.nil? or /\A(?:yes|no)\z/ =~ value
|
82
|
+
@alternate = value
|
83
|
+
else
|
84
|
+
if @do_validate
|
85
|
+
args = ["?xml-stylesheet?", %Q[alternate="#{value}"]]
|
86
|
+
raise NotAvailableValueError.new(*args)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
@alternate
|
90
|
+
end
|
91
|
+
|
92
|
+
def setup_maker(maker)
|
93
|
+
xss = maker.xml_stylesheets.new_xml_stylesheet
|
94
|
+
ATTRIBUTES.each do |attr|
|
95
|
+
xss.__send__("#{attr}=", __send__(attr))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
def guess_type(filename)
|
101
|
+
/\.([^.]+)$/ =~ filename
|
102
|
+
GUESS_TABLE[$1]
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
data/lib/rss/xml.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
require_relative "utils"
|
3
|
+
|
4
|
+
module RSS
|
5
|
+
module XML
|
6
|
+
class Element
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
attr_reader :name, :prefix, :uri, :attributes, :children
|
10
|
+
def initialize(name, prefix=nil, uri=nil, attributes={}, children=[])
|
11
|
+
@name = name
|
12
|
+
@prefix = prefix
|
13
|
+
@uri = uri
|
14
|
+
@attributes = attributes
|
15
|
+
if children.is_a?(String) or !children.respond_to?(:each)
|
16
|
+
@children = [children]
|
17
|
+
else
|
18
|
+
@children = children
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def [](name)
|
23
|
+
@attributes[name]
|
24
|
+
end
|
25
|
+
|
26
|
+
def []=(name, value)
|
27
|
+
@attributes[name] = value
|
28
|
+
end
|
29
|
+
|
30
|
+
def <<(child)
|
31
|
+
@children << child
|
32
|
+
end
|
33
|
+
|
34
|
+
def each(&block)
|
35
|
+
@children.each(&block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def ==(other)
|
39
|
+
other.kind_of?(self.class) and
|
40
|
+
@name == other.name and
|
41
|
+
@uri == other.uri and
|
42
|
+
@attributes == other.attributes and
|
43
|
+
@children == other.children
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
rv = "<#{full_name}"
|
48
|
+
attributes.each do |key, value|
|
49
|
+
rv << " #{Utils.html_escape(key)}=\"#{Utils.html_escape(value)}\""
|
50
|
+
end
|
51
|
+
if children.empty?
|
52
|
+
rv << "/>"
|
53
|
+
else
|
54
|
+
rv << ">"
|
55
|
+
children.each do |child|
|
56
|
+
rv << child.to_s
|
57
|
+
end
|
58
|
+
rv << "</#{full_name}>"
|
59
|
+
end
|
60
|
+
rv
|
61
|
+
end
|
62
|
+
|
63
|
+
def full_name
|
64
|
+
if @prefix
|
65
|
+
"#{@prefix}:#{@name}"
|
66
|
+
else
|
67
|
+
@name
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
begin
|
3
|
+
require "xml/parser"
|
4
|
+
rescue LoadError
|
5
|
+
require "xmlparser"
|
6
|
+
end
|
7
|
+
|
8
|
+
begin
|
9
|
+
require "xml/encoding-ja"
|
10
|
+
rescue LoadError
|
11
|
+
require "xmlencoding-ja"
|
12
|
+
if defined?(Kconv)
|
13
|
+
module XMLEncoding_ja
|
14
|
+
class SJISHandler
|
15
|
+
include Kconv
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module XML
|
22
|
+
class Parser
|
23
|
+
unless defined?(Error)
|
24
|
+
# This error is legacy, so we just set it to the new one
|
25
|
+
Error = ::XMLParserError # :nodoc:
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
module RSS
|
31
|
+
|
32
|
+
class REXMLLikeXMLParser < ::XML::Parser
|
33
|
+
|
34
|
+
include ::XML::Encoding_ja
|
35
|
+
|
36
|
+
def listener=(listener)
|
37
|
+
@listener = listener
|
38
|
+
end
|
39
|
+
|
40
|
+
def startElement(name, attrs)
|
41
|
+
@listener.tag_start(name, attrs)
|
42
|
+
end
|
43
|
+
|
44
|
+
def endElement(name)
|
45
|
+
@listener.tag_end(name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def character(data)
|
49
|
+
@listener.text(data)
|
50
|
+
end
|
51
|
+
|
52
|
+
def xmlDecl(version, encoding, standalone)
|
53
|
+
@listener.xmldecl(version, encoding, standalone == 1)
|
54
|
+
end
|
55
|
+
|
56
|
+
def processingInstruction(target, content)
|
57
|
+
@listener.instruction(target, content)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
class XMLParserParser < BaseParser
|
63
|
+
|
64
|
+
class << self
|
65
|
+
def listener
|
66
|
+
XMLParserListener
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def _parse
|
72
|
+
begin
|
73
|
+
parser = REXMLLikeXMLParser.new
|
74
|
+
parser.listener = @listener
|
75
|
+
parser.parse(@rss)
|
76
|
+
rescue ::XML::Parser::Error => e
|
77
|
+
raise NotWellFormedError.new(parser.line){e.message}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
class XMLParserListener < BaseListener
|
84
|
+
|
85
|
+
include ListenerMixin
|
86
|
+
|
87
|
+
def xmldecl(version, encoding, standalone)
|
88
|
+
super
|
89
|
+
# Encoding is converted to UTF-8 when XMLParser parses XML.
|
90
|
+
@encoding = 'UTF-8'
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|