rubysl-rss 1.0.0
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 +17 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/rss.rb +1 -0
- data/lib/rss/0.9.rb +428 -0
- data/lib/rss/1.0.rb +452 -0
- data/lib/rss/2.0.rb +111 -0
- data/lib/rss/atom.rb +749 -0
- data/lib/rss/content.rb +31 -0
- data/lib/rss/content/1.0.rb +10 -0
- data/lib/rss/content/2.0.rb +12 -0
- data/lib/rss/converter.rb +162 -0
- data/lib/rss/dublincore.rb +161 -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 +193 -0
- data/lib/rss/itunes.rb +410 -0
- data/lib/rss/maker.rb +44 -0
- data/lib/rss/maker/0.9.rb +467 -0
- data/lib/rss/maker/1.0.rb +434 -0
- data/lib/rss/maker/2.0.rb +223 -0
- data/lib/rss/maker/atom.rb +172 -0
- data/lib/rss/maker/base.rb +868 -0
- data/lib/rss/maker/content.rb +21 -0
- data/lib/rss/maker/dublincore.rb +124 -0
- data/lib/rss/maker/entry.rb +163 -0
- data/lib/rss/maker/feed.rb +429 -0
- data/lib/rss/maker/image.rb +111 -0
- data/lib/rss/maker/itunes.rb +242 -0
- data/lib/rss/maker/slash.rb +33 -0
- data/lib/rss/maker/syndication.rb +18 -0
- data/lib/rss/maker/taxonomy.rb +118 -0
- data/lib/rss/maker/trackback.rb +61 -0
- data/lib/rss/parser.rb +541 -0
- data/lib/rss/rexmlparser.rb +54 -0
- data/lib/rss/rss.rb +1312 -0
- data/lib/rss/slash.rb +49 -0
- data/lib/rss/syndication.rb +67 -0
- data/lib/rss/taxonomy.rb +145 -0
- data/lib/rss/trackback.rb +288 -0
- data/lib/rss/utils.rb +111 -0
- data/lib/rss/xml-stylesheet.rb +105 -0
- data/lib/rss/xml.rb +71 -0
- data/lib/rss/xmlparser.rb +93 -0
- data/lib/rss/xmlscanner.rb +121 -0
- data/lib/rubysl/rss.rb +2 -0
- data/lib/rubysl/rss/rss.rb +19 -0
- data/lib/rubysl/rss/version.rb +5 -0
- data/rubysl-rss.gemspec +23 -0
- metadata +153 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
require "rexml/document"
|
2
|
+
require "rexml/streamlistener"
|
3
|
+
|
4
|
+
/\A(\d+)\.(\d+)(?:\.\d+)+\z/ =~ REXML::Version
|
5
|
+
if ([$1.to_i, $2.to_i] <=> [2, 5]) < 0
|
6
|
+
raise LoadError, "needs REXML 2.5 or later (#{REXML::Version})"
|
7
|
+
end
|
8
|
+
|
9
|
+
module RSS
|
10
|
+
|
11
|
+
class REXMLParser < BaseParser
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def listener
|
15
|
+
REXMLListener
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def _parse
|
21
|
+
begin
|
22
|
+
REXML::Document.parse_stream(@rss, @listener)
|
23
|
+
rescue RuntimeError => e
|
24
|
+
raise NotWellFormedError.new{e.message}
|
25
|
+
rescue REXML::ParseException => e
|
26
|
+
context = e.context
|
27
|
+
line = context[0] if context
|
28
|
+
raise NotWellFormedError.new(line){e.message}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
class REXMLListener < BaseListener
|
35
|
+
|
36
|
+
include REXML::StreamListener
|
37
|
+
include ListenerMixin
|
38
|
+
|
39
|
+
class << self
|
40
|
+
def raise_for_undefined_entity?
|
41
|
+
false
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def xmldecl(version, encoding, standalone)
|
46
|
+
super(version, encoding, standalone == "yes")
|
47
|
+
# Encoding is converted to UTF-8 when REXML parse XML.
|
48
|
+
@encoding = 'UTF-8'
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_method(:cdata, :text)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/lib/rss/rss.rb
ADDED
@@ -0,0 +1,1312 @@
|
|
1
|
+
require "time"
|
2
|
+
|
3
|
+
class Time
|
4
|
+
class << self
|
5
|
+
unless respond_to?(:w3cdtf)
|
6
|
+
def w3cdtf(date)
|
7
|
+
if /\A\s*
|
8
|
+
(-?\d+)-(\d\d)-(\d\d)
|
9
|
+
(?:T
|
10
|
+
(\d\d):(\d\d)(?::(\d\d))?
|
11
|
+
(\.\d+)?
|
12
|
+
(Z|[+-]\d\d:\d\d)?)?
|
13
|
+
\s*\z/ix =~ date and (($5 and $8) or (!$5 and !$8))
|
14
|
+
datetime = [$1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i]
|
15
|
+
usec = 0
|
16
|
+
usec = $7.to_f * 1000000 if $7
|
17
|
+
zone = $8
|
18
|
+
if zone
|
19
|
+
off = zone_offset(zone, datetime[0])
|
20
|
+
datetime = apply_offset(*(datetime + [off]))
|
21
|
+
datetime << usec
|
22
|
+
time = Time.utc(*datetime)
|
23
|
+
time.localtime unless zone_utc?(zone)
|
24
|
+
time
|
25
|
+
else
|
26
|
+
datetime << usec
|
27
|
+
Time.local(*datetime)
|
28
|
+
end
|
29
|
+
else
|
30
|
+
raise ArgumentError.new("invalid date: #{date.inspect}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
unless method_defined?(:w3cdtf)
|
37
|
+
def w3cdtf
|
38
|
+
if usec.zero?
|
39
|
+
fraction_digits = 0
|
40
|
+
else
|
41
|
+
fraction_digits = Math.log10(usec.to_s.sub(/0*$/, '').to_i).floor + 1
|
42
|
+
end
|
43
|
+
xmlschema(fraction_digits)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
require "English"
|
49
|
+
require "rss/utils"
|
50
|
+
require "rss/converter"
|
51
|
+
require "rss/xml-stylesheet"
|
52
|
+
|
53
|
+
module RSS
|
54
|
+
|
55
|
+
VERSION = "0.2.4"
|
56
|
+
|
57
|
+
URI = "http://purl.org/rss/1.0/"
|
58
|
+
|
59
|
+
DEBUG = false
|
60
|
+
|
61
|
+
class Error < StandardError; end
|
62
|
+
|
63
|
+
class OverlappedPrefixError < Error
|
64
|
+
attr_reader :prefix
|
65
|
+
def initialize(prefix)
|
66
|
+
@prefix = prefix
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class InvalidRSSError < Error; end
|
71
|
+
|
72
|
+
class MissingTagError < InvalidRSSError
|
73
|
+
attr_reader :tag, :parent
|
74
|
+
def initialize(tag, parent)
|
75
|
+
@tag, @parent = tag, parent
|
76
|
+
super("tag <#{tag}> is missing in tag <#{parent}>")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
class TooMuchTagError < InvalidRSSError
|
81
|
+
attr_reader :tag, :parent
|
82
|
+
def initialize(tag, parent)
|
83
|
+
@tag, @parent = tag, parent
|
84
|
+
super("tag <#{tag}> is too much in tag <#{parent}>")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class MissingAttributeError < InvalidRSSError
|
89
|
+
attr_reader :tag, :attribute
|
90
|
+
def initialize(tag, attribute)
|
91
|
+
@tag, @attribute = tag, attribute
|
92
|
+
super("attribute <#{attribute}> is missing in tag <#{tag}>")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class UnknownTagError < InvalidRSSError
|
97
|
+
attr_reader :tag, :uri
|
98
|
+
def initialize(tag, uri)
|
99
|
+
@tag, @uri = tag, uri
|
100
|
+
super("tag <#{tag}> is unknown in namespace specified by uri <#{uri}>")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class NotExpectedTagError < InvalidRSSError
|
105
|
+
attr_reader :tag, :uri, :parent
|
106
|
+
def initialize(tag, uri, parent)
|
107
|
+
@tag, @uri, @parent = tag, uri, parent
|
108
|
+
super("tag <{#{uri}}#{tag}> is not expected in tag <#{parent}>")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
# For backward compatibility :X
|
112
|
+
NotExceptedTagError = NotExpectedTagError
|
113
|
+
|
114
|
+
class NotAvailableValueError < InvalidRSSError
|
115
|
+
attr_reader :tag, :value, :attribute
|
116
|
+
def initialize(tag, value, attribute=nil)
|
117
|
+
@tag, @value, @attribute = tag, value, attribute
|
118
|
+
message = "value <#{value}> of "
|
119
|
+
message << "attribute <#{attribute}> of " if attribute
|
120
|
+
message << "tag <#{tag}> is not available."
|
121
|
+
super(message)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class UnknownConversionMethodError < Error
|
126
|
+
attr_reader :to, :from
|
127
|
+
def initialize(to, from)
|
128
|
+
@to = to
|
129
|
+
@from = from
|
130
|
+
super("can't convert to #{to} from #{from}.")
|
131
|
+
end
|
132
|
+
end
|
133
|
+
# for backward compatibility
|
134
|
+
UnknownConvertMethod = UnknownConversionMethodError
|
135
|
+
|
136
|
+
class ConversionError < Error
|
137
|
+
attr_reader :string, :to, :from
|
138
|
+
def initialize(string, to, from)
|
139
|
+
@string = string
|
140
|
+
@to = to
|
141
|
+
@from = from
|
142
|
+
super("can't convert #{@string} to #{to} from #{from}.")
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class NotSetError < Error
|
147
|
+
attr_reader :name, :variables
|
148
|
+
def initialize(name, variables)
|
149
|
+
@name = name
|
150
|
+
@variables = variables
|
151
|
+
super("required variables of #{@name} are not set: #{@variables.join(', ')}")
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class UnsupportedMakerVersionError < Error
|
156
|
+
attr_reader :version
|
157
|
+
def initialize(version)
|
158
|
+
@version = version
|
159
|
+
super("Maker doesn't support version: #{@version}")
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
module BaseModel
|
164
|
+
include Utils
|
165
|
+
|
166
|
+
def install_have_child_element(tag_name, uri, occurs, name=nil, type=nil)
|
167
|
+
name ||= tag_name
|
168
|
+
add_need_initialize_variable(name)
|
169
|
+
install_model(tag_name, uri, occurs, name)
|
170
|
+
|
171
|
+
writer_type, reader_type = type
|
172
|
+
def_corresponded_attr_writer name, writer_type
|
173
|
+
def_corresponded_attr_reader name, reader_type
|
174
|
+
install_element(name) do |n, elem_name|
|
175
|
+
<<-EOC
|
176
|
+
if @#{n}
|
177
|
+
"\#{@#{n}.to_s(need_convert, indent)}"
|
178
|
+
else
|
179
|
+
''
|
180
|
+
end
|
181
|
+
EOC
|
182
|
+
end
|
183
|
+
end
|
184
|
+
alias_method(:install_have_attribute_element, :install_have_child_element)
|
185
|
+
|
186
|
+
def install_have_children_element(tag_name, uri, occurs, name=nil, plural_name=nil)
|
187
|
+
name ||= tag_name
|
188
|
+
plural_name ||= "#{name}s"
|
189
|
+
add_have_children_element(name, plural_name)
|
190
|
+
add_plural_form(name, plural_name)
|
191
|
+
install_model(tag_name, uri, occurs, plural_name, true)
|
192
|
+
|
193
|
+
def_children_accessor(name, plural_name)
|
194
|
+
install_element(name, "s") do |n, elem_name|
|
195
|
+
<<-EOC
|
196
|
+
rv = []
|
197
|
+
@#{n}.each do |x|
|
198
|
+
value = "\#{x.to_s(need_convert, indent)}"
|
199
|
+
rv << value if /\\A\\s*\\z/ !~ value
|
200
|
+
end
|
201
|
+
rv.join("\n")
|
202
|
+
EOC
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def install_text_element(tag_name, uri, occurs, name=nil, type=nil,
|
207
|
+
disp_name=nil)
|
208
|
+
name ||= tag_name
|
209
|
+
disp_name ||= name
|
210
|
+
self::ELEMENTS << name unless self::ELEMENTS.include?(name)
|
211
|
+
add_need_initialize_variable(name)
|
212
|
+
install_model(tag_name, uri, occurs, name)
|
213
|
+
|
214
|
+
def_corresponded_attr_writer(name, type, disp_name)
|
215
|
+
def_corresponded_attr_reader(name, type || :convert)
|
216
|
+
install_element(name) do |n, elem_name|
|
217
|
+
<<-EOC
|
218
|
+
if respond_to?(:#{n}_content)
|
219
|
+
content = #{n}_content
|
220
|
+
else
|
221
|
+
content = @#{n}
|
222
|
+
end
|
223
|
+
if content
|
224
|
+
rv = "\#{indent}<#{elem_name}>"
|
225
|
+
value = html_escape(content)
|
226
|
+
if need_convert
|
227
|
+
rv << convert(value)
|
228
|
+
else
|
229
|
+
rv << value
|
230
|
+
end
|
231
|
+
rv << "</#{elem_name}>"
|
232
|
+
rv
|
233
|
+
else
|
234
|
+
''
|
235
|
+
end
|
236
|
+
EOC
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def install_date_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil)
|
241
|
+
name ||= tag_name
|
242
|
+
type ||= :w3cdtf
|
243
|
+
disp_name ||= name
|
244
|
+
self::ELEMENTS << name
|
245
|
+
add_need_initialize_variable(name)
|
246
|
+
install_model(tag_name, uri, occurs, name)
|
247
|
+
|
248
|
+
# accessor
|
249
|
+
convert_attr_reader name
|
250
|
+
date_writer(name, type, disp_name)
|
251
|
+
|
252
|
+
install_element(name) do |n, elem_name|
|
253
|
+
<<-EOC
|
254
|
+
if @#{n}
|
255
|
+
rv = "\#{indent}<#{elem_name}>"
|
256
|
+
value = html_escape(@#{n}.#{type})
|
257
|
+
if need_convert
|
258
|
+
rv << convert(value)
|
259
|
+
else
|
260
|
+
rv << value
|
261
|
+
end
|
262
|
+
rv << "</#{elem_name}>"
|
263
|
+
rv
|
264
|
+
else
|
265
|
+
''
|
266
|
+
end
|
267
|
+
EOC
|
268
|
+
end
|
269
|
+
|
270
|
+
end
|
271
|
+
|
272
|
+
private
|
273
|
+
def install_element(name, postfix="")
|
274
|
+
elem_name = name.sub('_', ':')
|
275
|
+
method_name = "#{name}_element#{postfix}"
|
276
|
+
add_to_element_method(method_name)
|
277
|
+
module_eval(<<-EOC, *get_file_and_line_from_caller(2))
|
278
|
+
def #{method_name}(need_convert=true, indent='')
|
279
|
+
#{yield(name, elem_name)}
|
280
|
+
end
|
281
|
+
private :#{method_name}
|
282
|
+
EOC
|
283
|
+
end
|
284
|
+
|
285
|
+
def inherit_convert_attr_reader(*attrs)
|
286
|
+
attrs.each do |attr|
|
287
|
+
attr = attr.id2name if attr.kind_of?(Integer)
|
288
|
+
module_eval(<<-EOC, *get_file_and_line_from_caller(2))
|
289
|
+
def #{attr}_without_inherit
|
290
|
+
convert(@#{attr})
|
291
|
+
end
|
292
|
+
|
293
|
+
def #{attr}
|
294
|
+
if @#{attr}
|
295
|
+
#{attr}_without_inherit
|
296
|
+
elsif @parent
|
297
|
+
@parent.#{attr}
|
298
|
+
else
|
299
|
+
nil
|
300
|
+
end
|
301
|
+
end
|
302
|
+
EOC
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def uri_convert_attr_reader(*attrs)
|
307
|
+
attrs.each do |attr|
|
308
|
+
attr = attr.id2name if attr.kind_of?(Integer)
|
309
|
+
module_eval(<<-EOC, *get_file_and_line_from_caller(2))
|
310
|
+
def #{attr}_without_base
|
311
|
+
convert(@#{attr})
|
312
|
+
end
|
313
|
+
|
314
|
+
def #{attr}
|
315
|
+
value = #{attr}_without_base
|
316
|
+
return nil if value.nil?
|
317
|
+
if /\\A[a-z][a-z0-9+.\\-]*:/i =~ value
|
318
|
+
value
|
319
|
+
else
|
320
|
+
"\#{base}\#{value}"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
EOC
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def convert_attr_reader(*attrs)
|
328
|
+
attrs.each do |attr|
|
329
|
+
attr = attr.id2name if attr.kind_of?(Integer)
|
330
|
+
module_eval(<<-EOC, *get_file_and_line_from_caller(2))
|
331
|
+
def #{attr}
|
332
|
+
convert(@#{attr})
|
333
|
+
end
|
334
|
+
EOC
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def yes_clean_other_attr_reader(*attrs)
|
339
|
+
attrs.each do |attr|
|
340
|
+
attr = attr.id2name if attr.kind_of?(Integer)
|
341
|
+
module_eval(<<-EOC, __FILE__, __LINE__ + 1)
|
342
|
+
attr_reader(:#{attr})
|
343
|
+
def #{attr}?
|
344
|
+
YesCleanOther.parse(@#{attr})
|
345
|
+
end
|
346
|
+
EOC
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
def yes_other_attr_reader(*attrs)
|
351
|
+
attrs.each do |attr|
|
352
|
+
attr = attr.id2name if attr.kind_of?(Integer)
|
353
|
+
module_eval(<<-EOC, __FILE__, __LINE__ + 1)
|
354
|
+
attr_reader(:#{attr})
|
355
|
+
def #{attr}?
|
356
|
+
Utils::YesOther.parse(@#{attr})
|
357
|
+
end
|
358
|
+
EOC
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
def csv_attr_reader(*attrs)
|
363
|
+
separator = nil
|
364
|
+
if attrs.last.is_a?(Hash)
|
365
|
+
options = attrs.pop
|
366
|
+
separator = options[:separator]
|
367
|
+
end
|
368
|
+
separator ||= ", "
|
369
|
+
attrs.each do |attr|
|
370
|
+
attr = attr.id2name if attr.kind_of?(Integer)
|
371
|
+
module_eval(<<-EOC, __FILE__, __LINE__ + 1)
|
372
|
+
attr_reader(:#{attr})
|
373
|
+
def #{attr}_content
|
374
|
+
if @#{attr}.nil?
|
375
|
+
@#{attr}
|
376
|
+
else
|
377
|
+
@#{attr}.join(#{separator.dump})
|
378
|
+
end
|
379
|
+
end
|
380
|
+
EOC
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
def date_writer(name, type, disp_name=name)
|
385
|
+
module_eval(<<-EOC, *get_file_and_line_from_caller(2))
|
386
|
+
def #{name}=(new_value)
|
387
|
+
if new_value.nil?
|
388
|
+
@#{name} = new_value
|
389
|
+
elsif new_value.kind_of?(Time)
|
390
|
+
@#{name} = new_value.dup
|
391
|
+
else
|
392
|
+
if @do_validate
|
393
|
+
begin
|
394
|
+
@#{name} = Time.__send__('#{type}', new_value)
|
395
|
+
rescue ArgumentError
|
396
|
+
raise NotAvailableValueError.new('#{disp_name}', new_value)
|
397
|
+
end
|
398
|
+
else
|
399
|
+
@#{name} = nil
|
400
|
+
if /\\A\\s*\\z/ !~ new_value.to_s
|
401
|
+
begin
|
402
|
+
unless Date._parse(new_value, false).empty?
|
403
|
+
@#{name} = Time.parse(new_value)
|
404
|
+
end
|
405
|
+
rescue ArgumentError
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
# Is it need?
|
412
|
+
if @#{name}
|
413
|
+
class << @#{name}
|
414
|
+
undef_method(:to_s)
|
415
|
+
alias_method(:to_s, :#{type})
|
416
|
+
end
|
417
|
+
end
|
418
|
+
|
419
|
+
end
|
420
|
+
EOC
|
421
|
+
end
|
422
|
+
|
423
|
+
def integer_writer(name, disp_name=name)
|
424
|
+
module_eval(<<-EOC, *get_file_and_line_from_caller(2))
|
425
|
+
def #{name}=(new_value)
|
426
|
+
if new_value.nil?
|
427
|
+
@#{name} = new_value
|
428
|
+
else
|
429
|
+
if @do_validate
|
430
|
+
begin
|
431
|
+
@#{name} = Integer(new_value)
|
432
|
+
rescue ArgumentError
|
433
|
+
raise NotAvailableValueError.new('#{disp_name}', new_value)
|
434
|
+
end
|
435
|
+
else
|
436
|
+
@#{name} = new_value.to_i
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|
440
|
+
EOC
|
441
|
+
end
|
442
|
+
|
443
|
+
def positive_integer_writer(name, disp_name=name)
|
444
|
+
module_eval(<<-EOC, *get_file_and_line_from_caller(2))
|
445
|
+
def #{name}=(new_value)
|
446
|
+
if new_value.nil?
|
447
|
+
@#{name} = new_value
|
448
|
+
else
|
449
|
+
if @do_validate
|
450
|
+
begin
|
451
|
+
tmp = Integer(new_value)
|
452
|
+
raise ArgumentError if tmp <= 0
|
453
|
+
@#{name} = tmp
|
454
|
+
rescue ArgumentError
|
455
|
+
raise NotAvailableValueError.new('#{disp_name}', new_value)
|
456
|
+
end
|
457
|
+
else
|
458
|
+
@#{name} = new_value.to_i
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
EOC
|
463
|
+
end
|
464
|
+
|
465
|
+
def boolean_writer(name, disp_name=name)
|
466
|
+
module_eval(<<-EOC, *get_file_and_line_from_caller(2))
|
467
|
+
def #{name}=(new_value)
|
468
|
+
if new_value.nil?
|
469
|
+
@#{name} = new_value
|
470
|
+
else
|
471
|
+
if @do_validate and
|
472
|
+
![true, false, "true", "false"].include?(new_value)
|
473
|
+
raise NotAvailableValueError.new('#{disp_name}', new_value)
|
474
|
+
end
|
475
|
+
if [true, false].include?(new_value)
|
476
|
+
@#{name} = new_value
|
477
|
+
else
|
478
|
+
@#{name} = new_value == "true"
|
479
|
+
end
|
480
|
+
end
|
481
|
+
end
|
482
|
+
EOC
|
483
|
+
end
|
484
|
+
|
485
|
+
def text_type_writer(name, disp_name=name)
|
486
|
+
module_eval(<<-EOC, *get_file_and_line_from_caller(2))
|
487
|
+
def #{name}=(new_value)
|
488
|
+
if @do_validate and
|
489
|
+
!["text", "html", "xhtml", nil].include?(new_value)
|
490
|
+
raise NotAvailableValueError.new('#{disp_name}', new_value)
|
491
|
+
end
|
492
|
+
@#{name} = new_value
|
493
|
+
end
|
494
|
+
EOC
|
495
|
+
end
|
496
|
+
|
497
|
+
def content_writer(name, disp_name=name)
|
498
|
+
klass_name = "self.class::#{Utils.to_class_name(name)}"
|
499
|
+
module_eval(<<-EOC, *get_file_and_line_from_caller(2))
|
500
|
+
def #{name}=(new_value)
|
501
|
+
if new_value.is_a?(#{klass_name})
|
502
|
+
@#{name} = new_value
|
503
|
+
else
|
504
|
+
@#{name} = #{klass_name}.new
|
505
|
+
@#{name}.content = new_value
|
506
|
+
end
|
507
|
+
end
|
508
|
+
EOC
|
509
|
+
end
|
510
|
+
|
511
|
+
def yes_clean_other_writer(name, disp_name=name)
|
512
|
+
module_eval(<<-EOC, __FILE__, __LINE__ + 1)
|
513
|
+
def #{name}=(value)
|
514
|
+
value = (value ? "yes" : "no") if [true, false].include?(value)
|
515
|
+
@#{name} = value
|
516
|
+
end
|
517
|
+
EOC
|
518
|
+
end
|
519
|
+
|
520
|
+
def yes_other_writer(name, disp_name=name)
|
521
|
+
module_eval(<<-EOC, __FILE__, __LINE__ + 1)
|
522
|
+
def #{name}=(new_value)
|
523
|
+
if [true, false].include?(new_value)
|
524
|
+
new_value = new_value ? "yes" : "no"
|
525
|
+
end
|
526
|
+
@#{name} = new_value
|
527
|
+
end
|
528
|
+
EOC
|
529
|
+
end
|
530
|
+
|
531
|
+
def csv_writer(name, disp_name=name)
|
532
|
+
module_eval(<<-EOC, __FILE__, __LINE__ + 1)
|
533
|
+
def #{name}=(new_value)
|
534
|
+
@#{name} = Utils::CSV.parse(new_value)
|
535
|
+
end
|
536
|
+
EOC
|
537
|
+
end
|
538
|
+
|
539
|
+
def csv_integer_writer(name, disp_name=name)
|
540
|
+
module_eval(<<-EOC, __FILE__, __LINE__ + 1)
|
541
|
+
def #{name}=(new_value)
|
542
|
+
@#{name} = Utils::CSV.parse(new_value) {|v| Integer(v)}
|
543
|
+
end
|
544
|
+
EOC
|
545
|
+
end
|
546
|
+
|
547
|
+
def def_children_accessor(accessor_name, plural_name)
|
548
|
+
module_eval(<<-EOC, *get_file_and_line_from_caller(2))
|
549
|
+
def #{plural_name}
|
550
|
+
@#{accessor_name}
|
551
|
+
end
|
552
|
+
|
553
|
+
def #{accessor_name}(*args)
|
554
|
+
if args.empty?
|
555
|
+
@#{accessor_name}.first
|
556
|
+
else
|
557
|
+
@#{accessor_name}[*args]
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
def #{accessor_name}=(*args)
|
562
|
+
receiver = self.class.name
|
563
|
+
warn("Warning:\#{caller.first.sub(/:in `.*'\z/, '')}: " \
|
564
|
+
"Don't use `\#{receiver}\##{accessor_name} = XXX'/" \
|
565
|
+
"`\#{receiver}\#set_#{accessor_name}(XXX)'. " \
|
566
|
+
"Those APIs are not sense of Ruby. " \
|
567
|
+
"Use `\#{receiver}\##{plural_name} << XXX' instead of them.")
|
568
|
+
if args.size == 1
|
569
|
+
@#{accessor_name}.push(args[0])
|
570
|
+
else
|
571
|
+
@#{accessor_name}.__send__("[]=", *args)
|
572
|
+
end
|
573
|
+
end
|
574
|
+
alias_method(:set_#{accessor_name}, :#{accessor_name}=)
|
575
|
+
EOC
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
module SetupMaker
|
580
|
+
def setup_maker(maker)
|
581
|
+
target = maker_target(maker)
|
582
|
+
unless target.nil?
|
583
|
+
setup_maker_attributes(target)
|
584
|
+
setup_maker_element(target)
|
585
|
+
setup_maker_elements(target)
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
private
|
590
|
+
def maker_target(maker)
|
591
|
+
nil
|
592
|
+
end
|
593
|
+
|
594
|
+
def setup_maker_attributes(target)
|
595
|
+
end
|
596
|
+
|
597
|
+
def setup_maker_element(target)
|
598
|
+
self.class.need_initialize_variables.each do |var|
|
599
|
+
value = __send__(var)
|
600
|
+
next if value.nil?
|
601
|
+
if value.respond_to?("setup_maker") and
|
602
|
+
!not_need_to_call_setup_maker_variables.include?(var)
|
603
|
+
value.setup_maker(target)
|
604
|
+
else
|
605
|
+
setter = "#{var}="
|
606
|
+
if target.respond_to?(setter)
|
607
|
+
target.__send__(setter, value)
|
608
|
+
end
|
609
|
+
end
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
def not_need_to_call_setup_maker_variables
|
614
|
+
[]
|
615
|
+
end
|
616
|
+
|
617
|
+
def setup_maker_elements(parent)
|
618
|
+
self.class.have_children_elements.each do |name, plural_name|
|
619
|
+
if parent.respond_to?(plural_name)
|
620
|
+
target = parent.__send__(plural_name)
|
621
|
+
__send__(plural_name).each do |elem|
|
622
|
+
elem.setup_maker(target)
|
623
|
+
end
|
624
|
+
end
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
class Element
|
630
|
+
extend BaseModel
|
631
|
+
include Utils
|
632
|
+
extend Utils::InheritedReader
|
633
|
+
include SetupMaker
|
634
|
+
|
635
|
+
INDENT = " "
|
636
|
+
|
637
|
+
MUST_CALL_VALIDATORS = {}
|
638
|
+
MODELS = []
|
639
|
+
GET_ATTRIBUTES = []
|
640
|
+
HAVE_CHILDREN_ELEMENTS = []
|
641
|
+
TO_ELEMENT_METHODS = []
|
642
|
+
NEED_INITIALIZE_VARIABLES = []
|
643
|
+
PLURAL_FORMS = {}
|
644
|
+
|
645
|
+
class << self
|
646
|
+
def must_call_validators
|
647
|
+
inherited_hash_reader("MUST_CALL_VALIDATORS")
|
648
|
+
end
|
649
|
+
def models
|
650
|
+
inherited_array_reader("MODELS")
|
651
|
+
end
|
652
|
+
def get_attributes
|
653
|
+
inherited_array_reader("GET_ATTRIBUTES")
|
654
|
+
end
|
655
|
+
def have_children_elements
|
656
|
+
inherited_array_reader("HAVE_CHILDREN_ELEMENTS")
|
657
|
+
end
|
658
|
+
def to_element_methods
|
659
|
+
inherited_array_reader("TO_ELEMENT_METHODS")
|
660
|
+
end
|
661
|
+
def need_initialize_variables
|
662
|
+
inherited_array_reader("NEED_INITIALIZE_VARIABLES")
|
663
|
+
end
|
664
|
+
def plural_forms
|
665
|
+
inherited_hash_reader("PLURAL_FORMS")
|
666
|
+
end
|
667
|
+
|
668
|
+
def inherited_base
|
669
|
+
::RSS::Element
|
670
|
+
end
|
671
|
+
|
672
|
+
def inherited(klass)
|
673
|
+
klass.const_set("MUST_CALL_VALIDATORS", {})
|
674
|
+
klass.const_set("MODELS", [])
|
675
|
+
klass.const_set("GET_ATTRIBUTES", [])
|
676
|
+
klass.const_set("HAVE_CHILDREN_ELEMENTS", [])
|
677
|
+
klass.const_set("TO_ELEMENT_METHODS", [])
|
678
|
+
klass.const_set("NEED_INITIALIZE_VARIABLES", [])
|
679
|
+
klass.const_set("PLURAL_FORMS", {})
|
680
|
+
|
681
|
+
tag_name = klass.name.split(/::/).last
|
682
|
+
tag_name[0, 1] = tag_name[0, 1].downcase
|
683
|
+
klass.instance_variable_set("@tag_name", tag_name)
|
684
|
+
klass.instance_variable_set("@have_content", false)
|
685
|
+
end
|
686
|
+
|
687
|
+
def install_must_call_validator(prefix, uri)
|
688
|
+
self::MUST_CALL_VALIDATORS[uri] = prefix
|
689
|
+
end
|
690
|
+
|
691
|
+
def install_model(tag, uri, occurs=nil, getter=nil, plural=false)
|
692
|
+
getter ||= tag
|
693
|
+
if m = self::MODELS.find {|t, u, o, g, p| t == tag and u == uri}
|
694
|
+
m[2] = occurs
|
695
|
+
else
|
696
|
+
self::MODELS << [tag, uri, occurs, getter, plural]
|
697
|
+
end
|
698
|
+
end
|
699
|
+
|
700
|
+
def install_get_attribute(name, uri, required=true,
|
701
|
+
type=nil, disp_name=nil,
|
702
|
+
element_name=nil)
|
703
|
+
disp_name ||= name
|
704
|
+
element_name ||= name
|
705
|
+
writer_type, reader_type = type
|
706
|
+
def_corresponded_attr_writer name, writer_type, disp_name
|
707
|
+
def_corresponded_attr_reader name, reader_type
|
708
|
+
if type == :boolean and /^is/ =~ name
|
709
|
+
alias_method "#{$'}?", name
|
710
|
+
end
|
711
|
+
self::GET_ATTRIBUTES << [name, uri, required, element_name]
|
712
|
+
add_need_initialize_variable(disp_name)
|
713
|
+
end
|
714
|
+
|
715
|
+
def def_corresponded_attr_writer(name, type=nil, disp_name=nil)
|
716
|
+
disp_name ||= name
|
717
|
+
case type
|
718
|
+
when :integer
|
719
|
+
integer_writer name, disp_name
|
720
|
+
when :positive_integer
|
721
|
+
positive_integer_writer name, disp_name
|
722
|
+
when :boolean
|
723
|
+
boolean_writer name, disp_name
|
724
|
+
when :w3cdtf, :rfc822, :rfc2822
|
725
|
+
date_writer name, type, disp_name
|
726
|
+
when :text_type
|
727
|
+
text_type_writer name, disp_name
|
728
|
+
when :content
|
729
|
+
content_writer name, disp_name
|
730
|
+
when :yes_clean_other
|
731
|
+
yes_clean_other_writer name, disp_name
|
732
|
+
when :yes_other
|
733
|
+
yes_other_writer name, disp_name
|
734
|
+
when :csv
|
735
|
+
csv_writer name
|
736
|
+
when :csv_integer
|
737
|
+
csv_integer_writer name
|
738
|
+
else
|
739
|
+
attr_writer name
|
740
|
+
end
|
741
|
+
end
|
742
|
+
|
743
|
+
def def_corresponded_attr_reader(name, type=nil)
|
744
|
+
case type
|
745
|
+
when :inherit
|
746
|
+
inherit_convert_attr_reader name
|
747
|
+
when :uri
|
748
|
+
uri_convert_attr_reader name
|
749
|
+
when :yes_clean_other
|
750
|
+
yes_clean_other_attr_reader name
|
751
|
+
when :yes_other
|
752
|
+
yes_other_attr_reader name
|
753
|
+
when :csv
|
754
|
+
csv_attr_reader name
|
755
|
+
when :csv_integer
|
756
|
+
csv_attr_reader name, :separator => ","
|
757
|
+
else
|
758
|
+
convert_attr_reader name
|
759
|
+
end
|
760
|
+
end
|
761
|
+
|
762
|
+
def content_setup(type=nil, disp_name=nil)
|
763
|
+
writer_type, reader_type = type
|
764
|
+
def_corresponded_attr_writer :content, writer_type, disp_name
|
765
|
+
def_corresponded_attr_reader :content, reader_type
|
766
|
+
@have_content = true
|
767
|
+
end
|
768
|
+
|
769
|
+
def have_content?
|
770
|
+
@have_content
|
771
|
+
end
|
772
|
+
|
773
|
+
def add_have_children_element(variable_name, plural_name)
|
774
|
+
self::HAVE_CHILDREN_ELEMENTS << [variable_name, plural_name]
|
775
|
+
end
|
776
|
+
|
777
|
+
def add_to_element_method(method_name)
|
778
|
+
self::TO_ELEMENT_METHODS << method_name
|
779
|
+
end
|
780
|
+
|
781
|
+
def add_need_initialize_variable(variable_name)
|
782
|
+
self::NEED_INITIALIZE_VARIABLES << variable_name
|
783
|
+
end
|
784
|
+
|
785
|
+
def add_plural_form(singular, plural)
|
786
|
+
self::PLURAL_FORMS[singular] = plural
|
787
|
+
end
|
788
|
+
|
789
|
+
def required_prefix
|
790
|
+
nil
|
791
|
+
end
|
792
|
+
|
793
|
+
def required_uri
|
794
|
+
""
|
795
|
+
end
|
796
|
+
|
797
|
+
def need_parent?
|
798
|
+
false
|
799
|
+
end
|
800
|
+
|
801
|
+
def install_ns(prefix, uri)
|
802
|
+
if self::NSPOOL.has_key?(prefix)
|
803
|
+
raise OverlappedPrefixError.new(prefix)
|
804
|
+
end
|
805
|
+
self::NSPOOL[prefix] = uri
|
806
|
+
end
|
807
|
+
|
808
|
+
def tag_name
|
809
|
+
@tag_name
|
810
|
+
end
|
811
|
+
end
|
812
|
+
|
813
|
+
attr_accessor :parent, :do_validate
|
814
|
+
|
815
|
+
def initialize(do_validate=true, attrs=nil)
|
816
|
+
@parent = nil
|
817
|
+
@converter = nil
|
818
|
+
if attrs.nil? and (do_validate.is_a?(Hash) or do_validate.is_a?(Array))
|
819
|
+
do_validate, attrs = true, do_validate
|
820
|
+
end
|
821
|
+
@do_validate = do_validate
|
822
|
+
initialize_variables(attrs || {})
|
823
|
+
end
|
824
|
+
|
825
|
+
def tag_name
|
826
|
+
self.class.tag_name
|
827
|
+
end
|
828
|
+
|
829
|
+
def full_name
|
830
|
+
tag_name
|
831
|
+
end
|
832
|
+
|
833
|
+
def converter=(converter)
|
834
|
+
@converter = converter
|
835
|
+
targets = children.dup
|
836
|
+
self.class.have_children_elements.each do |variable_name, plural_name|
|
837
|
+
targets.concat(__send__(plural_name))
|
838
|
+
end
|
839
|
+
targets.each do |target|
|
840
|
+
target.converter = converter unless target.nil?
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
844
|
+
def convert(value)
|
845
|
+
if @converter
|
846
|
+
@converter.convert(value)
|
847
|
+
else
|
848
|
+
value
|
849
|
+
end
|
850
|
+
end
|
851
|
+
|
852
|
+
def valid?(ignore_unknown_element=true)
|
853
|
+
validate(ignore_unknown_element)
|
854
|
+
true
|
855
|
+
rescue RSS::Error
|
856
|
+
false
|
857
|
+
end
|
858
|
+
|
859
|
+
def validate(ignore_unknown_element=true)
|
860
|
+
do_validate = @do_validate
|
861
|
+
@do_validate = true
|
862
|
+
validate_attribute
|
863
|
+
__validate(ignore_unknown_element)
|
864
|
+
ensure
|
865
|
+
@do_validate = do_validate
|
866
|
+
end
|
867
|
+
|
868
|
+
def validate_for_stream(tags, ignore_unknown_element=true)
|
869
|
+
validate_attribute
|
870
|
+
__validate(ignore_unknown_element, tags, false)
|
871
|
+
end
|
872
|
+
|
873
|
+
def to_s(need_convert=true, indent='')
|
874
|
+
if self.class.have_content?
|
875
|
+
return "" if !empty_content? and !content_is_set?
|
876
|
+
rv = tag(indent) do |next_indent|
|
877
|
+
if empty_content?
|
878
|
+
""
|
879
|
+
else
|
880
|
+
xmled_content
|
881
|
+
end
|
882
|
+
end
|
883
|
+
else
|
884
|
+
rv = tag(indent) do |next_indent|
|
885
|
+
self.class.to_element_methods.collect do |method_name|
|
886
|
+
__send__(method_name, false, next_indent)
|
887
|
+
end
|
888
|
+
end
|
889
|
+
end
|
890
|
+
rv = convert(rv) if need_convert
|
891
|
+
rv
|
892
|
+
end
|
893
|
+
|
894
|
+
def have_xml_content?
|
895
|
+
false
|
896
|
+
end
|
897
|
+
|
898
|
+
def need_base64_encode?
|
899
|
+
false
|
900
|
+
end
|
901
|
+
|
902
|
+
def set_next_element(tag_name, next_element)
|
903
|
+
klass = next_element.class
|
904
|
+
prefix = ""
|
905
|
+
prefix << "#{klass.required_prefix}_" if klass.required_prefix
|
906
|
+
key = "#{prefix}#{tag_name.gsub(/-/, '_')}"
|
907
|
+
if self.class.plural_forms.has_key?(key)
|
908
|
+
ary = __send__("#{self.class.plural_forms[key]}")
|
909
|
+
ary << next_element
|
910
|
+
else
|
911
|
+
__send__("#{key}=", next_element)
|
912
|
+
end
|
913
|
+
end
|
914
|
+
|
915
|
+
protected
|
916
|
+
def have_required_elements?
|
917
|
+
self.class::MODELS.all? do |tag, uri, occurs, getter|
|
918
|
+
if occurs.nil? or occurs == "+"
|
919
|
+
child = __send__(getter)
|
920
|
+
if child.is_a?(Array)
|
921
|
+
children = child
|
922
|
+
children.any? {|c| c.have_required_elements?}
|
923
|
+
else
|
924
|
+
!child.to_s.empty?
|
925
|
+
end
|
926
|
+
else
|
927
|
+
true
|
928
|
+
end
|
929
|
+
end
|
930
|
+
end
|
931
|
+
|
932
|
+
private
|
933
|
+
def initialize_variables(attrs)
|
934
|
+
normalized_attrs = {}
|
935
|
+
attrs.each do |key, value|
|
936
|
+
normalized_attrs[key.to_s] = value
|
937
|
+
end
|
938
|
+
self.class.need_initialize_variables.each do |variable_name|
|
939
|
+
value = normalized_attrs[variable_name.to_s]
|
940
|
+
if value
|
941
|
+
__send__("#{variable_name}=", value)
|
942
|
+
else
|
943
|
+
instance_variable_set("@#{variable_name}", nil)
|
944
|
+
end
|
945
|
+
end
|
946
|
+
initialize_have_children_elements
|
947
|
+
@content = normalized_attrs["content"] if self.class.have_content?
|
948
|
+
end
|
949
|
+
|
950
|
+
def initialize_have_children_elements
|
951
|
+
self.class.have_children_elements.each do |variable_name, plural_name|
|
952
|
+
instance_variable_set("@#{variable_name}", [])
|
953
|
+
end
|
954
|
+
end
|
955
|
+
|
956
|
+
def tag(indent, additional_attrs={}, &block)
|
957
|
+
next_indent = indent + INDENT
|
958
|
+
|
959
|
+
attrs = collect_attrs
|
960
|
+
return "" if attrs.nil?
|
961
|
+
|
962
|
+
return "" unless have_required_elements?
|
963
|
+
|
964
|
+
attrs.update(additional_attrs)
|
965
|
+
start_tag = make_start_tag(indent, next_indent, attrs.dup)
|
966
|
+
|
967
|
+
if block
|
968
|
+
content = block.call(next_indent)
|
969
|
+
else
|
970
|
+
content = []
|
971
|
+
end
|
972
|
+
|
973
|
+
if content.is_a?(String)
|
974
|
+
content = [content]
|
975
|
+
start_tag << ">"
|
976
|
+
end_tag = "</#{full_name}>"
|
977
|
+
else
|
978
|
+
content = content.reject{|x| x.empty?}
|
979
|
+
if content.empty?
|
980
|
+
return "" if attrs.empty?
|
981
|
+
end_tag = "/>"
|
982
|
+
else
|
983
|
+
start_tag << ">\n"
|
984
|
+
end_tag = "\n#{indent}</#{full_name}>"
|
985
|
+
end
|
986
|
+
end
|
987
|
+
|
988
|
+
start_tag + content.join("\n") + end_tag
|
989
|
+
end
|
990
|
+
|
991
|
+
def make_start_tag(indent, next_indent, attrs)
|
992
|
+
start_tag = ["#{indent}<#{full_name}"]
|
993
|
+
unless attrs.empty?
|
994
|
+
start_tag << attrs.collect do |key, value|
|
995
|
+
%Q[#{h key}="#{h value}"]
|
996
|
+
end.join("\n#{next_indent}")
|
997
|
+
end
|
998
|
+
start_tag.join(" ")
|
999
|
+
end
|
1000
|
+
|
1001
|
+
def collect_attrs
|
1002
|
+
attrs = {}
|
1003
|
+
_attrs.each do |name, required, alias_name|
|
1004
|
+
value = __send__(alias_name || name)
|
1005
|
+
return nil if required and value.nil?
|
1006
|
+
next if value.nil?
|
1007
|
+
return nil if attrs.has_key?(name)
|
1008
|
+
attrs[name] = value
|
1009
|
+
end
|
1010
|
+
attrs
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
def tag_name_with_prefix(prefix)
|
1014
|
+
"#{prefix}:#{tag_name}"
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
# For backward compatibility
|
1018
|
+
def calc_indent
|
1019
|
+
''
|
1020
|
+
end
|
1021
|
+
|
1022
|
+
def children
|
1023
|
+
rv = []
|
1024
|
+
self.class.models.each do |name, uri, occurs, getter|
|
1025
|
+
value = __send__(getter)
|
1026
|
+
next if value.nil?
|
1027
|
+
value = [value] unless value.is_a?(Array)
|
1028
|
+
value.each do |v|
|
1029
|
+
rv << v if v.is_a?(Element)
|
1030
|
+
end
|
1031
|
+
end
|
1032
|
+
rv
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
def _tags
|
1036
|
+
rv = []
|
1037
|
+
self.class.models.each do |name, uri, occurs, getter, plural|
|
1038
|
+
value = __send__(getter)
|
1039
|
+
next if value.nil?
|
1040
|
+
if plural and value.is_a?(Array)
|
1041
|
+
rv.concat([[uri, name]] * value.size)
|
1042
|
+
else
|
1043
|
+
rv << [uri, name]
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
rv
|
1047
|
+
end
|
1048
|
+
|
1049
|
+
def _attrs
|
1050
|
+
self.class.get_attributes.collect do |name, uri, required, element_name|
|
1051
|
+
[element_name, required, name]
|
1052
|
+
end
|
1053
|
+
end
|
1054
|
+
|
1055
|
+
def __validate(ignore_unknown_element, tags=_tags, recursive=true)
|
1056
|
+
if recursive
|
1057
|
+
children.compact.each do |child|
|
1058
|
+
child.validate
|
1059
|
+
end
|
1060
|
+
end
|
1061
|
+
must_call_validators = self.class.must_call_validators
|
1062
|
+
tags = tag_filter(tags.dup)
|
1063
|
+
p tags if DEBUG
|
1064
|
+
must_call_validators.each do |uri, prefix|
|
1065
|
+
_validate(ignore_unknown_element, tags[uri], uri)
|
1066
|
+
meth = "#{prefix}_validate"
|
1067
|
+
if !prefix.empty? and respond_to?(meth, true)
|
1068
|
+
__send__(meth, ignore_unknown_element, tags[uri], uri)
|
1069
|
+
end
|
1070
|
+
end
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
def validate_attribute
|
1074
|
+
_attrs.each do |a_name, required, alias_name|
|
1075
|
+
value = instance_variable_get("@#{alias_name || a_name}")
|
1076
|
+
if required and value.nil?
|
1077
|
+
raise MissingAttributeError.new(tag_name, a_name)
|
1078
|
+
end
|
1079
|
+
__send__("#{alias_name || a_name}=", value)
|
1080
|
+
end
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
def _validate(ignore_unknown_element, tags, uri, models=self.class.models)
|
1084
|
+
count = 1
|
1085
|
+
do_redo = false
|
1086
|
+
not_shift = false
|
1087
|
+
tag = nil
|
1088
|
+
models = models.find_all {|model| model[1] == uri}
|
1089
|
+
element_names = models.collect {|model| model[0]}
|
1090
|
+
if tags
|
1091
|
+
tags_size = tags.size
|
1092
|
+
tags = tags.sort_by {|x| element_names.index(x) || tags_size}
|
1093
|
+
end
|
1094
|
+
|
1095
|
+
_tags = tags.dup if tags
|
1096
|
+
models.each_with_index do |model, i|
|
1097
|
+
name, model_uri, occurs, getter = model
|
1098
|
+
|
1099
|
+
if DEBUG
|
1100
|
+
p "before"
|
1101
|
+
p tags
|
1102
|
+
p model
|
1103
|
+
end
|
1104
|
+
|
1105
|
+
if not_shift
|
1106
|
+
not_shift = false
|
1107
|
+
elsif tags
|
1108
|
+
tag = tags.shift
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
if DEBUG
|
1112
|
+
p "mid"
|
1113
|
+
p count
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
case occurs
|
1117
|
+
when '?'
|
1118
|
+
if count > 2
|
1119
|
+
raise TooMuchTagError.new(name, tag_name)
|
1120
|
+
else
|
1121
|
+
if name == tag
|
1122
|
+
do_redo = true
|
1123
|
+
else
|
1124
|
+
not_shift = true
|
1125
|
+
end
|
1126
|
+
end
|
1127
|
+
when '*'
|
1128
|
+
if name == tag
|
1129
|
+
do_redo = true
|
1130
|
+
else
|
1131
|
+
not_shift = true
|
1132
|
+
end
|
1133
|
+
when '+'
|
1134
|
+
if name == tag
|
1135
|
+
do_redo = true
|
1136
|
+
else
|
1137
|
+
if count > 1
|
1138
|
+
not_shift = true
|
1139
|
+
else
|
1140
|
+
raise MissingTagError.new(name, tag_name)
|
1141
|
+
end
|
1142
|
+
end
|
1143
|
+
else
|
1144
|
+
if name == tag
|
1145
|
+
if models[i+1] and models[i+1][0] != name and
|
1146
|
+
tags and tags.first == name
|
1147
|
+
raise TooMuchTagError.new(name, tag_name)
|
1148
|
+
end
|
1149
|
+
else
|
1150
|
+
raise MissingTagError.new(name, tag_name)
|
1151
|
+
end
|
1152
|
+
end
|
1153
|
+
|
1154
|
+
if DEBUG
|
1155
|
+
p "after"
|
1156
|
+
p not_shift
|
1157
|
+
p do_redo
|
1158
|
+
p tag
|
1159
|
+
end
|
1160
|
+
|
1161
|
+
if do_redo
|
1162
|
+
do_redo = false
|
1163
|
+
count += 1
|
1164
|
+
redo
|
1165
|
+
else
|
1166
|
+
count = 1
|
1167
|
+
end
|
1168
|
+
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
if !ignore_unknown_element and !tags.nil? and !tags.empty?
|
1172
|
+
raise NotExpectedTagError.new(tags.first, uri, tag_name)
|
1173
|
+
end
|
1174
|
+
|
1175
|
+
end
|
1176
|
+
|
1177
|
+
def tag_filter(tags)
|
1178
|
+
rv = {}
|
1179
|
+
tags.each do |tag|
|
1180
|
+
rv[tag[0]] = [] unless rv.has_key?(tag[0])
|
1181
|
+
rv[tag[0]].push(tag[1])
|
1182
|
+
end
|
1183
|
+
rv
|
1184
|
+
end
|
1185
|
+
|
1186
|
+
def empty_content?
|
1187
|
+
false
|
1188
|
+
end
|
1189
|
+
|
1190
|
+
def content_is_set?
|
1191
|
+
if have_xml_content?
|
1192
|
+
__send__(self.class.xml_getter)
|
1193
|
+
else
|
1194
|
+
content
|
1195
|
+
end
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
def xmled_content
|
1199
|
+
if have_xml_content?
|
1200
|
+
__send__(self.class.xml_getter).to_s
|
1201
|
+
else
|
1202
|
+
_content = content
|
1203
|
+
_content = Base64.encode64(_content) if need_base64_encode?
|
1204
|
+
h(_content)
|
1205
|
+
end
|
1206
|
+
end
|
1207
|
+
end
|
1208
|
+
|
1209
|
+
module RootElementMixin
|
1210
|
+
|
1211
|
+
include XMLStyleSheetMixin
|
1212
|
+
|
1213
|
+
attr_reader :output_encoding
|
1214
|
+
attr_reader :feed_type, :feed_subtype, :feed_version
|
1215
|
+
attr_accessor :version, :encoding, :standalone
|
1216
|
+
def initialize(feed_version, version=nil, encoding=nil, standalone=nil)
|
1217
|
+
super()
|
1218
|
+
@feed_type = nil
|
1219
|
+
@feed_subtype = nil
|
1220
|
+
@feed_version = feed_version
|
1221
|
+
@version = version || '1.0'
|
1222
|
+
@encoding = encoding
|
1223
|
+
@standalone = standalone
|
1224
|
+
@output_encoding = nil
|
1225
|
+
end
|
1226
|
+
|
1227
|
+
def feed_info
|
1228
|
+
[@feed_type, @feed_version, @feed_subtype]
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
def output_encoding=(enc)
|
1232
|
+
@output_encoding = enc
|
1233
|
+
self.converter = Converter.new(@output_encoding, @encoding)
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
def setup_maker(maker)
|
1237
|
+
maker.version = version
|
1238
|
+
maker.encoding = encoding
|
1239
|
+
maker.standalone = standalone
|
1240
|
+
|
1241
|
+
xml_stylesheets.each do |xss|
|
1242
|
+
xss.setup_maker(maker)
|
1243
|
+
end
|
1244
|
+
|
1245
|
+
super
|
1246
|
+
end
|
1247
|
+
|
1248
|
+
def to_feed(type, &block)
|
1249
|
+
Maker.make(type) do |maker|
|
1250
|
+
setup_maker(maker)
|
1251
|
+
block.call(maker) if block
|
1252
|
+
end
|
1253
|
+
end
|
1254
|
+
|
1255
|
+
def to_rss(type, &block)
|
1256
|
+
to_feed("rss#{type}", &block)
|
1257
|
+
end
|
1258
|
+
|
1259
|
+
def to_atom(type, &block)
|
1260
|
+
to_feed("atom:#{type}", &block)
|
1261
|
+
end
|
1262
|
+
|
1263
|
+
def to_xml(type=nil, &block)
|
1264
|
+
if type.nil? or same_feed_type?(type)
|
1265
|
+
to_s
|
1266
|
+
else
|
1267
|
+
to_feed(type, &block).to_s
|
1268
|
+
end
|
1269
|
+
end
|
1270
|
+
|
1271
|
+
private
|
1272
|
+
def same_feed_type?(type)
|
1273
|
+
if /^(atom|rss)?(\d+\.\d+)?(?::(.+))?$/i =~ type
|
1274
|
+
feed_type = ($1 || @feed_type).downcase
|
1275
|
+
feed_version = $2 || @feed_version
|
1276
|
+
feed_subtype = $3 || @feed_subtype
|
1277
|
+
[feed_type, feed_version, feed_subtype] == feed_info
|
1278
|
+
else
|
1279
|
+
false
|
1280
|
+
end
|
1281
|
+
end
|
1282
|
+
|
1283
|
+
def tag(indent, attrs={}, &block)
|
1284
|
+
rv = super(indent, ns_declarations.merge(attrs), &block)
|
1285
|
+
return rv if rv.empty?
|
1286
|
+
"#{xmldecl}#{xml_stylesheet_pi}#{rv}"
|
1287
|
+
end
|
1288
|
+
|
1289
|
+
def xmldecl
|
1290
|
+
rv = %Q[<?xml version="#{@version}"]
|
1291
|
+
if @output_encoding or @encoding
|
1292
|
+
rv << %Q[ encoding="#{@output_encoding or @encoding}"]
|
1293
|
+
end
|
1294
|
+
rv << %Q[ standalone="yes"] if @standalone
|
1295
|
+
rv << "?>\n"
|
1296
|
+
rv
|
1297
|
+
end
|
1298
|
+
|
1299
|
+
def ns_declarations
|
1300
|
+
decls = {}
|
1301
|
+
self.class::NSPOOL.collect do |prefix, uri|
|
1302
|
+
prefix = ":#{prefix}" unless prefix.empty?
|
1303
|
+
decls["xmlns#{prefix}"] = uri
|
1304
|
+
end
|
1305
|
+
decls
|
1306
|
+
end
|
1307
|
+
|
1308
|
+
def maker_target(target)
|
1309
|
+
target
|
1310
|
+
end
|
1311
|
+
end
|
1312
|
+
end
|