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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +88 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +8 -0
  10. data/lib/rss.rb +92 -0
  11. data/lib/rss/0.9.rb +462 -0
  12. data/lib/rss/1.0.rb +485 -0
  13. data/lib/rss/2.0.rb +143 -0
  14. data/lib/rss/atom.rb +1025 -0
  15. data/lib/rss/content.rb +34 -0
  16. data/lib/rss/content/1.0.rb +10 -0
  17. data/lib/rss/content/2.0.rb +12 -0
  18. data/lib/rss/converter.rb +171 -0
  19. data/lib/rss/dublincore.rb +164 -0
  20. data/lib/rss/dublincore/1.0.rb +13 -0
  21. data/lib/rss/dublincore/2.0.rb +13 -0
  22. data/lib/rss/dublincore/atom.rb +17 -0
  23. data/lib/rss/image.rb +198 -0
  24. data/lib/rss/itunes.rb +413 -0
  25. data/lib/rss/maker.rb +79 -0
  26. data/lib/rss/maker/0.9.rb +509 -0
  27. data/lib/rss/maker/1.0.rb +436 -0
  28. data/lib/rss/maker/2.0.rb +224 -0
  29. data/lib/rss/maker/atom.rb +173 -0
  30. data/lib/rss/maker/base.rb +945 -0
  31. data/lib/rss/maker/content.rb +22 -0
  32. data/lib/rss/maker/dublincore.rb +122 -0
  33. data/lib/rss/maker/entry.rb +164 -0
  34. data/lib/rss/maker/feed.rb +427 -0
  35. data/lib/rss/maker/image.rb +112 -0
  36. data/lib/rss/maker/itunes.rb +243 -0
  37. data/lib/rss/maker/slash.rb +34 -0
  38. data/lib/rss/maker/syndication.rb +19 -0
  39. data/lib/rss/maker/taxonomy.rb +119 -0
  40. data/lib/rss/maker/trackback.rb +62 -0
  41. data/lib/rss/parser.rb +589 -0
  42. data/lib/rss/rexmlparser.rb +50 -0
  43. data/lib/rss/rss.rb +1346 -0
  44. data/lib/rss/slash.rb +52 -0
  45. data/lib/rss/syndication.rb +69 -0
  46. data/lib/rss/taxonomy.rb +148 -0
  47. data/lib/rss/trackback.rb +291 -0
  48. data/lib/rss/utils.rb +200 -0
  49. data/lib/rss/xml-stylesheet.rb +106 -0
  50. data/lib/rss/xml.rb +72 -0
  51. data/lib/rss/xmlparser.rb +95 -0
  52. data/lib/rss/xmlscanner.rb +122 -0
  53. data/rss.gemspec +38 -0
  54. metadata +138 -0
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: false
2
+ require_relative '../trackback'
3
+ require_relative '1.0'
4
+ require_relative '2.0'
5
+
6
+ module RSS
7
+ module Maker
8
+ module TrackBackModel
9
+ def self.append_features(klass)
10
+ super
11
+
12
+ klass.def_other_element("#{RSS::TRACKBACK_PREFIX}_ping")
13
+ klass.def_classed_elements("#{RSS::TRACKBACK_PREFIX}_about", "value",
14
+ "TrackBackAbouts")
15
+ end
16
+
17
+ class TrackBackAboutsBase < Base
18
+ def_array_element("about", nil, "TrackBackAbout")
19
+
20
+ class TrackBackAboutBase < Base
21
+ attr_accessor :value
22
+ add_need_initialize_variable("value")
23
+
24
+ alias_method(:resource, :value)
25
+ alias_method(:resource=, :value=)
26
+ alias_method(:content, :value)
27
+ alias_method(:content=, :value=)
28
+
29
+ def have_required_values?
30
+ @value
31
+ end
32
+
33
+ def to_feed(feed, current)
34
+ if current.respond_to?(:trackback_abouts) and have_required_values?
35
+ about = current.class::TrackBackAbout.new
36
+ setup_values(about)
37
+ setup_other_elements(about)
38
+ current.trackback_abouts << about
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ class ItemsBase
46
+ class ItemBase; include TrackBackModel; end
47
+ end
48
+
49
+ makers.each do |maker|
50
+ maker.module_eval(<<-EOC, __FILE__, __LINE__ + 1)
51
+ class Items
52
+ class Item
53
+ class TrackBackAbouts < TrackBackAboutsBase
54
+ class TrackBackAbout < TrackBackAboutBase
55
+ end
56
+ end
57
+ end
58
+ end
59
+ EOC
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,589 @@
1
+ # frozen_string_literal: false
2
+ require "forwardable"
3
+ require "open-uri"
4
+
5
+ require_relative "rss"
6
+ require_relative "xml"
7
+
8
+ module RSS
9
+
10
+ class NotWellFormedError < Error
11
+ attr_reader :line, :element
12
+
13
+ # Create a new NotWellFormedError for an error at +line+
14
+ # in +element+. If a block is given the return value of
15
+ # the block ends up in the error message.
16
+ def initialize(line=nil, element=nil)
17
+ message = "This is not well formed XML"
18
+ if element or line
19
+ message << "\nerror occurred"
20
+ message << " in #{element}" if element
21
+ message << " at about #{line} line" if line
22
+ end
23
+ message << "\n#{yield}" if block_given?
24
+ super(message)
25
+ end
26
+ end
27
+
28
+ class XMLParserNotFound < Error
29
+ def initialize
30
+ super("available XML parser was not found in " <<
31
+ "#{AVAILABLE_PARSER_LIBRARIES.inspect}.")
32
+ end
33
+ end
34
+
35
+ class NotValidXMLParser < Error
36
+ def initialize(parser)
37
+ super("#{parser} is not an available XML parser. " <<
38
+ "Available XML parser" <<
39
+ (AVAILABLE_PARSERS.size > 1 ? "s are " : " is ") <<
40
+ "#{AVAILABLE_PARSERS.inspect}.")
41
+ end
42
+ end
43
+
44
+ class NSError < InvalidRSSError
45
+ attr_reader :tag, :prefix, :uri
46
+ def initialize(tag, prefix, require_uri)
47
+ @tag, @prefix, @uri = tag, prefix, require_uri
48
+ super("prefix <#{prefix}> doesn't associate uri " <<
49
+ "<#{require_uri}> in tag <#{tag}>")
50
+ end
51
+ end
52
+
53
+ class Parser
54
+
55
+ extend Forwardable
56
+
57
+ class << self
58
+
59
+ @@default_parser = nil
60
+
61
+ def default_parser
62
+ @@default_parser || AVAILABLE_PARSERS.first
63
+ end
64
+
65
+ # Set @@default_parser to new_value if it is one of the
66
+ # available parsers. Else raise NotValidXMLParser error.
67
+ def default_parser=(new_value)
68
+ if AVAILABLE_PARSERS.include?(new_value)
69
+ @@default_parser = new_value
70
+ else
71
+ raise NotValidXMLParser.new(new_value)
72
+ end
73
+ end
74
+
75
+ def parse(rss, *args)
76
+ if args.last.is_a?(Hash)
77
+ options = args.pop
78
+ else
79
+ options = {}
80
+ end
81
+ do_validate = boolean_argument(args[0], options[:validate], true)
82
+ ignore_unknown_element =
83
+ boolean_argument(args[1], options[:ignore_unknown_element], true)
84
+ parser_class = args[2] || options[:parser_class] || default_parser
85
+ parser = new(rss, parser_class)
86
+ parser.do_validate = do_validate
87
+ parser.ignore_unknown_element = ignore_unknown_element
88
+ parser.parse
89
+ end
90
+
91
+ private
92
+ def boolean_argument(positioned_value, option_value, default)
93
+ value = positioned_value
94
+ if value.nil? and not option_value.nil?
95
+ value = option_value
96
+ end
97
+ value = default if value.nil?
98
+ value
99
+ end
100
+ end
101
+
102
+ def_delegators(:@parser, :parse, :rss,
103
+ :ignore_unknown_element,
104
+ :ignore_unknown_element=, :do_validate,
105
+ :do_validate=)
106
+
107
+ def initialize(rss, parser_class=self.class.default_parser)
108
+ @parser = parser_class.new(normalize_rss(rss))
109
+ end
110
+
111
+ private
112
+
113
+ # Try to get the XML associated with +rss+.
114
+ # Return +rss+ if it already looks like XML, or treat it as a URI,
115
+ # or a file to get the XML,
116
+ def normalize_rss(rss)
117
+ return rss if maybe_xml?(rss)
118
+
119
+ uri = to_uri(rss)
120
+
121
+ if uri.respond_to?(:read)
122
+ uri.read
123
+ elsif !rss.tainted? and File.readable?(rss)
124
+ File.open(rss) {|f| f.read}
125
+ else
126
+ rss
127
+ end
128
+ end
129
+
130
+ # maybe_xml? tests if source is a string that looks like XML.
131
+ def maybe_xml?(source)
132
+ source.is_a?(String) and /</ =~ source
133
+ end
134
+
135
+ # Attempt to convert rss to a URI, but just return it if
136
+ # there's a ::URI::Error
137
+ def to_uri(rss)
138
+ return rss if rss.is_a?(::URI::Generic)
139
+
140
+ begin
141
+ ::URI.parse(rss)
142
+ rescue ::URI::Error
143
+ rss
144
+ end
145
+ end
146
+ end
147
+
148
+ class BaseParser
149
+
150
+ class << self
151
+ def raise_for_undefined_entity?
152
+ listener.raise_for_undefined_entity?
153
+ end
154
+ end
155
+
156
+ def initialize(rss)
157
+ @listener = self.class.listener.new
158
+ @rss = rss
159
+ end
160
+
161
+ def rss
162
+ @listener.rss
163
+ end
164
+
165
+ def ignore_unknown_element
166
+ @listener.ignore_unknown_element
167
+ end
168
+
169
+ def ignore_unknown_element=(new_value)
170
+ @listener.ignore_unknown_element = new_value
171
+ end
172
+
173
+ def do_validate
174
+ @listener.do_validate
175
+ end
176
+
177
+ def do_validate=(new_value)
178
+ @listener.do_validate = new_value
179
+ end
180
+
181
+ def parse
182
+ if @listener.rss.nil?
183
+ _parse
184
+ end
185
+ @listener.rss
186
+ end
187
+
188
+ end
189
+
190
+ class BaseListener
191
+
192
+ extend Utils
193
+
194
+ class << self
195
+
196
+ @@accessor_bases = {}
197
+ @@registered_uris = {}
198
+ @@class_names = {}
199
+
200
+ # return the setter for the uri, tag_name pair, or nil.
201
+ def setter(uri, tag_name)
202
+ _getter = getter(uri, tag_name)
203
+ if _getter
204
+ "#{_getter}="
205
+ else
206
+ nil
207
+ end
208
+ end
209
+
210
+ def getter(uri, tag_name)
211
+ (@@accessor_bases[uri] || {})[tag_name]
212
+ end
213
+
214
+ # return the tag_names for setters associated with uri
215
+ def available_tags(uri)
216
+ (@@accessor_bases[uri] || {}).keys
217
+ end
218
+
219
+ # register uri against this name.
220
+ def register_uri(uri, name)
221
+ @@registered_uris[name] ||= {}
222
+ @@registered_uris[name][uri] = nil
223
+ end
224
+
225
+ # test if this uri is registered against this name
226
+ def uri_registered?(uri, name)
227
+ @@registered_uris[name].has_key?(uri)
228
+ end
229
+
230
+ # record class_name for the supplied uri and tag_name
231
+ def install_class_name(uri, tag_name, class_name)
232
+ @@class_names[uri] ||= {}
233
+ @@class_names[uri][tag_name] = class_name
234
+ end
235
+
236
+ # retrieve class_name for the supplied uri and tag_name
237
+ # If it doesn't exist, capitalize the tag_name
238
+ def class_name(uri, tag_name)
239
+ name = (@@class_names[uri] || {})[tag_name]
240
+ return name if name
241
+
242
+ tag_name = tag_name.gsub(/[_\-]([a-z]?)/) {$1.upcase}
243
+ tag_name[0, 1].upcase + tag_name[1..-1]
244
+ end
245
+
246
+ def install_get_text_element(uri, name, accessor_base)
247
+ install_accessor_base(uri, name, accessor_base)
248
+ def_get_text_element(uri, name, *get_file_and_line_from_caller(1))
249
+ end
250
+
251
+ def raise_for_undefined_entity?
252
+ true
253
+ end
254
+
255
+ private
256
+ # set the accessor for the uri, tag_name pair
257
+ def install_accessor_base(uri, tag_name, accessor_base)
258
+ @@accessor_bases[uri] ||= {}
259
+ @@accessor_bases[uri][tag_name] = accessor_base.chomp("=")
260
+ end
261
+
262
+ def def_get_text_element(uri, element_name, file, line)
263
+ register_uri(uri, element_name)
264
+ method_name = "start_#{element_name}"
265
+ unless private_method_defined?(method_name)
266
+ define_method(method_name) do |name, prefix, attrs, ns|
267
+ uri = _ns(ns, prefix)
268
+ if self.class.uri_registered?(uri, element_name)
269
+ start_get_text_element(name, prefix, ns, uri)
270
+ else
271
+ start_else_element(name, prefix, attrs, ns)
272
+ end
273
+ end
274
+ private(method_name)
275
+ end
276
+ end
277
+ end
278
+ end
279
+
280
+ module ListenerMixin
281
+ attr_reader :rss
282
+
283
+ attr_accessor :ignore_unknown_element
284
+ attr_accessor :do_validate
285
+
286
+ def initialize
287
+ @rss = nil
288
+ @ignore_unknown_element = true
289
+ @do_validate = true
290
+ @ns_stack = [{"xml" => :xml}]
291
+ @tag_stack = [[]]
292
+ @text_stack = ['']
293
+ @proc_stack = []
294
+ @last_element = nil
295
+ @version = @encoding = @standalone = nil
296
+ @xml_stylesheets = []
297
+ @xml_child_mode = false
298
+ @xml_element = nil
299
+ @last_xml_element = nil
300
+ end
301
+
302
+ # set instance vars for version, encoding, standalone
303
+ def xmldecl(version, encoding, standalone)
304
+ @version, @encoding, @standalone = version, encoding, standalone
305
+ end
306
+
307
+ def instruction(name, content)
308
+ if name == "xml-stylesheet"
309
+ params = parse_pi_content(content)
310
+ if params.has_key?("href")
311
+ @xml_stylesheets << XMLStyleSheet.new(params)
312
+ end
313
+ end
314
+ end
315
+
316
+ def tag_start(name, attributes)
317
+ @text_stack.push('')
318
+
319
+ ns = @ns_stack.last.dup
320
+ attrs = {}
321
+ attributes.each do |n, v|
322
+ if /\Axmlns(?:\z|:)/ =~ n
323
+ ns[$POSTMATCH] = v
324
+ else
325
+ attrs[n] = v
326
+ end
327
+ end
328
+ @ns_stack.push(ns)
329
+
330
+ prefix, local = split_name(name)
331
+ @tag_stack.last.push([_ns(ns, prefix), local])
332
+ @tag_stack.push([])
333
+ if @xml_child_mode
334
+ previous = @last_xml_element
335
+ element_attrs = attributes.dup
336
+ unless previous
337
+ ns.each do |ns_prefix, value|
338
+ next if ns_prefix == "xml"
339
+ key = ns_prefix.empty? ? "xmlns" : "xmlns:#{ns_prefix}"
340
+ element_attrs[key] ||= value
341
+ end
342
+ end
343
+ next_element = XML::Element.new(local,
344
+ prefix.empty? ? nil : prefix,
345
+ _ns(ns, prefix),
346
+ element_attrs)
347
+ previous << next_element if previous
348
+ @last_xml_element = next_element
349
+ pr = Proc.new do |text, tags|
350
+ if previous
351
+ @last_xml_element = previous
352
+ else
353
+ @xml_element = @last_xml_element
354
+ @last_xml_element = nil
355
+ end
356
+ end
357
+ @proc_stack.push(pr)
358
+ else
359
+ if @rss.nil? and respond_to?("initial_start_#{local}", true)
360
+ __send__("initial_start_#{local}", local, prefix, attrs, ns.dup)
361
+ elsif respond_to?("start_#{local}", true)
362
+ __send__("start_#{local}", local, prefix, attrs, ns.dup)
363
+ else
364
+ start_else_element(local, prefix, attrs, ns.dup)
365
+ end
366
+ end
367
+ end
368
+
369
+ def tag_end(name)
370
+ if DEBUG
371
+ p "end tag #{name}"
372
+ p @tag_stack
373
+ end
374
+ text = @text_stack.pop
375
+ tags = @tag_stack.pop
376
+ pr = @proc_stack.pop
377
+ pr.call(text, tags) unless pr.nil?
378
+ @ns_stack.pop
379
+ end
380
+
381
+ def text(data)
382
+ if @xml_child_mode
383
+ @last_xml_element << data if @last_xml_element
384
+ else
385
+ @text_stack.last << data
386
+ end
387
+ end
388
+
389
+ private
390
+ def _ns(ns, prefix)
391
+ ns.fetch(prefix, "")
392
+ end
393
+
394
+ CONTENT_PATTERN = /\s*([^=]+)=(["'])([^\2]+?)\2/
395
+ # Extract the first name="value" pair from content.
396
+ # Works with single quotes according to the constant
397
+ # CONTENT_PATTERN. Return a Hash.
398
+ def parse_pi_content(content)
399
+ params = {}
400
+ content.scan(CONTENT_PATTERN) do |name, quote, value|
401
+ params[name] = value
402
+ end
403
+ params
404
+ end
405
+
406
+ def start_else_element(local, prefix, attrs, ns)
407
+ class_name = self.class.class_name(_ns(ns, prefix), local)
408
+ current_class = @last_element.class
409
+ if known_class?(current_class, class_name)
410
+ next_class = current_class.const_get(class_name)
411
+ start_have_something_element(local, prefix, attrs, ns, next_class)
412
+ else
413
+ if !@do_validate or @ignore_unknown_element
414
+ @proc_stack.push(setup_next_element_in_unknown_element)
415
+ else
416
+ parent = "ROOT ELEMENT???"
417
+ if current_class.tag_name
418
+ parent = current_class.tag_name
419
+ end
420
+ raise NotExpectedTagError.new(local, _ns(ns, prefix), parent)
421
+ end
422
+ end
423
+ end
424
+
425
+ if Module.method(:const_defined?).arity == -1
426
+ def known_class?(target_class, class_name)
427
+ class_name and
428
+ (target_class.const_defined?(class_name, false) or
429
+ target_class.constants.include?(class_name.to_sym))
430
+ end
431
+ else
432
+ def known_class?(target_class, class_name)
433
+ class_name and
434
+ (target_class.const_defined?(class_name) or
435
+ target_class.constants.include?(class_name))
436
+ end
437
+ end
438
+
439
+ NAMESPLIT = /^(?:([\w:][-\w.]*):)?([\w:][-\w.]*)/
440
+ def split_name(name)
441
+ name =~ NAMESPLIT
442
+ [$1 || '', $2]
443
+ end
444
+
445
+ def check_ns(tag_name, prefix, ns, require_uri, ignore_unknown_element=nil)
446
+ if _ns(ns, prefix) == require_uri
447
+ true
448
+ else
449
+ if ignore_unknown_element.nil?
450
+ ignore_unknown_element = @ignore_unknown_element
451
+ end
452
+
453
+ if ignore_unknown_element
454
+ false
455
+ elsif @do_validate
456
+ raise NSError.new(tag_name, prefix, require_uri)
457
+ else
458
+ # Force bind required URI with prefix
459
+ @ns_stack.last[prefix] = require_uri
460
+ true
461
+ end
462
+ end
463
+ end
464
+
465
+ def start_get_text_element(tag_name, prefix, ns, required_uri)
466
+ pr = Proc.new do |text, tags|
467
+ setter = self.class.setter(required_uri, tag_name)
468
+ if setter and @last_element.respond_to?(setter)
469
+ if @do_validate
470
+ getter = self.class.getter(required_uri, tag_name)
471
+ if @last_element.__send__(getter)
472
+ raise TooMuchTagError.new(tag_name, @last_element.tag_name)
473
+ end
474
+ end
475
+ @last_element.__send__(setter, text.to_s)
476
+ else
477
+ if @do_validate and !@ignore_unknown_element
478
+ raise NotExpectedTagError.new(tag_name, _ns(ns, prefix),
479
+ @last_element.tag_name)
480
+ end
481
+ end
482
+ end
483
+ @proc_stack.push(pr)
484
+ end
485
+
486
+ def start_have_something_element(tag_name, prefix, attrs, ns, klass)
487
+ if check_ns(tag_name, prefix, ns, klass.required_uri)
488
+ attributes = collect_attributes(tag_name, prefix, attrs, ns, klass)
489
+ @proc_stack.push(setup_next_element(tag_name, klass, attributes))
490
+ else
491
+ @proc_stack.push(setup_next_element_in_unknown_element)
492
+ end
493
+ end
494
+
495
+ def collect_attributes(tag_name, prefix, attrs, ns, klass)
496
+ attributes = {}
497
+ klass.get_attributes.each do |a_name, a_uri, required, element_name|
498
+ if a_uri.is_a?(String) or !a_uri.respond_to?(:include?)
499
+ a_uri = [a_uri]
500
+ end
501
+ unless a_uri == [""]
502
+ for prefix, uri in ns
503
+ if a_uri.include?(uri)
504
+ val = attrs["#{prefix}:#{a_name}"]
505
+ break if val
506
+ end
507
+ end
508
+ end
509
+ if val.nil? and a_uri.include?("")
510
+ val = attrs[a_name]
511
+ end
512
+
513
+ if @do_validate and required and val.nil?
514
+ unless a_uri.include?("")
515
+ for prefix, uri in ns
516
+ if a_uri.include?(uri)
517
+ a_name = "#{prefix}:#{a_name}"
518
+ end
519
+ end
520
+ end
521
+ raise MissingAttributeError.new(tag_name, a_name)
522
+ end
523
+
524
+ attributes[a_name] = val
525
+ end
526
+ attributes
527
+ end
528
+
529
+ def setup_next_element(tag_name, klass, attributes)
530
+ previous = @last_element
531
+ next_element = klass.new(@do_validate, attributes)
532
+ previous.set_next_element(tag_name, next_element)
533
+ @last_element = next_element
534
+ @last_element.parent = previous if klass.need_parent?
535
+ @xml_child_mode = @last_element.have_xml_content?
536
+
537
+ Proc.new do |text, tags|
538
+ p(@last_element.class) if DEBUG
539
+ if @xml_child_mode
540
+ @last_element.content = @xml_element.to_s
541
+ xml_setter = @last_element.class.xml_setter
542
+ @last_element.__send__(xml_setter, @xml_element)
543
+ @xml_element = nil
544
+ @xml_child_mode = false
545
+ else
546
+ if klass.have_content?
547
+ if @last_element.need_base64_encode?
548
+ text = text.lstrip.unpack("m").first
549
+ end
550
+ @last_element.content = text
551
+ end
552
+ end
553
+ if @do_validate
554
+ @last_element.validate_for_stream(tags, @ignore_unknown_element)
555
+ end
556
+ @last_element = previous
557
+ end
558
+ end
559
+
560
+ def setup_next_element_in_unknown_element
561
+ current_element, @last_element = @last_element, nil
562
+ Proc.new {@last_element = current_element}
563
+ end
564
+ end
565
+
566
+ unless const_defined? :AVAILABLE_PARSER_LIBRARIES
567
+ # The list of all available libraries for parsing.
568
+ AVAILABLE_PARSER_LIBRARIES = [
569
+ ["rss/xmlparser", :XMLParserParser],
570
+ ["rss/xmlscanner", :XMLScanParser],
571
+ ["rss/rexmlparser", :REXMLParser],
572
+ ]
573
+ end
574
+
575
+ # The list of all available parsers, in constant form.
576
+ AVAILABLE_PARSERS = []
577
+
578
+ AVAILABLE_PARSER_LIBRARIES.each do |lib, parser|
579
+ begin
580
+ require lib
581
+ AVAILABLE_PARSERS.push(const_get(parser))
582
+ rescue LoadError
583
+ end
584
+ end
585
+
586
+ if AVAILABLE_PARSERS.empty?
587
+ raise XMLParserNotFound
588
+ end
589
+ end