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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/README.md +29 -0
  7. data/Rakefile +1 -0
  8. data/lib/rss.rb +1 -0
  9. data/lib/rss/0.9.rb +428 -0
  10. data/lib/rss/1.0.rb +452 -0
  11. data/lib/rss/2.0.rb +111 -0
  12. data/lib/rss/atom.rb +749 -0
  13. data/lib/rss/content.rb +31 -0
  14. data/lib/rss/content/1.0.rb +10 -0
  15. data/lib/rss/content/2.0.rb +12 -0
  16. data/lib/rss/converter.rb +162 -0
  17. data/lib/rss/dublincore.rb +161 -0
  18. data/lib/rss/dublincore/1.0.rb +13 -0
  19. data/lib/rss/dublincore/2.0.rb +13 -0
  20. data/lib/rss/dublincore/atom.rb +17 -0
  21. data/lib/rss/image.rb +193 -0
  22. data/lib/rss/itunes.rb +410 -0
  23. data/lib/rss/maker.rb +44 -0
  24. data/lib/rss/maker/0.9.rb +467 -0
  25. data/lib/rss/maker/1.0.rb +434 -0
  26. data/lib/rss/maker/2.0.rb +223 -0
  27. data/lib/rss/maker/atom.rb +172 -0
  28. data/lib/rss/maker/base.rb +868 -0
  29. data/lib/rss/maker/content.rb +21 -0
  30. data/lib/rss/maker/dublincore.rb +124 -0
  31. data/lib/rss/maker/entry.rb +163 -0
  32. data/lib/rss/maker/feed.rb +429 -0
  33. data/lib/rss/maker/image.rb +111 -0
  34. data/lib/rss/maker/itunes.rb +242 -0
  35. data/lib/rss/maker/slash.rb +33 -0
  36. data/lib/rss/maker/syndication.rb +18 -0
  37. data/lib/rss/maker/taxonomy.rb +118 -0
  38. data/lib/rss/maker/trackback.rb +61 -0
  39. data/lib/rss/parser.rb +541 -0
  40. data/lib/rss/rexmlparser.rb +54 -0
  41. data/lib/rss/rss.rb +1312 -0
  42. data/lib/rss/slash.rb +49 -0
  43. data/lib/rss/syndication.rb +67 -0
  44. data/lib/rss/taxonomy.rb +145 -0
  45. data/lib/rss/trackback.rb +288 -0
  46. data/lib/rss/utils.rb +111 -0
  47. data/lib/rss/xml-stylesheet.rb +105 -0
  48. data/lib/rss/xml.rb +71 -0
  49. data/lib/rss/xmlparser.rb +93 -0
  50. data/lib/rss/xmlscanner.rb +121 -0
  51. data/lib/rubysl/rss.rb +2 -0
  52. data/lib/rubysl/rss/rss.rb +19 -0
  53. data/lib/rubysl/rss/version.rb +5 -0
  54. data/rubysl-rss.gemspec +23 -0
  55. 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
@@ -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