From 11dbedfaad4a9a9521ece2198a8dc491678b1902 Mon Sep 17 00:00:00 2001 From: shyouhei Date: Wed, 29 Aug 2007 04:06:12 +0000 Subject: add tag v1_8_6_5001 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_8_6_5001@13304 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ruby_1_8_6/lib/rss/rss.rb | 1015 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1015 insertions(+) create mode 100644 ruby_1_8_6/lib/rss/rss.rb (limited to 'ruby_1_8_6/lib/rss/rss.rb') diff --git a/ruby_1_8_6/lib/rss/rss.rb b/ruby_1_8_6/lib/rss/rss.rb new file mode 100644 index 0000000000..a06985af94 --- /dev/null +++ b/ruby_1_8_6/lib/rss/rss.rb @@ -0,0 +1,1015 @@ +require "time" + +class Time + class << self + unless respond_to?(:w3cdtf) + def w3cdtf(date) + if /\A\s* + (-?\d+)-(\d\d)-(\d\d) + (?:T + (\d\d):(\d\d)(?::(\d\d))? + (\.\d+)? + (Z|[+-]\d\d:\d\d)?)? + \s*\z/ix =~ date and (($5 and $8) or (!$5 and !$8)) + datetime = [$1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i] + datetime << $7.to_f * 1000000 if $7 + if $8 + Time.utc(*datetime) - zone_offset($8) + else + Time.local(*datetime) + end + else + raise ArgumentError.new("invalid date: #{date.inspect}") + end + end + end + end + + unless instance_methods.include?("w3cdtf") + alias w3cdtf iso8601 + end +end + +require "English" +require "rss/utils" +require "rss/converter" +require "rss/xml-stylesheet" + +module RSS + + VERSION = "0.1.6" + + URI = "http://purl.org/rss/1.0/" + + DEBUG = false + + class Error < StandardError; end + + class OverlappedPrefixError < Error + attr_reader :prefix + def initialize(prefix) + @prefix = prefix + end + end + + class InvalidRSSError < Error; end + + class MissingTagError < InvalidRSSError + attr_reader :tag, :parent + def initialize(tag, parent) + @tag, @parent = tag, parent + super("tag <#{tag}> is missing in tag <#{parent}>") + end + end + + class TooMuchTagError < InvalidRSSError + attr_reader :tag, :parent + def initialize(tag, parent) + @tag, @parent = tag, parent + super("tag <#{tag}> is too much in tag <#{parent}>") + end + end + + class MissingAttributeError < InvalidRSSError + attr_reader :tag, :attribute + def initialize(tag, attribute) + @tag, @attribute = tag, attribute + super("attribute <#{attribute}> is missing in tag <#{tag}>") + end + end + + class UnknownTagError < InvalidRSSError + attr_reader :tag, :uri + def initialize(tag, uri) + @tag, @uri = tag, uri + super("tag <#{tag}> is unknown in namespace specified by uri <#{uri}>") + end + end + + class NotExpectedTagError < InvalidRSSError + attr_reader :tag, :uri, :parent + def initialize(tag, uri, parent) + @tag, @uri, @parent = tag, uri, parent + super("tag <{#{uri}}#{tag}> is not expected in tag <#{parent}>") + end + end + # For backward compatibility :X + NotExceptedTagError = NotExpectedTagError + + class NotAvailableValueError < InvalidRSSError + attr_reader :tag, :value, :attribute + def initialize(tag, value, attribute=nil) + @tag, @value, @attribute = tag, value, attribute + message = "value <#{value}> of " + message << "attribute <#{attribute}> of " if attribute + message << "tag <#{tag}> is not available." + super(message) + end + end + + class UnknownConversionMethodError < Error + attr_reader :to, :from + def initialize(to, from) + @to = to + @from = from + super("can't convert to #{to} from #{from}.") + end + end + # for backward compatibility + UnknownConvertMethod = UnknownConversionMethodError + + class ConversionError < Error + attr_reader :string, :to, :from + def initialize(string, to, from) + @string = string + @to = to + @from = from + super("can't convert #{@string} to #{to} from #{from}.") + end + end + + class NotSetError < Error + attr_reader :name, :variables + def initialize(name, variables) + @name = name + @variables = variables + super("required variables of #{@name} are not set: #{@variables.join(', ')}") + end + end + + module BaseModel + + include Utils + + def install_have_child_element(tag_name, uri, occurs, name=nil) + name ||= tag_name + add_need_initialize_variable(name) + install_model(tag_name, uri, occurs, name) + + attr_accessor name + install_element(name) do |n, elem_name| + <<-EOC + if @#{n} + "\#{@#{n}.to_s(need_convert, indent)}" + else + '' + end +EOC + end + end + alias_method(:install_have_attribute_element, :install_have_child_element) + + def install_have_children_element(tag_name, uri, occurs, name=nil, plural_name=nil) + name ||= tag_name + plural_name ||= "#{name}s" + add_have_children_element(name, plural_name) + add_plural_form(name, plural_name) + install_model(tag_name, uri, occurs, plural_name) + + def_children_accessor(name, plural_name) + install_element(name, "s") do |n, elem_name| + <<-EOC + rv = [] + @#{n}.each do |x| + value = "\#{x.to_s(need_convert, indent)}" + rv << value if /\\A\\s*\\z/ !~ value + end + rv.join("\n") +EOC + end + end + + def install_text_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil) + name ||= tag_name + disp_name ||= name + self::ELEMENTS << name + add_need_initialize_variable(name) + install_model(tag_name, uri, occurs, name) + + def_corresponded_attr_writer name, type, disp_name + convert_attr_reader name + install_element(name) do |n, elem_name| + <<-EOC + if @#{n} + rv = "\#{indent}<#{elem_name}>" + value = html_escape(@#{n}) + if need_convert + rv << convert(value) + else + rv << value + end + rv << "" + rv + else + '' + end +EOC + end + end + + def install_date_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil) + name ||= tag_name + type ||= :w3cdtf + disp_name ||= name + self::ELEMENTS << name + add_need_initialize_variable(name) + install_model(tag_name, uri, occurs, name) + + # accessor + convert_attr_reader name + date_writer(name, type, disp_name) + + install_element(name) do |n, elem_name| + <<-EOC + if @#{n} + rv = "\#{indent}<#{elem_name}>" + value = html_escape(@#{n}.#{type}) + if need_convert + rv << convert(value) + else + rv << value + end + rv << "" + rv + else + '' + end +EOC + end + + end + + private + def install_element(name, postfix="") + elem_name = name.sub('_', ':') + method_name = "#{name}_element#{postfix}" + add_to_element_method(method_name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{method_name}(need_convert=true, indent='') + #{yield(name, elem_name)} + end + private :#{method_name} +EOC + end + + def convert_attr_reader(*attrs) + attrs.each do |attr| + attr = attr.id2name if attr.kind_of?(Integer) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{attr} + if @converter + @converter.convert(@#{attr}) + else + @#{attr} + end + end +EOC + end + end + + def date_writer(name, type, disp_name=name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}=(new_value) + if new_value.nil? or new_value.kind_of?(Time) + @#{name} = new_value + else + if @do_validate + begin + @#{name} = Time.__send__('#{type}', new_value) + rescue ArgumentError + raise NotAvailableValueError.new('#{disp_name}', new_value) + end + else + @#{name} = nil + if /\\A\\s*\\z/ !~ new_value.to_s + begin + @#{name} = Time.parse(new_value) + rescue ArgumentError + end + end + end + end + + # Is it need? + if @#{name} + class << @#{name} + undef_method(:to_s) + alias_method(:to_s, :#{type}) + end + end + + end +EOC + end + + def integer_writer(name, disp_name=name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}=(new_value) + if new_value.nil? + @#{name} = new_value + else + if @do_validate + begin + @#{name} = Integer(new_value) + rescue ArgumentError + raise NotAvailableValueError.new('#{disp_name}', new_value) + end + else + @#{name} = new_value.to_i + end + end + end +EOC + end + + def positive_integer_writer(name, disp_name=name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}=(new_value) + if new_value.nil? + @#{name} = new_value + else + if @do_validate + begin + tmp = Integer(new_value) + raise ArgumentError if tmp <= 0 + @#{name} = tmp + rescue ArgumentError + raise NotAvailableValueError.new('#{disp_name}', new_value) + end + else + @#{name} = new_value.to_i + end + end + end +EOC + end + + def boolean_writer(name, disp_name=name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}=(new_value) + if new_value.nil? + @#{name} = new_value + else + if @do_validate and + ![true, false, "true", "false"].include?(new_value) + raise NotAvailableValueError.new('#{disp_name}', new_value) + end + if [true, false].include?(new_value) + @#{name} = new_value + else + @#{name} = new_value == "true" + end + end + end +EOC + end + + def def_children_accessor(accessor_name, plural_name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{plural_name} + @#{accessor_name} + end + + def #{accessor_name}(*args) + if args.empty? + @#{accessor_name}.first + else + @#{accessor_name}[*args] + end + end + + def #{accessor_name}=(*args) + warn("Warning:\#{caller.first.sub(/:in `.*'\z/, '')}: " \ + "Don't use `#{accessor_name} = XXX'/`set_#{accessor_name}(XXX)'. " \ + "Those APIs are not sense of Ruby. " \ + "Use `#{plural_name} << XXX' instead of them.") + if args.size == 1 + @#{accessor_name}.push(args[0]) + else + @#{accessor_name}.__send__("[]=", *args) + end + end + alias_method(:set_#{accessor_name}, :#{accessor_name}=) +EOC + end + end + + class Element + + extend BaseModel + include Utils + + INDENT = " " + + MUST_CALL_VALIDATORS = {} + MODELS = [] + GET_ATTRIBUTES = [] + HAVE_CHILDREN_ELEMENTS = [] + TO_ELEMENT_METHODS = [] + NEED_INITIALIZE_VARIABLES = [] + PLURAL_FORMS = {} + + class << self + + def must_call_validators + MUST_CALL_VALIDATORS + end + def models + MODELS + end + def get_attributes + GET_ATTRIBUTES + end + def have_children_elements + HAVE_CHILDREN_ELEMENTS + end + def to_element_methods + TO_ELEMENT_METHODS + end + def need_initialize_variables + NEED_INITIALIZE_VARIABLES + end + def plural_forms + PLURAL_FORMS + end + + + def inherited(klass) + klass.const_set("MUST_CALL_VALIDATORS", {}) + klass.const_set("MODELS", []) + klass.const_set("GET_ATTRIBUTES", []) + klass.const_set("HAVE_CHILDREN_ELEMENTS", []) + klass.const_set("TO_ELEMENT_METHODS", []) + klass.const_set("NEED_INITIALIZE_VARIABLES", []) + klass.const_set("PLURAL_FORMS", {}) + + klass.module_eval(<<-EOC) + public + + @tag_name = name.split(/::/).last + @tag_name[0,1] = @tag_name[0,1].downcase + @have_content = false + + def self.must_call_validators + super.merge(MUST_CALL_VALIDATORS) + end + def self.models + MODELS + super + end + def self.get_attributes + GET_ATTRIBUTES + super + end + def self.have_children_elements + HAVE_CHILDREN_ELEMENTS + super + end + def self.to_element_methods + TO_ELEMENT_METHODS + super + end + def self.need_initialize_variables + NEED_INITIALIZE_VARIABLES + super + end + def self.plural_forms + super.merge(PLURAL_FORMS) + end + + + def self.install_must_call_validator(prefix, uri) + MUST_CALL_VALIDATORS[uri] = prefix + end + + def self.install_model(tag, uri, occurs=nil, getter=nil) + getter ||= tag + if m = MODELS.find {|t, u, o, g| t == tag and u == uri} + m[2] = occurs + else + MODELS << [tag, uri, occurs, getter] + end + end + + def self.install_get_attribute(name, uri, required=true, + type=nil, disp_name=nil, + element_name=nil) + disp_name ||= name + element_name ||= name + def_corresponded_attr_writer name, type, disp_name + convert_attr_reader name + if type == :boolean and /^is/ =~ name + alias_method "\#{$POSTMATCH}?", name + end + GET_ATTRIBUTES << [name, uri, required, element_name] + add_need_initialize_variable(disp_name) + end + + def self.def_corresponded_attr_writer(name, type=nil, disp_name=name) + case type + when :integer + integer_writer name, disp_name + when :positive_integer + positive_integer_writer name, disp_name + when :boolean + boolean_writer name, disp_name + when :w3cdtf, :rfc822, :rfc2822 + date_writer name, type, disp_name + else + attr_writer name + end + end + + def self.content_setup(type=nil) + def_corresponded_attr_writer "content", type + convert_attr_reader :content + @have_content = true + end + + def self.have_content? + @have_content + end + + def self.add_have_children_element(variable_name, plural_name) + HAVE_CHILDREN_ELEMENTS << [variable_name, plural_name] + end + + def self.add_to_element_method(method_name) + TO_ELEMENT_METHODS << method_name + end + + def self.add_need_initialize_variable(variable_name) + NEED_INITIALIZE_VARIABLES << variable_name + end + + def self.add_plural_form(singular, plural) + PLURAL_FORMS[singular] = plural + end + + EOC + end + + def required_prefix + nil + end + + def required_uri + "" + end + + def install_ns(prefix, uri) + if self::NSPOOL.has_key?(prefix) + raise OverlappedPrefixError.new(prefix) + end + self::NSPOOL[prefix] = uri + end + + def tag_name + @tag_name + end + end + + attr_accessor :do_validate + + def initialize(do_validate=true, attrs={}) + @converter = nil + @do_validate = do_validate + initialize_variables(attrs) + end + + def tag_name + self.class.tag_name + end + + def full_name + tag_name + end + + def converter=(converter) + @converter = converter + targets = children.dup + self.class.have_children_elements.each do |variable_name, plural_name| + targets.concat(__send__(plural_name)) + end + targets.each do |target| + target.converter = converter unless target.nil? + end + end + + def convert(value) + if @converter + @converter.convert(value) + else + value + end + end + + def validate(ignore_unknown_element=true) + validate_attribute + __validate(ignore_unknown_element) + end + + def validate_for_stream(tags, ignore_unknown_element=true) + validate_attribute + __validate(ignore_unknown_element, tags, false) + end + + def setup_maker(maker) + target = maker_target(maker) + unless target.nil? + setup_maker_attributes(target) + setup_maker_element(target) + setup_maker_elements(target) + end + end + + def to_s(need_convert=true, indent='') + if self.class.have_content? + return "" unless @content + rv = tag(indent) do |next_indent| + h(@content) + end + else + rv = tag(indent) do |next_indent| + self.class.to_element_methods.collect do |method_name| + __send__(method_name, false, next_indent) + end + end + end + rv = convert(rv) if need_convert + rv + end + + private + def initialize_variables(attrs) + normalized_attrs = {} + attrs.each do |key, value| + normalized_attrs[key.to_s] = value + end + self.class.need_initialize_variables.each do |variable_name| + value = normalized_attrs[variable_name.to_s] + if value + __send__("#{variable_name}=", value) + else + instance_eval("@#{variable_name} = nil") + end + end + initialize_have_children_elements + @content = "" if self.class.have_content? + end + + def initialize_have_children_elements + self.class.have_children_elements.each do |variable_name, plural_name| + instance_eval("@#{variable_name} = []") + end + end + + def tag(indent, additional_attrs={}, &block) + next_indent = indent + INDENT + + attrs = collect_attrs + return "" if attrs.nil? + + attrs.update(additional_attrs) + start_tag = make_start_tag(indent, next_indent, attrs) + + if block + content = block.call(next_indent) + else + content = [] + end + + if content.is_a?(String) + content = [content] + start_tag << ">" + end_tag = "" + else + content = content.reject{|x| x.empty?} + if content.empty? + end_tag = "/>" + else + start_tag << ">\n" + end_tag = "\n#{indent}" + end + end + + start_tag + content.join("\n") + end_tag + end + + def make_start_tag(indent, next_indent, attrs) + start_tag = ["#{indent}<#{full_name}"] + unless attrs.empty? + start_tag << attrs.collect do |key, value| + %Q[#{h key}="#{h value}"] + end.join("\n#{next_indent}") + end + start_tag.join(" ") + end + + def collect_attrs + attrs = {} + _attrs.each do |name, required, alias_name| + value = __send__(alias_name || name) + return nil if required and value.nil? + next if value.nil? + return nil if attrs.has_key?(name) + attrs[name] = value + end + attrs + end + + def tag_name_with_prefix(prefix) + "#{prefix}:#{tag_name}" + end + + # For backward compatibility + def calc_indent + '' + end + + def maker_target(maker) + nil + end + + def setup_maker_attributes(target) + end + + def setup_maker_element(target) + self.class.need_initialize_variables.each do |var| + value = __send__(var) + if value.respond_to?("setup_maker") and + !not_need_to_call_setup_maker_variables.include?(var) + value.setup_maker(target) + else + setter = "#{var}=" + if target.respond_to?(setter) + target.__send__(setter, value) + end + end + end + end + + def not_need_to_call_setup_maker_variables + [] + end + + def setup_maker_elements(parent) + self.class.have_children_elements.each do |name, plural_name| + if parent.respond_to?(plural_name) + target = parent.__send__(plural_name) + __send__(plural_name).each do |elem| + elem.setup_maker(target) + end + end + end + end + + def set_next_element(tag_name, next_element) + klass = next_element.class + prefix = "" + prefix << "#{klass.required_prefix}_" if klass.required_prefix + key = "#{prefix}#{tag_name}" + if self.class.plural_forms.has_key?(key) + ary = __send__("#{self.class.plural_forms[key]}") + ary << next_element + else + __send__("#{prefix}#{tag_name}=", next_element) + end + end + + def children + rv = [] + self.class.models.each do |name, uri, occurs, getter| + value = __send__(getter) + next if value.nil? + value = [value] unless value.is_a?(Array) + value.each do |v| + rv << v if v.is_a?(Element) + end + end + rv + end + + def _tags + rv = [] + self.class.models.each do |name, uri, occurs, getter| + value = __send__(getter) + next if value.nil? + if value.is_a?(Array) + rv.concat([[uri, name]] * value.size) + else + rv << [uri, name] + end + end + rv + end + + def _attrs + self.class.get_attributes.collect do |name, uri, required, element_name| + [element_name, required, name] + end + end + + def __validate(ignore_unknown_element, tags=_tags, recursive=true) + if recursive + children.compact.each do |child| + child.validate + end + end + must_call_validators = self.class.must_call_validators + tags = tag_filter(tags.dup) + p tags if DEBUG + must_call_validators.each do |uri, prefix| + _validate(ignore_unknown_element, tags[uri], uri) + meth = "#{prefix}_validate" + if respond_to?(meth, true) + __send__(meth, ignore_unknown_element, tags[uri], uri) + end + end + end + + def validate_attribute + _attrs.each do |a_name, required, alias_name| + if required and __send__(alias_name || a_name).nil? + raise MissingAttributeError.new(tag_name, a_name) + end + end + end + + def _validate(ignore_unknown_element, tags, uri, models=self.class.models) + count = 1 + do_redo = false + not_shift = false + tag = nil + models = models.find_all {|model| model[1] == uri} + element_names = models.collect {|model| model[0]} + if tags + tags_size = tags.size + tags = tags.sort_by {|x| element_names.index(x) || tags_size} + end + + models.each_with_index do |model, i| + name, model_uri, occurs, getter = model + + if DEBUG + p "before" + p tags + p model + end + + if not_shift + not_shift = false + elsif tags + tag = tags.shift + end + + if DEBUG + p "mid" + p count + end + + case occurs + when '?' + if count > 2 + raise TooMuchTagError.new(name, tag_name) + else + if name == tag + do_redo = true + else + not_shift = true + end + end + when '*' + if name == tag + do_redo = true + else + not_shift = true + end + when '+' + if name == tag + do_redo = true + else + if count > 1 + not_shift = true + else + raise MissingTagError.new(name, tag_name) + end + end + else + if name == tag + if models[i+1] and models[i+1][0] != name and + tags and tags.first == name + raise TooMuchTagError.new(name, tag_name) + end + else + raise MissingTagError.new(name, tag_name) + end + end + + if DEBUG + p "after" + p not_shift + p do_redo + p tag + end + + if do_redo + do_redo = false + count += 1 + redo + else + count = 1 + end + + end + + if !ignore_unknown_element and !tags.nil? and !tags.empty? + raise NotExpectedTagError.new(tags.first, uri, tag_name) + end + + end + + def tag_filter(tags) + rv = {} + tags.each do |tag| + rv[tag[0]] = [] unless rv.has_key?(tag[0]) + rv[tag[0]].push(tag[1]) + end + rv + end + + end + + module RootElementMixin + + include XMLStyleSheetMixin + + attr_reader :output_encoding + + def initialize(rss_version, version=nil, encoding=nil, standalone=nil) + super() + @rss_version = rss_version + @version = version || '1.0' + @encoding = encoding + @standalone = standalone + @output_encoding = nil + end + + def output_encoding=(enc) + @output_encoding = enc + self.converter = Converter.new(@output_encoding, @encoding) + end + + def setup_maker(maker) + maker.version = version + maker.encoding = encoding + maker.standalone = standalone + + xml_stylesheets.each do |xss| + xss.setup_maker(maker) + end + + setup_maker_elements(maker) + end + + def to_xml(version=nil, &block) + if version.nil? or version == @rss_version + to_s + else + RSS::Maker.make(version) do |maker| + setup_maker(maker) + block.call(maker) if block + end.to_s + end + end + + private + def tag(indent, attrs={}, &block) + rv = xmldecl + xml_stylesheet_pi + rv << super(indent, ns_declarations.merge(attrs), &block) + rv + end + + def xmldecl + rv = %Q[\n" + rv + end + + def ns_declarations + decls = {} + self.class::NSPOOL.collect do |prefix, uri| + prefix = ":#{prefix}" unless prefix.empty? + decls["xmlns#{prefix}"] = uri + end + decls + end + + def setup_maker_elements(maker) + channel.setup_maker(maker) if channel + image.setup_maker(maker) if image + textinput.setup_maker(maker) if textinput + super(maker) + end + end + +end -- cgit v1.2.3