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