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