diff options
author | kou <kou@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2007-03-17 10:13:25 +0000 |
---|---|---|
committer | kou <kou@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2007-03-17 10:13:25 +0000 |
commit | 966a25465aab5c2972e6c453f631a15fc2223256 (patch) | |
tree | 847090e856c9901ab2cc19251c179b9b0985e65b /lib/rss/rss.rb | |
parent | 53cbab048452742b537ac8bccf494630d1c184c8 (diff) | |
download | ruby-966a25465aab5c2972e6c453f631a15fc2223256.tar.gz |
* lib/rss, test/rss:
- supported Atom.
- bumped version 0.1.6 to 0.1.7.
* sample/rss/convert.rb: added new sample.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12087 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rss/rss.rb')
-rw-r--r-- | lib/rss/rss.rb | 661 |
1 files changed, 466 insertions, 195 deletions
diff --git a/lib/rss/rss.rb b/lib/rss/rss.rb index 52ca4db890..c0ce96d6bf 100644 --- a/lib/rss/rss.rb +++ b/lib/rss/rss.rb @@ -11,11 +11,19 @@ class Time (\.\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) + datetime = [$1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i] + usec = 0 + usec = $7.to_f * 1000000 if $7 + zone = $8 + if zone + off = zone_offset(zone, datetime[0]) + datetime = apply_offset(*(datetime + [off])) + datetime << usec + time = Time.utc(*datetime) + time.localtime unless zone_utc?(zone) + time else + datetime << usec Time.local(*datetime) end else @@ -26,7 +34,14 @@ class Time end unless method_defined?(:w3cdtf) - alias w3cdtf iso8601 + def w3cdtf + if usec.zero? + fraction_digits = 0 + else + fraction_digits = Math.log10(usec.to_s.sub(/0*$/, '').to_i).floor + 1 + end + xmlschema(fraction_digits) + end end end @@ -38,7 +53,7 @@ require "rss/xml-stylesheet" module RSS - VERSION = "0.1.6" + VERSION = "0.1.7" URI = "http://purl.org/rss/1.0/" @@ -87,13 +102,15 @@ module RSS end end - class NotExceptedTagError < InvalidRSSError - attr_reader :tag, :parent - def initialize(tag, parent) - @tag, @parent = tag, parent - super("tag <#{tag}> is not expected in tag <#{parent}>") + 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 @@ -135,15 +152,27 @@ module RSS super("required variables of #{@name} are not set: #{@variables.join(', ')}") end end - + + class UnsupportedMakerVersionError < Error + attr_reader :version + def initialize(version) + @version = version + super("Maker doesn't support version: #{@version}") + end + end + module BaseModel include Utils - def install_have_child_element(name) + def install_have_child_element(tag_name, uri, occurs, name=nil, type=nil) + name ||= tag_name add_need_initialize_variable(name) + install_model(tag_name, uri, occurs, name) - attr_accessor name + writer_type, reader_type = type + def_corresponded_attr_writer name, writer_type + def_corresponded_attr_reader name, reader_type install_element(name) do |n, elem_name| <<-EOC if @#{n} @@ -156,11 +185,13 @@ EOC end alias_method(:install_have_attribute_element, :install_have_child_element) - def install_have_children_element(name, plural_name=nil) + 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 @@ -174,9 +205,12 @@ EOC end end - def install_text_element(name, type=nil, disp_name=name) + 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 @@ -199,9 +233,13 @@ EOC end end - def install_date_element(name, type, disp_name=name) + 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 @@ -230,34 +268,76 @@ EOC 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 #{name}_element#{postfix}(need_convert=true, indent='') + def #{method_name}(need_convert=true, indent='') #{yield(name, elem_name)} end - private :#{name}_element#{postfix} + private :#{method_name} EOC end - def convert_attr_reader(*attrs) + def inherit_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}_without_inherit + convert(@#{attr}) + end + + def #{attr} + if @#{attr} + #{attr}_without_inherit + elsif @parent + @parent.#{attr} + else + nil + end + end +EOC + end + end + + def uri_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}_without_base + convert(@#{attr}) + end + def #{attr} - if @converter - @converter.convert(@#{attr}) + value = #{attr}_without_base + return nil if value.nil? + if /\\A[a-z][a-z0-9+.\\-]*:/i =~ value + value else - @#{attr} + "\#{base}\#{value}" end end EOC end 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} + convert(@#{attr}) + 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) + if new_value.nil? @#{name} = new_value + elsif new_value.kind_of?(Time) + @#{name} = new_value.dup else if @do_validate begin @@ -269,7 +349,9 @@ EOC @#{name} = nil if /\\A\\s*\\z/ !~ new_value.to_s begin - @#{name} = Time.parse(new_value) + unless Date._parse(new_value, false).empty? + @#{name} = Time.parse(new_value) + end rescue ArgumentError end end @@ -350,6 +432,32 @@ EOC EOC end + def text_type_writer(name, disp_name=name) + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}=(new_value) + if @do_validate and + !["text", "html", "xhtml", nil].include?(new_value) + raise NotAvailableValueError.new('#{disp_name}', new_value) + end + @#{name} = new_value + end +EOC + end + + def content_writer(name, disp_name=name) + klass_name = "self.class::#{Utils.to_class_name(name)}" + module_eval(<<-EOC, *get_file_and_line_from_caller(2)) + def #{name}=(new_value) + if new_value.is_a?(#{klass_name}) + @#{name} = new_value + else + @#{name} = #{klass_name}.new + @#{name}.content = new_value + 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} @@ -365,10 +473,12 @@ EOC end def #{accessor_name}=(*args) + receiver = self.class.name warn("Warning:\#{caller.first.sub(/:in `.*'\z/, '')}: " \ - "Don't use `#{accessor_name} = XXX'/`set_#{accessor_name}(XXX)'. " \ + "Don't use `\#{receiver}\##{accessor_name} = XXX'/" \ + "`\#{receiver}\#set_#{accessor_name}(XXX)'. " \ "Those APIs are not sense of Ruby. " \ - "Use `#{plural_name} << XXX' instead of them.") + "Use `\#{receiver}\##{plural_name} << XXX' instead of them.") if args.size == 1 @#{accessor_name}.push(args[0]) else @@ -378,36 +488,70 @@ EOC alias_method(:set_#{accessor_name}, :#{accessor_name}=) EOC end + end - def def_content_only_to_s - module_eval(<<-EOC, *get_file_and_line_from_caller(2)) - def to_s(need_convert=true, indent=calc_indent) - if @content - rv = tag(indent) do |next_indent| - h(@content) - end - rv = convert(rv) if need_convert - rv + module SetupMaker + 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 + + private + 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 -EOC end - end class Element extend BaseModel include Utils + include SetupMaker INDENT = " " MUST_CALL_VALIDATORS = {} - MODEL = [] + MODELS = [] GET_ATTRIBUTES = [] HAVE_CHILDREN_ELEMENTS = [] + TO_ELEMENT_METHODS = [] NEED_INITIALIZE_VARIABLES = [] PLURAL_FORMS = {} @@ -416,8 +560,8 @@ EOC def must_call_validators MUST_CALL_VALIDATORS end - def model - MODEL + def models + MODELS end def get_attributes GET_ATTRIBUTES @@ -425,6 +569,9 @@ EOC def have_children_elements HAVE_CHILDREN_ELEMENTS end + def to_element_methods + TO_ELEMENT_METHODS + end def need_initialize_variables NEED_INITIALIZE_VARIABLES end @@ -435,9 +582,10 @@ EOC def inherited(klass) klass.const_set("MUST_CALL_VALIDATORS", {}) - klass.const_set("MODEL", []) + 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", {}) @@ -446,14 +594,13 @@ EOC @tag_name = name.split(/::/).last @tag_name[0,1] = @tag_name[0,1].downcase - @indent_size = name.split(/::/).size - 2 @have_content = false def self.must_call_validators super.merge(MUST_CALL_VALIDATORS) end - def self.model - MODEL + super + def self.models + MODELS + super end def self.get_attributes GET_ATTRIBUTES + super @@ -461,6 +608,9 @@ EOC 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 @@ -473,25 +623,32 @@ EOC MUST_CALL_VALIDATORS[uri] = prefix end - def self.install_model(tag, occurs=nil) - if m = MODEL.find {|t, o| t == tag} - m[1] = occurs + 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 - MODEL << [tag, occurs] + MODELS << [tag, uri, occurs, getter] end end def self.install_get_attribute(name, uri, required=true, - type=nil, disp_name=name) - def_corresponded_attr_writer name, type, disp_name - convert_attr_reader name + type=nil, disp_name=nil, + element_name=nil) + disp_name ||= name + element_name ||= name + writer_type, reader_type = type + def_corresponded_attr_writer name, writer_type, disp_name + def_corresponded_attr_reader name, reader_type if type == :boolean and /^is/ =~ name alias_method "\#{$POSTMATCH}?", name end - GET_ATTRIBUTES << [name, uri, required] + 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) + def self.def_corresponded_attr_writer(name, type=nil, disp_name=nil) + disp_name ||= name case type when :integer integer_writer name, disp_name @@ -499,15 +656,32 @@ EOC positive_integer_writer name, disp_name when :boolean boolean_writer name, disp_name + when :w3cdtf, :rfc822, :rfc2822 + date_writer name, type, disp_name + when :text_type + text_type_writer name, disp_name + when :content + content_writer name, disp_name else attr_writer name end end - def self.content_setup(type=nil) - def_corresponded_attr_writer "content", type - convert_attr_reader :content - def_content_only_to_s + def self.def_corresponded_attr_reader(name, type=nil) + case type + when :inherit + inherit_convert_attr_reader name + when :uri + uri_convert_attr_reader name + else + convert_attr_reader name + end + end + + def self.content_setup(type=nil, disp_name=nil) + writer_type, reader_type = type + def_corresponded_attr_writer :content, writer_type, disp_name + def_corresponded_attr_reader :content, reader_type @have_content = true end @@ -519,6 +693,10 @@ EOC 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 @@ -535,7 +713,11 @@ EOC end def required_uri - nil + "" + end + + def need_parent? + false end def install_ns(prefix, uri) @@ -548,19 +730,18 @@ EOC def tag_name @tag_name end - - def indent_size - @indent_size - end - end - attr_accessor :do_validate + attr_accessor :parent, :do_validate - def initialize(do_validate=true) + def initialize(do_validate=true, attrs=nil) + @parent = nil @converter = nil + if attrs.nil? and (do_validate.is_a?(Hash) or do_validate.is_a?(Array)) + do_validate, attrs = true, do_validate + end @do_validate = do_validate - initialize_variables + initialize_variables(attrs || {}) end def tag_name @@ -571,10 +752,6 @@ EOC tag_name end - def indent_size - self.class.indent_size - end - def converter=(converter) @converter = converter targets = children.dup @@ -593,33 +770,73 @@ EOC value end end - - def validate + + def valid?(ignore_unknown_element=true) + validate(ignore_unknown_element) + true + rescue RSS::Error + false + end + + def validate(ignore_unknown_element=true) + do_validate = @do_validate + @do_validate = true validate_attribute - __validate + __validate(ignore_unknown_element) + ensure + @do_validate = do_validate end - def validate_for_stream(tags) + def validate_for_stream(tags, ignore_unknown_element=true) validate_attribute - __validate(tags, false) + __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) + def to_s(need_convert=true, indent='') + if self.class.have_content? + return "" if !empty_content? and !content_is_set? + rv = tag(indent) do |next_indent| + if empty_content? + "" + else + xmled_content + end + 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 - + + def have_xml_content? + false + end + + def need_base64_encode? + false + end + private - def initialize_variables + 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| - instance_eval("@#{variable_name} = nil") + 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? + @content = normalized_attrs["content"] if self.class.have_content? end def initialize_have_children_elements @@ -628,14 +845,16 @@ EOC end end - def tag(indent, additional_attrs=[], &block) + def tag(indent, additional_attrs={}, &block) next_indent = indent + INDENT attrs = collect_attrs return "" if attrs.nil? - attrs += additional_attrs - start_tag = make_start_tag(indent, next_indent, attrs) + return "" unless have_required_elements? + + attrs.update(additional_attrs) + start_tag = make_start_tag(indent, next_indent, attrs.dup) if block content = block.call(next_indent) @@ -650,6 +869,7 @@ EOC else content = content.reject{|x| x.empty?} if content.empty? + return "" if attrs.empty? end_tag = "/>" else start_tag << ">\n" @@ -671,58 +891,24 @@ EOC end def collect_attrs - _attrs.collect do |name, required, alias_name| + attrs = {} + _attrs.each do |name, required, alias_name| value = __send__(alias_name || name) return nil if required and value.nil? - [name, value] - end.reject do |name, value| - 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 - - def calc_indent - INDENT * (self.class.indent_size) - 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 + # For backward compatibility + def calc_indent + '' end def set_next_element(tag_name, next_element) @@ -737,22 +923,41 @@ EOC __send__("#{prefix}#{tag_name}=", next_element) end end - - # not String class children. + 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 - # default #validate() argument. 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(tags=_tags, recursive=true) + def __validate(ignore_unknown_element, tags=_tags, recursive=true) if recursive children.compact.each do |child| child.validate @@ -761,54 +966,44 @@ EOC must_call_validators = self.class.must_call_validators tags = tag_filter(tags.dup) p tags if DEBUG - self.class::NSPOOL.each do |prefix, uri| - if tags.has_key?(uri) and !must_call_validators.has_key?(uri) - meth = "#{prefix}_validate" - __send__(meth, tags[uri]) if respond_to?(meth, true) - end - end must_call_validators.each do |uri, prefix| - __send__("#{prefix}_validate", tags[uri]) + _validate(ignore_unknown_element, tags[uri], uri) + meth = "#{prefix}_validate" + if !prefix.empty? and 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? + value = instance_variable_get("@#{alias_name || a_name}") + if required and value.nil? raise MissingAttributeError.new(tag_name, a_name) end + __send__("#{alias_name || a_name}=", value) end end - def other_element(need_convert, indent='') - rv = [] - private_methods.each do |meth| - if /\A([^_]+)_[^_]+_elements?\z/ =~ meth.to_s and - self.class::NSPOOL.has_key?($1) - res = __send__(meth, need_convert, indent) - rv << res if /\A\s*\z/ !~ res - end - end - rv.join("\n") - end - - def _validate(tags, model=self.class.model) + def _validate(ignore_unknown_element, tags, uri, models=self.class.models) count = 1 do_redo = false not_shift = false tag = nil - element_names = model.collect {|elem| elem[0]} + 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 - model.each_with_index do |elem, i| + models.each_with_index do |model, i| + name, model_uri, occurs, getter = model if DEBUG p "before" p tags - p elem + p model end if not_shift @@ -822,41 +1017,41 @@ EOC p count end - case elem[1] + case occurs when '?' if count > 2 - raise TooMuchTagError.new(elem[0], tag_name) + raise TooMuchTagError.new(name, tag_name) else - if elem[0] == tag + if name == tag do_redo = true else not_shift = true end end when '*' - if elem[0] == tag + if name == tag do_redo = true else not_shift = true end when '+' - if elem[0] == tag + if name == tag do_redo = true else if count > 1 not_shift = true else - raise MissingTagError.new(elem[0], tag_name) + raise MissingTagError.new(name, tag_name) end end else - if elem[0] == tag - if model[i+1] and model[i+1][0] != elem[0] and - tags and tags.first == elem[0] - raise TooMuchTagError.new(elem[0], tag_name) + 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(elem[0], tag_name) + raise MissingTagError.new(name, tag_name) end end @@ -877,8 +1072,8 @@ EOC end - if !tags.nil? and !tags.empty? - raise NotExceptedTagError.new(tag, tag_name) + if !ignore_unknown_element and !tags.nil? and !tags.empty? + raise NotExpectedTagError.new(tags.first, uri, tag_name) end end @@ -892,6 +1087,43 @@ EOC rv end + def empty_content? + false + end + + def content_is_set? + if have_xml_content? + __send__(self.class.xml_getter) + else + @content + end + end + + def xmled_content + if have_xml_content? + __send__(self.class.xml_getter).to_s + else + content = @content + content = Base64.encode64(content) if need_base64_encode? + h(content) + end + end + + def have_required_elements? + self.class::MODELS.all? do |tag, uri, occurs, getter| + if occurs.nil? or occurs == "+" + child = __send__(getter) + if child.is_a?(Array) + children = child + children.any? {|child| child.__send!(:have_required_elements?)} + else + !child.to_s.empty? + end + else + true + end + end + end end module RootElementMixin @@ -899,16 +1131,23 @@ EOC include XMLStyleSheetMixin attr_reader :output_encoding - - def initialize(rss_version, version=nil, encoding=nil, standalone=nil) + attr_reader :feed_type, :feed_subtype, :feed_version + attr_accessor :version, :encoding, :standalone + def initialize(feed_version, version=nil, encoding=nil, standalone=nil) super() - @rss_version = rss_version + @feed_type = nil + @feed_subtype = nil + @feed_version = feed_version @version = version || '1.0' @encoding = encoding @standalone = standalone @output_encoding = nil end + def feed_info + [@feed_type, @feed_version, @feed_subtype] + end + def output_encoding=(enc) @output_encoding = enc self.converter = Converter.new(@output_encoding, @encoding) @@ -923,14 +1162,48 @@ EOC xss.setup_maker(maker) end - setup_maker_elements(maker) + super end - + + def to_feed(type, &block) + Maker.make(type) do |maker| + setup_maker(maker) + block.call(maker) if block + end + end + + def to_rss(type, &block) + to_feed("rss#{type}", &block) + end + + def to_atom(type, &block) + to_feed("atom:#{type}", &block) + end + + def to_xml(type=nil, &block) + if type.nil? or same_feed_type?(type) + to_s + else + to_feed(type, &block).to_s + end + end + private - def tag(indent, attrs, &block) - rv = xmldecl + xml_stylesheet_pi - rv << super(indent, attrs, &block) - rv + def same_feed_type?(type) + if /^(atom|rss)?(\d+\.\d+)?(?::(.+))?$/i =~ type + feed_type = ($1 || @feed_type).downcase + feed_version = $2 || @feed_version + feed_subtype = $3 || @feed_subtype + [feed_type, feed_version, feed_subtype] == feed_info + else + false + end + end + + def tag(indent, attrs={}, &block) + rv = super(indent, ns_declarations.merge(attrs), &block) + return rv if rv.empty? + "#{xmldecl}#{xml_stylesheet_pi}#{rv}" end def xmldecl @@ -944,18 +1217,16 @@ EOC end def ns_declarations + decls = {} self.class::NSPOOL.collect do |prefix, uri| prefix = ":#{prefix}" unless prefix.empty? - ["xmlns#{prefix}", uri] + 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) + + def maker_target(target) + target end end - end |