From 71976790ec02f88d9fb51fab151312124f6df223 Mon Sep 17 00:00:00 2001 From: kou Date: Sun, 5 Aug 2007 03:03:05 +0000 Subject: * lib/rss, sample/rss, test/rss: - 0.1.7 -> 0.1.8. - supported . - reverted backward incompatibility API changes introduced 0.1.7. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12871 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 7 + lib/rss.rb | 3 +- lib/rss/atom.rb | 42 +- lib/rss/image.rb | 6 +- lib/rss/itunes.rb | 410 ++++++++++++++++++++ lib/rss/maker.rb | 1 + lib/rss/maker/0.9.rb | 13 +- lib/rss/maker/1.0.rb | 6 +- lib/rss/maker/2.0.rb | 8 +- lib/rss/maker/atom.rb | 4 +- lib/rss/maker/base.rb | 518 +++++++++++-------------- lib/rss/maker/content.rb | 13 +- lib/rss/maker/dublincore.rb | 62 ++- lib/rss/maker/entry.rb | 14 +- lib/rss/maker/feed.rb | 14 +- lib/rss/maker/image.rb | 38 +- lib/rss/maker/itunes.rb | 248 ++++++++++++ lib/rss/maker/syndication.rb | 13 +- lib/rss/maker/taxonomy.rb | 52 +-- lib/rss/maker/trackback.rb | 38 +- lib/rss/parser.rb | 28 +- lib/rss/rss.rb | 387 +++++++++++-------- lib/rss/utils.rb | 76 +++- sample/rss/blend.rb | 2 +- sample/rss/list_description.rb | 4 +- sample/rss/rss_recent.rb | 3 +- test/rss/rss-assertions.rb | 685 ++------------------------------- test/rss/test_atom.rb | 413 +++++++++++++++++++- test/rss/test_image.rb | 10 +- test/rss/test_inherit.rb | 5 +- test/rss/test_itunes.rb | 347 +++++++++++++++++ test/rss/test_maker_0.9.rb | 44 ++- test/rss/test_maker_1.0.rb | 92 +++-- test/rss/test_maker_2.0.rb | 12 +- test/rss/test_maker_atom_feed.rb | 11 +- test/rss/test_maker_itunes.rb | 471 +++++++++++++++++++++++ test/rss/test_maker_xml-stylesheet.rb | 4 +- test/rss/test_parser.rb | 14 +- test/rss/test_setup_maker_1.0.rb | 8 +- test/rss/test_setup_maker_atom_feed.rb | 4 +- test/rss/test_setup_maker_itunes.rb | 144 +++++++ test/rss/test_to_s.rb | 232 ++++++++++- test/rss/test_version.rb | 2 +- 43 files changed, 3053 insertions(+), 1455 deletions(-) create mode 100644 lib/rss/itunes.rb create mode 100644 lib/rss/maker/itunes.rb create mode 100644 test/rss/test_itunes.rb create mode 100644 test/rss/test_maker_itunes.rb create mode 100644 test/rss/test_setup_maker_itunes.rb diff --git a/ChangeLog b/ChangeLog index 84e222816a..85338f5141 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +Sun Aug 5 11:51:39 2007 Kouhei Sutou + + * lib/rss, sample/rss, test/rss: + - 0.1.7 -> 0.1.8. + - supported . + - reverted backward incompatibility API changes introduced 0.1.7. + Sun Aug 5 04:56:25 2007 Nobuyoshi Nakada * io.c (pipe_open_v, pipe_open_s): separate array and string diff --git a/lib/rss.rb b/lib/rss.rb index bbe19ad95c..8eb8603581 100644 --- a/lib/rss.rb +++ b/lib/rss.rb @@ -1,4 +1,4 @@ -# Copyright (c) 2003-2006 Kouhei Sutou. You can redistribute it and/or +# Copyright (c) 2003-2007 Kouhei Sutou. You can redistribute it and/or # modify it under the same terms as Ruby. # # Author:: Kouhei Sutou @@ -10,6 +10,7 @@ require 'rss/atom' require 'rss/content' require 'rss/dublincore' require 'rss/image' +require 'rss/itunes' require 'rss/syndication' require 'rss/taxonomy' require 'rss/trackback' diff --git a/lib/rss/atom.rb b/lib/rss/atom.rb index 901e69a4b0..7cba934feb 100644 --- a/lib/rss/atom.rb +++ b/lib/rss/atom.rb @@ -131,7 +131,7 @@ module RSS private def maker_target(target) - target.__send__(self.class.name.split(/::/).last.downcase) + target.__send__(self.class.name.split(/::/).last.downcase) {|x| x} end def setup_maker_attributes(target) @@ -239,6 +239,11 @@ module RSS alias_method :items, :entries + def have_author? + authors.any? {|author| !author.to_s.empty?} or + entries.any? {|entry| entry.have_author?(false)} + end + private def atom_validate(ignore_unknown_element, tags, uri) unless have_author? @@ -251,11 +256,6 @@ module RSS super and have_author? end - def have_author? - authors.any? {|author| !author.to_s.empty?} or - entries.any? {|entry| entry.__send!(:have_author?, false)} - end - def maker_target(maker) maker.channel end @@ -315,9 +315,10 @@ module RSS private def setup_maker_attributes(target) - generator = target.generator - generator.uri = uri if uri - generator.version = version if version + target.generator do |generator| + generator.uri = uri if uri + generator.version = version if version + end end end @@ -408,6 +409,12 @@ module RSS tag, URI, occurs, tag, *args) end + def have_author?(check_parent=true) + authors.any? {|author| !author.to_s.empty?} or + (check_parent and @parent and @parent.have_author?) or + (source and source.have_author?) + end + private def atom_validate(ignore_unknown_element, tags, uri) unless have_author? @@ -420,12 +427,6 @@ module RSS super and have_author? end - def have_author?(check_parent=true) - authors.any? {|author| !author.to_s.empty?} or - (check_parent and @parent and @parent.__send!(:have_author?)) or - (source and source.__send!(:have_author?)) - end - def maker_target(items) if items.respond_to?("items") # For backward compatibility @@ -606,7 +607,6 @@ module RSS tag, URI, occurs, tag, *args) end - private def have_author? !author.to_s.empty? end @@ -674,6 +674,11 @@ module RSS super(maker) end + def have_author? + authors.any? {|author| !author.to_s.empty?} or + (source and source.have_author?) + end + private def atom_validate(ignore_unknown_element, tags, uri) unless have_author? @@ -686,11 +691,6 @@ module RSS super and have_author? end - def have_author? - authors.any? {|author| !author.to_s.empty?} or - (source and source.__send!(:have_author?)) - end - def maker_target(maker) maker.items.new_item end diff --git a/lib/rss/image.rb b/lib/rss/image.rb index 02a8a0b6a7..c4714aea12 100644 --- a/lib/rss/image.rb +++ b/lib/rss/image.rb @@ -142,8 +142,8 @@ module RSS end AVAILABLE_SIZES = %w(small medium large) - alias_method :_size=, :size= - private :_size= + alias_method :set_size, :size= + private :set_size def size=(new_value) if @do_validate and !new_value.nil? new_value = new_value.strip @@ -152,7 +152,7 @@ module RSS raise NotAvailableValueError.new(full_name, new_value, attr_name) end end - __send!(:_size=, new_value) + set_size(new_value) end alias image_size= size= diff --git a/lib/rss/itunes.rb b/lib/rss/itunes.rb new file mode 100644 index 0000000000..7414bc511a --- /dev/null +++ b/lib/rss/itunes.rb @@ -0,0 +1,410 @@ +require 'rss/2.0' + +module RSS + ITUNES_PREFIX = 'itunes' + ITUNES_URI = 'http://www.itunes.com/dtds/podcast-1.0.dtd' + + Rss.install_ns(ITUNES_PREFIX, ITUNES_URI) + + module ITunesModelUtils + include Utils + + def def_class_accessor(klass, name, type, *args) + normalized_name = name.gsub(/-/, "_") + full_name = "#{ITUNES_PREFIX}_#{normalized_name}" + klass_name = "ITunes#{Utils.to_class_name(normalized_name)}" + + case type + when :element, :attribute + klass::ELEMENTS << full_name + def_element_class_accessor(klass, name, full_name, klass_name, *args) + when :elements + klass::ELEMENTS << full_name + def_elements_class_accessor(klass, name, full_name, klass_name, *args) + else + klass.install_must_call_validator(ITUNES_PREFIX, ITUNES_URI) + klass.install_text_element(normalized_name, ITUNES_URI, "?", + full_name, type, name) + end + end + + def def_element_class_accessor(klass, name, full_name, klass_name, + recommended_attribute_name=nil) + klass.install_have_child_element(name, ITUNES_PREFIX, "?", full_name) + end + + def def_elements_class_accessor(klass, name, full_name, klass_name, + plural_name, recommended_attribute_name=nil) + full_plural_name = "#{ITUNES_PREFIX}_#{plural_name}" + klass.install_have_children_element(name, ITUNES_PREFIX, "*", + full_name, full_plural_name) + end + end + + module ITunesBaseModel + extend ITunesModelUtils + + ELEMENTS = [] + + ELEMENT_INFOS = [["author"], + ["block", :yes_other], + ["explicit", :yes_clean_other], + ["keywords", :csv], + ["subtitle"], + ["summary"]] + end + + module ITunesChannelModel + extend BaseModel + extend ITunesModelUtils + include ITunesBaseModel + + ELEMENTS = [] + + class << self + def append_features(klass) + super + + return if klass.instance_of?(Module) + ELEMENT_INFOS.each do |name, type, *additional_infos| + def_class_accessor(klass, name, type, *additional_infos) + end + end + end + + ELEMENT_INFOS = [ + ["category", :elements, "categories", "text"], + ["image", :attribute, "href"], + ["owner", :element], + ["new-feed-url"], + ] + ITunesBaseModel::ELEMENT_INFOS + + class ITunesCategory < Element + include RSS09 + + @tag_name = "category" + + class << self + def required_prefix + ITUNES_PREFIX + end + + def required_uri + ITUNES_URI + end + end + + [ + ["text", "", true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required) + end + + ITunesCategory = self + install_have_children_element("category", ITUNES_URI, "*", + "#{ITUNES_PREFIX}_category", + "#{ITUNES_PREFIX}_categories") + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.text = args[0] + end + end + + def full_name + tag_name_with_prefix(ITUNES_PREFIX) + end + + private + def maker_target(categories) + if text or !itunes_categories.empty? + categories.new_category + else + nil + end + end + + def setup_maker_attributes(category) + category.text = text if text + end + + def setup_maker_elements(category) + super(category) + itunes_categories.each do |sub_category| + sub_category.setup_maker(category) + end + end + end + + class ITunesImage < Element + include RSS09 + + @tag_name = "image" + + class << self + def required_prefix + ITUNES_PREFIX + end + + def required_uri + ITUNES_URI + end + end + + [ + ["href", "", true] + ].each do |name, uri, required| + install_get_attribute(name, uri, required) + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.href = args[0] + end + end + + def full_name + tag_name_with_prefix(ITUNES_PREFIX) + end + + private + def maker_target(target) + if href + target.itunes_image {|image| image} + else + nil + end + end + + def setup_maker_attributes(image) + image.href = href + end + end + + class ITunesOwner < Element + include RSS09 + + @tag_name = "owner" + + class << self + def required_prefix + ITUNES_PREFIX + end + + def required_uri + ITUNES_URI + end + end + + install_must_call_validator(ITUNES_PREFIX, ITUNES_URI) + [ + ["name"], + ["email"], + ].each do |name,| + ITunesBaseModel::ELEMENT_INFOS << name + install_text_element(name, ITUNES_URI, nil, "#{ITUNES_PREFIX}_#{name}") + end + + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + self.itunes_name = args[0] + self.itunes_email = args[1] + end + end + + def full_name + tag_name_with_prefix(ITUNES_PREFIX) + end + + private + def maker_target(target) + target.itunes_owner + end + + def setup_maker_element(owner) + super(owner) + owner.itunes_name = itunes_name + owner.itunes_email = itunes_email + end + end + end + + module ITunesItemModel + extend BaseModel + extend ITunesModelUtils + include ITunesBaseModel + + class << self + def append_features(klass) + super + + return if klass.instance_of?(Module) + ELEMENT_INFOS.each do |name, type| + def_class_accessor(klass, name, type) + end + end + end + + ELEMENT_INFOS = ITunesBaseModel::ELEMENT_INFOS + + [["duration", :element, "content"]] + + class ITunesDuration < Element + include RSS09 + + @tag_name = "duration" + + class << self + def required_prefix + ITUNES_PREFIX + end + + def required_uri + ITUNES_URI + end + + def parse(duration, do_validate=true) + if do_validate and /\A(?: + \d?\d:[0-5]\d:[0-5]\d| + [0-5]?\d:[0-5]\d + )\z/x !~ duration + raise ArgumentError, + "must be one of HH:MM:SS, H:MM:SS, MM::SS, M:SS: " + + duration.inspect + end + + components = duration.split(':') + components[3..-1] = nil if components.size > 3 + + components.unshift("00") until components.size == 3 + + components.collect do |component| + component.to_i + end + end + + def construct(hour, minute, second) + components = [minute, second] + if components.include?(nil) + nil + else + components.unshift(hour) if hour and hour > 0 + components.collect do |component| + "%02d" % component + end.join(":") + end + end + end + + content_setup + alias_method(:value, :content) + remove_method(:content=) + + attr_reader :hour, :minute, :second + def initialize(*args) + if Utils.element_initialize_arguments?(args) + super + else + super() + args = args[0] if args.size == 1 and args[0].is_a?(Array) + if args.size == 1 + self.content = args[0] + elsif args.size > 3 + raise ArgumentError, + "must be (do_validate, params), (content), " + + "(minute, second), ([minute, second]), " + + "(hour, minute, second) or ([hour, minute, second]): " + + args.inspect + else + @second, @minute, @hour = args.reverse + update_content + end + end + end + + def content=(value) + if value.nil? + @content = nil + elsif value.is_a?(self.class) + self.content = value.content + else + begin + @hour, @minute, @second = self.class.parse(value, @do_validate) + rescue ArgumentError + raise NotAvailableValueError.new(tag_name, value) + end + @content = value + end + end + alias_method(:value=, :content=) + + def hour=(hour) + @hour = @do_validate ? Integer(hour) : hour.to_i + update_content + hour + end + + def minute=(minute) + @minute = @do_validate ? Integer(minute) : minute.to_i + update_content + minute + end + + def second=(second) + @second = @do_validate ? Integer(second) : second.to_i + update_content + second + end + + def full_name + tag_name_with_prefix(ITUNES_PREFIX) + end + + private + def update_content + @content = self.class.construct(hour, minute, second) + end + + def maker_target(target) + if @content + target.itunes_duration {|duration| duration} + else + nil + end + end + + def setup_maker_element(duration) + super(duration) + duration.content = @content + end + end + end + + class Rss + class Channel + include ITunesChannelModel + class Item; include ITunesItemModel; end + end + end + + element_infos = + ITunesChannelModel::ELEMENT_INFOS + ITunesItemModel::ELEMENT_INFOS + element_infos.each do |name, type| + class_name = Utils.to_class_name(name) + case type + when :element, :elements, :attribute + BaseListener.install_class_name(ITUNES_URI, name, "ITunes#{class_name}") + else + accessor_base = "#{ITUNES_PREFIX}_#{name.gsub(/-/, '_')}" + BaseListener.install_get_text_element(ITUNES_URI, name, accessor_base) + end + end +end diff --git a/lib/rss/maker.rb b/lib/rss/maker.rb index a47b55b670..e362e785fa 100644 --- a/lib/rss/maker.rb +++ b/lib/rss/maker.rb @@ -40,3 +40,4 @@ require "rss/maker/syndication" require "rss/maker/taxonomy" require "rss/maker/trackback" require "rss/maker/image" +require "rss/maker/itunes" diff --git a/lib/rss/maker/0.9.rb b/lib/rss/maker/0.9.rb index dd75c9289b..c83597dfd5 100644 --- a/lib/rss/maker/0.9.rb +++ b/lib/rss/maker/0.9.rb @@ -22,7 +22,6 @@ module RSS end class Channel < ChannelBase - def to_feed(rss) channel = Rss::Channel.new set = setup_values(channel) @@ -63,8 +62,8 @@ module RSS def not_set_required_variables vars = super - vars << "description" unless description.have_required_values? - vars << "title" unless title.have_required_values? + vars << "description" unless description {|d| d.have_required_values?} + vars << "title" unless title {|t| t.have_required_values?} vars end @@ -259,7 +258,7 @@ module RSS def to_feed(rss) item = Rss::Channel::Item.new set = setup_values(item) - if set or title.have_required_values? + if set or title {|t| t.have_required_values?} rss.items << item set_parent(item, rss.channel) setup_other_elements(rss, item) @@ -268,10 +267,6 @@ module RSS end end - def have_required_values? - super and title.have_required_values? - end - private def required_variable_names %w(link) @@ -279,7 +274,7 @@ module RSS def not_set_required_variables vars = super - vars << "title" unless title.have_required_values? + vars << "title" unless title {|t| t.have_required_values?} vars end diff --git a/lib/rss/maker/1.0.rb b/lib/rss/maker/1.0.rb index 12608ad94a..c4af6e9b2e 100644 --- a/lib/rss/maker/1.0.rb +++ b/lib/rss/maker/1.0.rb @@ -83,8 +83,8 @@ module RSS def not_set_required_variables vars = super - vars << "description" unless description.have_required_values? - vars << "title" unless title.have_required_values? + vars << "description" unless description {|d| d.have_required_values?} + vars << "title" unless title {|t| t.have_required_values?} vars end @@ -256,7 +256,7 @@ module RSS def not_set_required_variables set_default_values do vars = super - vars << "title" unless title.have_required_values? + vars << "title" unless title {|t| t.have_required_values?} vars end end diff --git a/lib/rss/maker/2.0.rb b/lib/rss/maker/2.0.rb index d93ba94d4a..9149c0e24b 100644 --- a/lib/rss/maker/2.0.rb +++ b/lib/rss/maker/2.0.rb @@ -15,7 +15,7 @@ module RSS private def required_variable_names - %w(title link description) + %w(link) end class SkipDays < RSS09::Channel::SkipDays @@ -90,13 +90,7 @@ module RSS end class Items < RSS09::Items - class Item < RSS09::Items::Item - - def have_required_values? - @title or @description - end - private def required_variable_names %w(title description) diff --git a/lib/rss/maker/atom.rb b/lib/rss/maker/atom.rb index 27d30c6d89..fd3198cd9e 100644 --- a/lib/rss/maker/atom.rb +++ b/lib/rss/maker/atom.rb @@ -147,11 +147,11 @@ EOC def to_feed(feed, current) logo = current.class::Logo.new class << logo - alias uri= content= + alias_method(:uri=, :content=) end set = setup_values(logo) class << logo - undef uri= + remove_method(:uri=) end if set current.logo = logo diff --git a/lib/rss/maker/base.rb b/lib/rss/maker/base.rb index ad47ff29cc..752b2fa58f 100644 --- a/lib/rss/maker/base.rb +++ b/lib/rss/maker/base.rb @@ -4,85 +4,155 @@ require 'rss/rss' module RSS module Maker - module Base - def self.append_features(klass) - super - - klass.module_eval(<<-EOC, __FILE__, __LINE__) + class Base + extend Utils::InheritedReader - OTHER_ELEMENTS = [] - NEED_INITIALIZE_VARIABLES = [] + OTHER_ELEMENTS = [] + NEED_INITIALIZE_VARIABLES = [] - def self.inherited(subclass) - subclass.const_set("OTHER_ELEMENTS", []) - subclass.const_set("NEED_INITIALIZE_VARIABLES", []) - - subclass.module_eval(<<-EOEOC, __FILE__, __LINE__) - def self.other_elements - OTHER_ELEMENTS + super - end - - def self.need_initialize_variables - NEED_INITIALIZE_VARIABLES + super - end - EOEOC + class << self + def other_elements + inherited_array_reader("OTHER_ELEMENTS") + end + def need_initialize_variables + inherited_array_reader("NEED_INITIALIZE_VARIABLES") end - def self.add_other_element(variable_name) - OTHER_ELEMENTS << variable_name + def inherited_base + ::RSS::Maker::Base end - def self.other_elements - OTHER_ELEMENTS + def inherited(subclass) + subclass.const_set("OTHER_ELEMENTS", []) + subclass.const_set("NEED_INITIALIZE_VARIABLES", []) end - def self.add_need_initialize_variable(variable_name, init_value="nil") - NEED_INITIALIZE_VARIABLES << [variable_name, init_value] + def add_other_element(variable_name) + self::OTHER_ELEMENTS << variable_name end - def self.need_initialize_variables - NEED_INITIALIZE_VARIABLES + def add_need_initialize_variable(variable_name, init_value="nil") + self::NEED_INITIALIZE_VARIABLES << [variable_name, init_value] end - def self.def_array_element(name, plural=nil, klass=nil) + def def_array_element(name, plural=nil, klass_name=nil) include Enumerable extend Forwardable - plural ||= "\#{name}s" - klass ||= "self.class::\#{Utils.to_class_name(name)}" - - def_delegators("@\#{plural}", :<<, :[], :[]=, :first, :last) - def_delegators("@\#{plural}", :push, :pop, :shift, :unshift) - def_delegators("@\#{plural}", :each, :size, :empty?, :clear) + plural ||= "#{name}s" + klass_name ||= Utils.to_class_name(name) + def_delegators("@#{plural}", :<<, :[], :[]=, :first, :last) + def_delegators("@#{plural}", :push, :pop, :shift, :unshift) + def_delegators("@#{plural}", :each, :size, :empty?, :clear) add_need_initialize_variable(plural, "[]") - module_eval(<<-EOM, __FILE__, __LINE__ + 1) - def new_\#{name} - \#{name} = \#{klass}.new(@maker) - @\#{plural} << \#{name} + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + def new_#{name} + #{name} = self.class::#{klass_name}.new(@maker) + @#{plural} << #{name} if block_given? - yield \#{name} + yield #{name} else - \#{name} + #{name} end end - alias new_child new_\#{name} + alias new_child new_#{name} def to_feed(*args) - @\#{plural}.each do |\#{name}| - \#{name}.to_feed(*args) + @#{plural}.each do |#{name}| + #{name}.to_feed(*args) end end def replace(elements) - @\#{plural}.replace(elements.to_a) + @#{plural}.replace(elements.to_a) end -EOM + EOC + end + + def def_classed_element_without_accessor(name, class_name=nil) + class_name ||= Utils.to_class_name(name) + add_other_element(name) + add_need_initialize_variable(name, "make_#{name}") + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + private + def setup_#{name}(feed, current) + @#{name}.to_feed(feed, current) + end + + def make_#{name} + self.class::#{class_name}.new(@maker) + end + EOC + end + + def def_classed_element(name, class_name=nil, attribute_name=nil) + def_classed_element_without_accessor(name, class_name) + if attribute_name + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + def #{name} + if block_given? + yield(@#{name}) + else + @#{name}.#{attribute_name} + end + end + + def #{name}=(new_value) + @#{name}.#{attribute_name} = new_value + end + EOC + else + attr_reader name + end + end + + def def_classed_elements(name, attribute, plural_class_name=nil, + plural_name=nil, new_name=nil) + plural_name ||= "#{name}s" + new_name ||= name + def_classed_element(plural_name, plural_class_name) + local_variable_name = "_#{name}" + new_value_variable_name = "new_value" + additional_setup_code = nil + if block_given? + additional_setup_code = yield(local_variable_name, + new_value_variable_name) + end + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + def #{name} + #{local_variable_name} = #{plural_name}.first + #{local_variable_name} ? #{local_variable_name}.#{attribute} : nil + end + + def #{name}=(#{new_value_variable_name}) + #{local_variable_name} = + #{plural_name}.first || #{plural_name}.new_#{new_name} + #{additional_setup_code} + #{local_variable_name}.#{attribute} = #{new_value_variable_name} + end + EOC + end + + def def_other_element(name) + attr_accessor name + def_other_element_without_accessor(name) + end + + def def_other_element_without_accessor(name) + add_need_initialize_variable(name) + add_other_element(name) + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + def setup_#{name}(feed, current) + if !@#{name}.nil? and current.respond_to?(:#{name}=) + current.#{name} = @#{name} + end + end + EOC end - EOC end - + attr_reader :maker def initialize(maker) @maker = maker @@ -183,12 +253,27 @@ EOM attr_accessor element add_need_initialize_variable(element) end -EOC + EOC end end module AtomTextConstructBase module EnsureXMLContent + class << self + def included(base) + super + base.class_eval do + %w(type content xml_content).each do |element| + attr_reader element + attr_writer element if element != "xml_content" + add_need_initialize_variable(element) + end + + alias_method(:xhtml, :xml_content) + end + end + end + def ensure_xml_content(content) xhtml_uri = ::RSS::Atom::XHTML_URI unless content.is_a?(RSS::XML::Element) and @@ -203,6 +288,14 @@ EOC content end + def xml_content=(content) + @xml_content = ensure_xml_content(content) + end + + def xhtml=(content) + self.xml_content = content + end + private def set_xhtml_uri_as_default_uri(children) children.collect do |child| @@ -221,21 +314,9 @@ EOC def self.append_features(klass) super - klass.class_eval(<<-EOC, __FILE__, __LINE__ + 1) + klass.class_eval do include EnsureXMLContent - - %w(type content xml_content).each do |element| - attr element, element != "xml_content" - add_need_initialize_variable(element) - end - - def xml_content=(content) - @xml_content = ensure_xml_content(content) - end - - alias_method(:xhtml, :xml_content) - alias_method(:xhtml=, :xml_content=) -EOC + end end end @@ -248,7 +329,7 @@ EOC } _date = date if _date and !dc_dates.any? {|dc_date| dc_date.value == _date} - dc_date = self.class::DublinCoreDates::Date.new(self) + dc_date = self.class::DublinCoreDates::DublinCoreDate.new(self) dc_date.value = _date.dup dc_dates.unshift(dc_date) end @@ -260,9 +341,7 @@ EOC end end - class RSSBase - include Base - + class RSSBase < Base class << self def make(&block) new.make(&block) @@ -281,7 +360,7 @@ EOC def make_#{element} self.class::#{Utils.to_class_name(element)}.new(self) end -EOC + EOC end attr_reader :feed_version @@ -326,13 +405,10 @@ EOC end end - class XMLStyleSheets - include Base - + class XMLStyleSheets < Base def_array_element("xml_stylesheet", nil, "XMLStyleSheet") - class XMLStyleSheet - include Base + class XMLStyleSheet < Base ::RSS::XMLStyleSheet::ATTRIBUTES.each do |attribute| attr_accessor attribute @@ -362,26 +438,23 @@ EOC end end - class ChannelBase - include Base + class ChannelBase < Base include SetupDefaultDate - %w(cloud categories skipDays skipHours links authors - contributors generator copyright description - title).each do |element| - attr_reader element - add_other_element(element) - add_need_initialize_variable(element, "make_#{element}") - module_eval(<<-EOC, __FILE__, __LINE__) - private - def setup_#{element}(feed, current) - @#{element}.to_feed(feed, current) - end + %w(cloud categories skipDays skipHours).each do |name| + def_classed_element(name) + end - def make_#{element} - self.class::#{Utils.to_class_name(element)}.new(@maker) - end -EOC + %w(generator copyright description title).each do |name| + def_classed_element(name, nil, "content") + end + + [ + ["link", "href", Proc.new {|target,| "#{target}.href = 'self'"}], + ["author", "name"], + ["contributor", "name"], + ].each do |name, attribute, additional_setup_maker| + def_classed_elements(name, attribute, &additional_setup_maker) end %w(id about language @@ -407,59 +480,12 @@ EOC self.date = date end - def link - _link = links.first - _link ? _link.href : nil - end - - def link=(href) - _link = links.first || links.new_link - _link.rel = "self" - _link.href = href - end - - def author - _author = authors.first - _author ? _author.name : nil - end - - def author=(name) - _author = authors.first || authors.new_author - _author.name = name - end - - def contributor - _contributor = contributors.first - _contributor ? _contributor.name : nil - end - - def contributor=(name) - _contributor = contributors.first || contributors.new_contributor - _contributor.name = name - end - - def generator=(content) - @generator.content = content - end - - def copyright=(content) - @copyright.content = content - end - alias_method(:rights, :copyright) alias_method(:rights=, :copyright=) - def description=(content) - @description.content = content - end - alias_method(:subtitle, :description) alias_method(:subtitle=, :description=) - def title=(content) - @title.content = content - end - def icon image_favicon.about end @@ -476,14 +502,10 @@ EOC maker.image.url = url end - class SkipDaysBase - include Base - + class SkipDaysBase < Base def_array_element("day") - class DayBase - include Base - + class DayBase < Base %w(content).each do |element| attr_accessor element add_need_initialize_variable(element) @@ -491,14 +513,10 @@ EOC end end - class SkipHoursBase - include Base - + class SkipHoursBase < Base def_array_element("hour") - class HourBase - include Base - + class HourBase < Base %w(content).each do |element| attr_accessor element add_need_initialize_variable(element) @@ -506,23 +524,17 @@ EOC end end - class CloudBase - include Base - + class CloudBase < Base %w(domain port path registerProcedure protocol).each do |element| attr_accessor element add_need_initialize_variable(element) end end - class CategoriesBase - include Base - + class CategoriesBase < Base def_array_element("category", "categories") - class CategoryBase - include Base - + class CategoryBase < Base %w(domain content label).each do |element| attr_accessor element add_need_initialize_variable(element) @@ -535,14 +547,10 @@ EOC end end - class LinksBase - include Base - + class LinksBase < Base def_array_element("link") - class LinkBase - include Base - + class LinkBase < Base %w(href rel type hreflang title length).each do |element| attr_accessor element add_need_initialize_variable(element) @@ -550,56 +558,43 @@ EOC end end - class AuthorsBase - include Base - + class AuthorsBase < Base def_array_element("author") - class AuthorBase - include Base + class AuthorBase < Base include AtomPersonConstructBase end end - class ContributorsBase - include Base - + class ContributorsBase < Base def_array_element("contributor") - class ContributorBase - include Base + class ContributorBase < Base include AtomPersonConstructBase end end - class GeneratorBase - include Base - + class GeneratorBase < Base %w(uri version content).each do |element| attr_accessor element add_need_initialize_variable(element) end end - class CopyrightBase - include Base + class CopyrightBase < Base include AtomTextConstructBase end - class DescriptionBase - include Base + class DescriptionBase < Base include AtomTextConstructBase end - class TitleBase - include Base + class TitleBase < Base include AtomTextConstructBase end end - class ImageBase - include Base - + class ImageBase < Base %w(title url width height description).each do |element| attr_accessor element add_need_initialize_variable(element) @@ -610,9 +605,7 @@ EOC end end - class ItemsBase - include Base - + class ItemsBase < Base def_array_element("item") attr_accessor :do_sort, :max_size @@ -646,27 +639,25 @@ EOC end end - class ItemBase - include Base + class ItemBase < Base include SetupDefaultDate - %w(guid enclosure source categories authors links - contributors rights description content title).each do |element| - attr_reader element - add_other_element(element) - add_need_initialize_variable(element, "make_#{element}") - module_eval(<<-EOC, __FILE__, __LINE__) - private - def setup_#{element}(feed, current) - @#{element}.to_feed(feed, current) - end + %w(guid enclosure source categories content).each do |name| + def_classed_element(name) + end - def make_#{element} - self.class::#{Utils.to_class_name(element)}.new(@maker) - end -EOC + %w(rights description title).each do |name| + def_classed_element(name, nil, "content") end + [ + ["author", "name"], + ["link", "href", Proc.new {|target,| "#{target}.href = 'alternate'"}], + ["contributor", "name"], + ].each do |name, attribute| + def_classed_elements(name, attribute) + end + %w(date comments id published).each do |element| attr_accessor element add_need_initialize_variable(element) @@ -688,42 +679,9 @@ EOC self.date = date end - def author - _link = authors.first - _link ? _author.name : nil - end - - def author=(name) - _author = authors.first || authors.new_author - _author.name = name - end - - def link - _link = links.first - _link ? _link.href : nil - end - - def link=(href) - _link = links.first || links.new_link - _link.rel = "alternate" - _link.href = href - end - - def rights=(content) - @rights.content = content - end - - def description=(content) - @description.content = content - end - alias_method(:summary, :description) alias_method(:summary=, :description=) - def title=(content) - @title.content = content - end - def <=>(other) _date = date || dc_date _other_date = other.date || other.dc_date @@ -738,42 +696,30 @@ EOC end end - class GuidBase - include Base - + class GuidBase < Base %w(isPermaLink content).each do |element| attr_accessor element add_need_initialize_variable(element) end end - class EnclosureBase - include Base - + class EnclosureBase < Base %w(url length type).each do |element| attr_accessor element add_need_initialize_variable(element) end end - class SourceBase - include Base - + class SourceBase < Base %w(authors categories contributors generator icon - links logo rights subtitle title).each do |element| - attr_reader element - add_other_element(element) - add_need_initialize_variable(element, "make_#{element}") - module_eval(<<-EOC, __FILE__, __LINE__) - private - def setup_#{element}(feed, current) - @#{element}.to_feed(feed, current) - end + logo rights subtitle title).each do |name| + def_classed_element(name) + end - def make_#{element} - self.class::#{Utils.to_class_name(element)}.new(@maker) - end - EOC + [ + ["link", "href"], + ].each do |name, attribute| + def_classed_elements(name, attribute) end %w(id content date).each do |element| @@ -781,15 +727,8 @@ EOC add_need_initialize_variable(element) end - def url - link = links.first - link ? link.href : nil - end - - def url=(value) - link = links.first || links.new_link - link.href = value - end + alias_method(:url, :link) + alias_method(:url=, :link=) def updated date @@ -805,9 +744,7 @@ EOC ContributorsBase = ChannelBase::ContributorsBase GeneratorBase = ChannelBase::GeneratorBase - class IconBase - include Base - + class IconBase < Base %w(url).each do |element| attr_accessor element add_need_initialize_variable(element) @@ -816,27 +753,22 @@ EOC LinksBase = ChannelBase::LinksBase - class LogoBase - include Base - + class LogoBase < Base %w(uri).each do |element| attr_accessor element add_need_initialize_variable(element) end end - class RightsBase - include Base + class RightsBase < Base include AtomTextConstructBase end - class SubtitleBase - include Base + class SubtitleBase < Base include AtomTextConstructBase end - class TitleBase - include Base + class TitleBase < Base include AtomTextConstructBase end end @@ -846,22 +778,19 @@ EOC LinksBase = ChannelBase::LinksBase ContributorsBase = ChannelBase::ContributorsBase - class RightsBase - include Base + class RightsBase < Base include AtomTextConstructBase end - class DescriptionBase - include Base + class DescriptionBase < Base include AtomTextConstructBase end - class ContentBase - include Base + class ContentBase < Base include AtomTextConstructBase::EnsureXMLContent - %w(type src content xml_content).each do |element| - attr element, element != "xml_content" + %w(src).each do |element| + attr_accessor(element) add_need_initialize_variable(element) end @@ -870,13 +799,9 @@ EOC @xml_content = content end - alias_method(:xhtml, :xml_content) - alias_method(:xhtml=, :xml_content=) - alias_method(:xml, :xml_content) alias_method(:xml=, :xml_content=) - private def inline_text? [nil, "text", "html"].include?(@type) end @@ -913,16 +838,13 @@ EOC end end - class TitleBase - include Base + class TitleBase < Base include AtomTextConstructBase end end end - class TextinputBase - include Base - + class TextinputBase < Base %w(title description name link).each do |element| attr_accessor element add_need_initialize_variable(element) diff --git a/lib/rss/maker/content.rb b/lib/rss/maker/content.rb index 18590d0cf8..a1fd283116 100644 --- a/lib/rss/maker/content.rb +++ b/lib/rss/maker/content.rb @@ -7,17 +7,8 @@ module RSS def self.append_features(klass) super - ::RSS::ContentModel::ELEMENTS.each do |element| - klass.add_need_initialize_variable(element) - klass.add_other_element(element) - klass.module_eval(<<-EOC, __FILE__, __LINE__+1) - attr_accessor :#{element} - def setup_#{element}(rss, current) - if #{element} and current.respond_to?(:#{element}=) - current.#{element} = @#{element} if @#{element} - end - end - EOC + ::RSS::ContentModel::ELEMENTS.each do |name| + klass.def_other_element(name) end end end diff --git a/lib/rss/maker/dublincore.rb b/lib/rss/maker/dublincore.rb index 088ae60942..ff4813fe19 100644 --- a/lib/rss/maker/dublincore.rb +++ b/lib/rss/maker/dublincore.rb @@ -15,61 +15,40 @@ module RSS plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}" full_plural_klass_name = "self.class::#{plural_klass_name}" full_klass_name = "#{full_plural_klass_name}::#{klass_name}" - klass.add_need_initialize_variable(full_plural_name, - "make_#{full_plural_name}") - klass.add_other_element(full_plural_name) - klass.module_eval(<<-EOC, __FILE__, __LINE__+1) - attr_accessor :#{full_plural_name} - def make_#{full_plural_name} - #{full_plural_klass_name}.new(@maker) - end - - def setup_#{full_plural_name}(feed, current) - @#{full_plural_name}.to_feed(feed, current) - end - - def #{full_name} - @#{full_plural_name}[0] and @#{full_plural_name}[0].value - end - - def #{full_name}=(new_value) - @#{full_plural_name}[0] = #{full_klass_name}.new(self) - @#{full_plural_name}[0].value = new_value - end - + klass.def_classed_elements(full_name, "value", plural_klass_name, + full_plural_name, name) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) def new_#{full_name}(value=nil) - #{full_name} = #{full_klass_name}.new(self) - #{full_name}.value = value - @#{full_plural_name} << #{full_name} + _#{full_name} = #{full_plural_name}.new_#{name} + _#{full_name}.value = value if block_given? - yield #{full_name} + yield _#{full_name} else - #{full_name} + _#{full_name} end end -EOC + EOC end klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) # For backward compatibility alias #{DC_PREFIX}_rightses #{DC_PREFIX}_rights_list -EOC + EOC end ::RSS::DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name| plural_name ||= "#{name}s" + full_name ||= "#{DC_PREFIX}_#{name}" + full_plural_name ||= "#{DC_PREFIX}_#{plural_name}" klass_name = Utils.to_class_name(name) full_klass_name = "DublinCore#{klass_name}" plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}" - module_eval(<<-EOC, __FILE__, __LINE__) - class #{plural_klass_name}Base - include Base - - def_array_element(#{name.dump}, #{plural_name.dump}) - - class #{klass_name}Base - include Base + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + class #{plural_klass_name}Base < Base + def_array_element(#{name.dump}, #{full_plural_name.dump}, + #{full_klass_name.dump}) + class #{full_klass_name}Base < Base attr_accessor :value add_need_initialize_variable("value") alias_method(:content, :value) @@ -80,12 +59,13 @@ EOC end def to_feed(feed, current) - if value and current.respond_to?(:dc_#{name}) + if value and current.respond_to?(:#{full_name}) new_item = current.class::#{full_klass_name}.new(value) - current.dc_#{plural_name} << new_item + current.#{full_plural_name} << new_item end end end + #{klass_name}Base = #{full_klass_name}Base end EOC end @@ -94,11 +74,13 @@ EOC ::RSS::DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name| plural_name ||= "#{name}s" klass_name = Utils.to_class_name(name) + full_klass_name = "DublinCore#{klass_name}" plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}" klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) class #{plural_klass_name} < #{plural_klass_name}Base - class #{klass_name} < #{klass_name}Base + class #{full_klass_name} < #{full_klass_name}Base end + #{klass_name} = #{full_klass_name} end EOC end diff --git a/lib/rss/maker/entry.rb b/lib/rss/maker/entry.rb index baa22c5bf1..be648832c3 100644 --- a/lib/rss/maker/entry.rb +++ b/lib/rss/maker/entry.rb @@ -75,12 +75,6 @@ module RSS end end - def have_required_values? - set_default_values do - super and title.have_required_values? - end - end - private def required_variable_names %w(id updated) @@ -100,7 +94,7 @@ module RSS if authors.all? {|author| !author.have_required_values?} vars << "author" end - vars << "title" unless title.have_required_values? + vars << "title" unless title {|t| t.have_required_values?} vars end end @@ -126,9 +120,11 @@ module RSS self.id ||= link || @maker.channel.id links.replace(@maker.channel.links) if keep[:links].empty? unless keep[:rights].variable_is_set? - @rights = @maker.channel.rights + @maker.channel.rights {|r| @rights = r} + end + unless keep[:title].variable_is_set? + @maker.channel.title {|t| @title = t} end - @title = @maker.channel.title unless keep[:title].variable_is_set? self.updated ||= @maker.channel.updated super(&block) ensure diff --git a/lib/rss/maker/feed.rb b/lib/rss/maker/feed.rb index ac26788102..95ae735c6b 100644 --- a/lib/rss/maker/feed.rb +++ b/lib/rss/maker/feed.rb @@ -64,7 +64,7 @@ module RSS @maker.items.all? {|item| item.author.to_s.empty?} vars << "author" end - vars << "title" unless title.have_required_values? + vars << "title" unless title {|t| t.have_required_values?} vars end @@ -148,11 +148,11 @@ module RSS def to_feed(feed) logo = feed.class::Logo.new class << logo - alias url= content= + alias_method(:url=, :content=) end set = setup_values(logo) class << logo - undef url= + remove_method(:url=) end if set feed.logo = logo @@ -194,7 +194,7 @@ module RSS def have_required_values? set_default_values do - super and title.have_required_values? + super and title {|t| t.have_required_values?} end end @@ -209,7 +209,7 @@ module RSS def not_set_required_variables vars = super - vars << "title" unless title.have_required_values? + vars << "title" unless title {|t| t.have_required_values?} vars end @@ -282,11 +282,11 @@ module RSS def to_feed(feed, current) icon = current.class::Icon.new class << icon - alias url= content= + alias_method(:url=, :content=) end set = setup_values(icon) class << icon - undef url= + remove_method(:url=) end if set current.icon = icon diff --git a/lib/rss/maker/image.rb b/lib/rss/maker/image.rb index e3469d0597..b95cf4c714 100644 --- a/lib/rss/maker/image.rb +++ b/lib/rss/maker/image.rb @@ -9,20 +9,7 @@ module RSS super name = "#{RSS::IMAGE_PREFIX}_item" - klass.add_need_initialize_variable(name, "make_#{name}") - klass.add_other_element(name) - klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) - attr_reader :#{name} - def setup_#{name}(feed, current) - if @#{name} - @#{name}.to_feed(feed, current) - end - end - - def make_#{name} - self.class::#{Utils.to_class_name(name)}.new(@maker) - end -EOC + klass.def_classed_element(name) end def self.install_image_item(klass) @@ -33,8 +20,7 @@ EOC EOC end - class ImageItemBase - include Base + class ImageItemBase < Base include Maker::DublinCoreModel attr_accessor :about, :resource, :image_width, :image_height @@ -67,20 +53,7 @@ EOC super name = "#{RSS::IMAGE_PREFIX}_favicon" - klass.add_need_initialize_variable(name, "make_#{name}") - klass.add_other_element(name) - klass.module_eval(<<-EOC, __FILE__, __LINE__+1) - attr_reader :#{name} - def setup_#{name}(feed, current) - if @#{name} - @#{name}.to_feed(feed, current) - end - end - - def make_#{name} - self.class::#{Utils.to_class_name(name)}.new(@maker) - end -EOC + klass.def_classed_element(name) end def self.install_image_favicon(klass) @@ -88,11 +61,10 @@ EOC class ImageFavicon < ImageFaviconBase DublinCoreModel.install_dublin_core(self) end -EOC + EOC end - class ImageFaviconBase - include Base + class ImageFaviconBase < Base include Maker::DublinCoreModel attr_accessor :about, :image_size diff --git a/lib/rss/maker/itunes.rb b/lib/rss/maker/itunes.rb new file mode 100644 index 0000000000..86f41e2fd7 --- /dev/null +++ b/lib/rss/maker/itunes.rb @@ -0,0 +1,248 @@ +require 'rss/itunes' +require 'rss/maker/2.0' + +module RSS + module Maker + module ITunesBaseModel + def def_class_accessor(klass, name, type, *args) + name = name.gsub(/-/, "_").gsub(/^itunes_/, '') + full_name = "#{RSS::ITUNES_PREFIX}_#{name}" + case type + when nil + klass.def_other_element(full_name) + when :yes_other + def_yes_other_accessor(klass, full_name) + when :yes_clean_other + def_yes_clean_other_accessor(klass, full_name) + when :csv + def_csv_accessor(klass, full_name) + when :element, :attribute + recommended_attribute_name, = *args + klass_name = "ITunes#{Utils.to_class_name(name)}" + klass.def_classed_element(full_name, klass_name, + recommended_attribute_name) + when :elements + plural_name, recommended_attribute_name = args + plural_name ||= "#{name}s" + full_plural_name = "#{RSS::ITUNES_PREFIX}_#{plural_name}" + klass_name = "ITunes#{Utils.to_class_name(name)}" + plural_klass_name = "ITunes#{Utils.to_class_name(plural_name)}" + def_elements_class_accessor(klass, full_name, full_plural_name, + klass_name, plural_klass_name, + recommended_attribute_name) + end + end + + def def_yes_other_accessor(klass, full_name) + klass.def_other_element(full_name) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + def #{full_name}? + Utils::YesOther.parse(@#{full_name}) + end + EOC + end + + def def_yes_clean_other_accessor(klass, full_name) + klass.def_other_element(full_name) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + def #{full_name}? + Utils::YesCleanOther.parse(#{full_name}) + end + EOC + end + + def def_csv_accessor(klass, full_name) + klass.def_other_element_without_accessor(full_name) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + attr_reader :#{full_name} + def #{full_name}=(value) + @#{full_name} = Utils::CSV.parse(value) + end + EOC + end + + def def_elements_class_accessor(klass, full_name, full_plural_name, + klass_name, plural_klass_name, + recommended_attribute_name=nil) + if recommended_attribute_name + klass.def_classed_elements(full_name, recommended_attribute_name, + plural_klass_name, full_plural_name) + else + klass.def_classed_element(full_plural_name, plural_klass_name) + end + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + def new_#{full_name}(text=nil) + #{full_name} = @#{full_plural_name}.new_#{full_name} + #{full_name}.text = text + if block_given? + yield #{full_name} + else + #{full_name} + end + end + EOC + end + end + + module ITunesChannelModel + extend ITunesBaseModel + + class << self + def append_features(klass) + super + + ::RSS::ITunesChannelModel::ELEMENT_INFOS.each do |name, type, *args| + def_class_accessor(klass, name, type, *args) + end + end + end + + class ITunesCategoriesBase < Base + def_array_element("category", "itunes_categories", + "ITunesCategory") + class ITunesCategoryBase < Base + attr_accessor :text + add_need_initialize_variable("text") + def_array_element("category", "itunes_categories", + "ITunesCategory") + + def have_required_values? + text + end + + alias_method :to_feed_for_categories, :to_feed + def to_feed(feed, current) + if text and current.respond_to?(:itunes_category) + new_item = current.class::ITunesCategory.new(text) + to_feed_for_categories(feed, new_item) + current.itunes_categories << new_item + end + end + end + end + + class ITunesImageBase < Base + add_need_initialize_variable("href") + attr_accessor("href") + + def to_feed(feed, current) + if @href and current.respond_to?(:itunes_image) + current.itunes_image ||= current.class::ITunesImage.new + current.itunes_image.href = @href + end + end + end + + class ITunesOwnerBase < Base + %w(itunes_name itunes_email).each do |name| + add_need_initialize_variable(name) + attr_accessor(name) + end + + def to_feed(feed, current) + if current.respond_to?(:itunes_owner=) + _not_set_required_variables = not_set_required_variables + if (required_variable_names - _not_set_required_variables).empty? + return + end + + unless have_required_values? + raise NotSetError.new("maker.channel.itunes_owner", + _not_set_required_variables) + end + current.itunes_owner ||= current.class::ITunesOwner.new + current.itunes_owner.itunes_name = @itunes_name + current.itunes_owner.itunes_email = @itunes_email + end + end + + private + def required_variable_names + %w(itunes_name itunes_email) + end + end + end + + module ITunesItemModel + extend ITunesBaseModel + + class << self + def append_features(klass) + super + + ::RSS::ITunesItemModel::ELEMENT_INFOS.each do |name, type, *args| + def_class_accessor(klass, name, type, *args) + end + end + end + + class ITunesDurationBase < Base + attr_reader :content + add_need_initialize_variable("content") + + %w(hour minute second).each do |name| + attr_reader(name) + add_need_initialize_variable(name, '0') + end + + def content=(content) + if content.nil? + @hour, @minute, @second, @content = nil + else + @hour, @minute, @second = + ::RSS::ITunesItemModel::ITunesDuration.parse(content) + @content = content + end + end + + def hour=(hour) + @hour = Integer(hour) + update_content + end + + def minute=(minute) + @minute = Integer(minute) + update_content + end + + def second=(second) + @second = Integer(second) + update_content + end + + def to_feed(feed, current) + if @content and current.respond_to?(:itunes_duration=) + current.itunes_duration ||= current.class::ITunesDuration.new + current.itunes_duration.content = @content + end + end + + private + def update_content + components = [@hour, @minute, @second] + @content = + ::RSS::ITunesItemModel::ITunesDuration.construct(*components) + end + end + end + + class ChannelBase + include Maker::ITunesChannelModel + class ITunesCategories < ITunesCategoriesBase + class ITunesCategory < ITunesCategoryBase + ITunesCategory = self + end + end + + class ITunesImage < ITunesImageBase; end + class ITunesOwner < ITunesOwnerBase; end + end + + class ItemsBase + class ItemBase + include Maker::ITunesItemModel + class ITunesDuration < ITunesDurationBase; end + end + end + end +end diff --git a/lib/rss/maker/syndication.rb b/lib/rss/maker/syndication.rb index 3717086257..b81230457c 100644 --- a/lib/rss/maker/syndication.rb +++ b/lib/rss/maker/syndication.rb @@ -7,17 +7,8 @@ module RSS def self.append_features(klass) super - ::RSS::SyndicationModel::ELEMENTS.each do |element| - klass.add_need_initialize_variable(element) - klass.add_other_element(element) - klass.module_eval(<<-EOC, __FILE__, __LINE__+1) - attr_accessor :#{element} - def setup_#{element}(rss, current) - if #{element} and current.respond_to?(:#{element}=) - current.#{element} = @#{element} if @#{element} - end - end - EOC + ::RSS::SyndicationModel::ELEMENTS.each do |name| + klass.def_other_element(name) end end end diff --git a/lib/rss/maker/taxonomy.rb b/lib/rss/maker/taxonomy.rb index 2e53a4e1f4..798b239df9 100644 --- a/lib/rss/maker/taxonomy.rb +++ b/lib/rss/maker/taxonomy.rb @@ -8,18 +8,8 @@ module RSS def self.append_features(klass) super - klass.add_need_initialize_variable("taxo_topics", "make_taxo_topics") - klass.add_other_element("taxo_topics") - klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) - attr_reader :taxo_topics - def make_taxo_topics - self.class::TaxonomyTopics.new(@maker) - end - - def setup_taxo_topics(feed, current) - @taxo_topics.to_feed(feed, current) - end -EOC + klass.def_classed_element("#{RSS::TAXO_PREFIX}_topics", + "TaxonomyTopics") end def self.install_taxo_topics(klass) @@ -39,9 +29,7 @@ EOC EOC end - class TaxonomyTopicsBase - include Base - + class TaxonomyTopicsBase < Base attr_reader :resources def_array_element("resource") remove_method :new_resource @@ -52,29 +40,10 @@ EOC def self.append_features(klass) super - klass.add_need_initialize_variable("taxo_topics", "make_taxo_topics") - klass.add_other_element("taxo_topics") - klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) - attr_reader :taxo_topics - def make_taxo_topics - self.class::TaxonomyTopics.new(@maker) - end - - def setup_taxo_topics(feed, current) - @taxo_topics.to_feed(feed, current) - end - - def taxo_topic - @taxo_topics[0] and @taxo_topics[0].value - end - - def taxo_topic=(new_value) - @taxo_topic[0] = self.class::TaxonomyTopic.new(self) - @taxo_topic[0].value = new_value - end -EOC + class_name = "TaxonomyTopics" + klass.def_classed_elements("#{TAXO_PREFIX}_topic", "value", class_name) end - + def self.install_taxo_topic(klass) klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) class TaxonomyTopics < TaxonomyTopicsBase @@ -96,13 +65,10 @@ EOC EOC end - class TaxonomyTopicsBase - include Base - - def_array_element("taxo_topic", nil, "self.class::TaxonomyTopic") + class TaxonomyTopicsBase < Base + def_array_element("taxo_topic", nil, "TaxonomyTopic") - class TaxonomyTopicBase - include Base + class TaxonomyTopicBase < Base include DublinCoreModel include TaxonomyTopicsModel diff --git a/lib/rss/maker/trackback.rb b/lib/rss/maker/trackback.rb index 09a2fceb2d..278fe53ebe 100644 --- a/lib/rss/maker/trackback.rb +++ b/lib/rss/maker/trackback.rb @@ -8,41 +8,15 @@ module RSS def self.append_features(klass) super - name = "#{RSS::TRACKBACK_PREFIX}_ping" - klass.add_need_initialize_variable(name) - klass.add_other_element(name) - klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) - attr_accessor :#{name} - def setup_#{name}(feed, current) - if #{name} and current.respond_to?(:#{name}=) - current.#{name} = #{name} - end - end - EOC - - name = "#{RSS::TRACKBACK_PREFIX}_abouts" - klass.add_need_initialize_variable(name, "make_#{name}") - klass.add_other_element(name) - klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) - attr_accessor :#{name} - def make_#{name} - self.class::TrackBackAbouts.new(self) - end - - def setup_#{name}(feed, current) - @#{name}.to_feed(feed, current) - end - EOC + klass.def_other_element("#{RSS::TRACKBACK_PREFIX}_ping") + klass.def_classed_elements("#{RSS::TRACKBACK_PREFIX}_about", "value", + "TrackBackAbouts") end - class TrackBackAboutsBase - include Base - - def_array_element("about", nil, "self.class::TrackBackAbout") - - class TrackBackAboutBase - include Base + class TrackBackAboutsBase < Base + def_array_element("about", nil, "TrackBackAbout") + class TrackBackAboutBase < Base attr_accessor :value add_need_initialize_variable("value") diff --git a/lib/rss/parser.rb b/lib/rss/parser.rb index f5ea2bbc03..b7a76397b8 100644 --- a/lib/rss/parser.rb +++ b/lib/rss/parser.rb @@ -222,25 +222,22 @@ module RSS @@accessor_bases[uri][tag_name] = accessor_base.chomp("=") end - def def_get_text_element(uri, name, file, line) - register_uri(uri, name) - unless private_instance_methods(false).include?("start_#{name}".to_sym) - module_eval(<<-EOT, file, line) - def start_#{name}(name, prefix, attrs, ns) + def def_get_text_element(uri, element_name, file, line) + register_uri(uri, element_name) + method_name = "start_#{element_name}" + unless private_method_defined?(method_name) + define_method(method_name) do |name, prefix, attrs, ns| uri = _ns(ns, prefix) - if self.class.uri_registered?(uri, #{name.inspect}) + if self.class.uri_registered?(uri, element_name) start_get_text_element(name, prefix, ns, uri) else start_else_element(name, prefix, attrs, ns) end end - EOT - __send!("private", "start_#{name}") + private(method_name) end end - end - end module ListenerMixin @@ -368,10 +365,10 @@ module RSS def start_else_element(local, prefix, attrs, ns) class_name = self.class.class_name(_ns(ns, prefix), local) current_class = @last_element.class - if current_class.const_defined?(class_name) + next_class = nil + begin next_class = current_class.const_get(class_name) - start_have_something_element(local, prefix, attrs, ns, next_class) - else + rescue NameError if !@do_validate or @ignore_unknown_element @proc_stack.push(nil) else @@ -382,6 +379,9 @@ module RSS raise NotExpectedTagError.new(local, _ns(ns, prefix), parent) end end + if next_class + start_have_something_element(local, prefix, attrs, ns, next_class) + end end NAMESPLIT = /^(?:([\w:][-\w\d.]*):)?([\w:][-\w\d.]*)/ @@ -460,7 +460,7 @@ module RSS previous = @last_element next_element = klass.new(@do_validate, attributes) - previous.__send!(:set_next_element, tag_name, next_element) + previous.set_next_element(tag_name, next_element) @last_element = next_element @last_element.parent = previous if klass.need_parent? @xml_child_mode = @last_element.have_xml_content? diff --git a/lib/rss/rss.rb b/lib/rss/rss.rb index c0ce96d6bf..fb777b1d53 100644 --- a/lib/rss/rss.rb +++ b/lib/rss/rss.rb @@ -53,7 +53,7 @@ require "rss/xml-stylesheet" module RSS - VERSION = "0.1.7" + VERSION = "0.1.8" URI = "http://purl.org/rss/1.0/" @@ -162,7 +162,6 @@ module RSS end module BaseModel - include Utils def install_have_child_element(tag_name, uri, occurs, name=nil, type=nil) @@ -190,7 +189,7 @@ EOC 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) + install_model(tag_name, uri, occurs, plural_name, true) def_children_accessor(name, plural_name) install_element(name, "s") do |n, elem_name| @@ -205,20 +204,26 @@ EOC end end - def install_text_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil) + 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 + def_corresponded_attr_writer(name, type, disp_name) + def_corresponded_attr_reader(name, type || :convert) install_element(name) do |n, elem_name| <<-EOC - if @#{n} + if respond_to?(:#{n}_content) + content = #{n}_content + else + content = @#{n} + end + if content rv = "\#{indent}<#{elem_name}>" - value = html_escape(@#{n}) + value = html_escape(content) if need_convert rv << convert(value) else @@ -331,6 +336,46 @@ EOC end end + def yes_clean_other_attr_reader(*attrs) + attrs.each do |attr| + attr = attr.id2name if attr.kind_of?(Integer) + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + attr_reader(:#{attr}) + def #{attr}? + YesCleanOther.parse(@#{attr}) + end + EOC + end + end + + def yes_other_attr_reader(*attrs) + attrs.each do |attr| + attr = attr.id2name if attr.kind_of?(Integer) + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + attr_reader(:#{attr}) + def #{attr}? + Utils::YesOther.parse(@#{attr}) + end + EOC + end + end + + def csv_attr_reader(*attrs) + attrs.each do |attr| + attr = attr.id2name if attr.kind_of?(Integer) + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + attr_reader(:#{attr}) + def #{attr}_content + if @#{attr}.nil? + @#{attr} + else + @#{attr}.join(", ") + 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) @@ -458,6 +503,34 @@ EOC EOC end + def yes_clean_other_writer(name, disp_name=name) + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + def #{name}=(value) + value = (value ? "yes" : "no") if [true, false].include?(value) + @#{name} = value + end + EOC + end + + def yes_other_writer(name, disp_name=name) + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + def #{name}=(new_value) + if [true, false].include?(new_value) + new_value = new_value ? "yes" : "no" + end + @#{name} = new_value + end + EOC + end + + def csv_writer(name, disp_name=name) + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + def #{name}=(new_value) + @#{name} = Utils::CSV.parse(new_value) + end + EOC + end + def def_children_accessor(accessor_name, plural_name) module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{plural_name} @@ -511,6 +584,7 @@ EOC def setup_maker_element(target) self.class.need_initialize_variables.each do |var| value = __send__(var) + next if value.nil? if value.respond_to?("setup_maker") and !not_need_to_call_setup_maker_variables.include?(var) value.setup_maker(target) @@ -540,9 +614,9 @@ EOC end class Element - extend BaseModel include Utils + extend Utils::InheritedReader include SetupMaker INDENT = " " @@ -554,32 +628,34 @@ EOC TO_ELEMENT_METHODS = [] NEED_INITIALIZE_VARIABLES = [] PLURAL_FORMS = {} - - class << self + class << self def must_call_validators - MUST_CALL_VALIDATORS + inherited_hash_reader("MUST_CALL_VALIDATORS") end def models - MODELS + inherited_array_reader("MODELS") end def get_attributes - GET_ATTRIBUTES + inherited_array_reader("GET_ATTRIBUTES") end def have_children_elements - HAVE_CHILDREN_ELEMENTS + inherited_array_reader("HAVE_CHILDREN_ELEMENTS") end def to_element_methods - TO_ELEMENT_METHODS + inherited_array_reader("TO_ELEMENT_METHODS") end def need_initialize_variables - NEED_INITIALIZE_VARIABLES + inherited_array_reader("NEED_INITIALIZE_VARIABLES") end def plural_forms - PLURAL_FORMS + inherited_hash_reader("PLURAL_FORMS") + end + + def inherited_base + ::RSS::Element end - def inherited(klass) klass.const_set("MUST_CALL_VALIDATORS", {}) klass.const_set("MODELS", []) @@ -589,123 +665,108 @@ EOC 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 + tag_name = klass.name.split(/::/).last + tag_name[0, 1] = tag_name[0, 1].downcase + klass.instance_variable_set("@tag_name", tag_name) + klass.instance_variable_set("@have_content", false) + end - 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) + def install_must_call_validator(prefix, uri) + self::MUST_CALL_VALIDATORS[uri] = prefix + end + + def install_model(tag, uri, occurs=nil, getter=nil, plural=false) + getter ||= tag + if m = self::MODELS.find {|t, u, o, g, p| t == tag and u == uri} + m[2] = occurs + else + self::MODELS << [tag, uri, occurs, getter, plural] end + end - - def self.install_must_call_validator(prefix, uri) - MUST_CALL_VALIDATORS[uri] = prefix + def install_get_attribute(name, uri, required=true, + 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 - - 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 + self::GET_ATTRIBUTES << [name, uri, required, element_name] + add_need_initialize_variable(disp_name) + end + + def def_corresponded_attr_writer(name, type=nil, disp_name=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 + when :text_type + text_type_writer name, disp_name + when :content + content_writer name, disp_name + when :yes_clean_other + yes_clean_other_writer name, disp_name + when :yes_other + yes_other_writer name, disp_name + when :csv + csv_writer name + else + attr_writer name 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 - 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, element_name] - add_need_initialize_variable(disp_name) + def def_corresponded_attr_reader(name, type=nil) + case type + when :inherit + inherit_convert_attr_reader name + when :uri + uri_convert_attr_reader name + when :yes_clean_other + yes_clean_other_attr_reader name + when :yes_other + yes_other_attr_reader name + when :csv + csv_attr_reader name + else + convert_attr_reader name end + end - def self.def_corresponded_attr_writer(name, type=nil, disp_name=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 - when :text_type - text_type_writer name, disp_name - when :content - content_writer name, disp_name - else - attr_writer name - end - end + def 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 - 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 have_content? + @have_content + 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 + def add_have_children_element(variable_name, plural_name) + self::HAVE_CHILDREN_ELEMENTS << [variable_name, plural_name] + end - def self.have_content? - @have_content - end + def add_to_element_method(method_name) + self::TO_ELEMENT_METHODS << method_name + 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 add_need_initialize_variable(variable_name) + self::NEED_INITIALIZE_VARIABLES << variable_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 + def add_plural_form(singular, plural) + self::PLURAL_FORMS[singular] = plural end def required_prefix @@ -719,7 +780,7 @@ EOC def need_parent? false end - + def install_ns(prefix, uri) if self::NSPOOL.has_key?(prefix) raise OverlappedPrefixError.new(prefix) @@ -821,6 +882,36 @@ EOC false 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.gsub(/-/, '_')}" + if self.class.plural_forms.has_key?(key) + ary = __send__("#{self.class.plural_forms[key]}") + ary << next_element + else + __send__("#{key}=", next_element) + end + end + + protected + 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? {|c| c.have_required_elements?} + else + !child.to_s.empty? + end + else + true + end + end + end + private def initialize_variables(attrs) normalized_attrs = {} @@ -832,7 +923,7 @@ EOC if value __send__("#{variable_name}=", value) else - instance_eval("@#{variable_name} = nil") + instance_variable_set("@#{variable_name}", nil) end end initialize_have_children_elements @@ -841,7 +932,7 @@ EOC def initialize_have_children_elements self.class.have_children_elements.each do |variable_name, plural_name| - instance_eval("@#{variable_name} = []") + instance_variable_set("@#{variable_name}", []) end end @@ -911,19 +1002,6 @@ EOC '' 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| @@ -939,10 +1017,10 @@ EOC def _tags rv = [] - self.class.models.each do |name, uri, occurs, getter| + self.class.models.each do |name, uri, occurs, getter, plural| value = __send__(getter) next if value.nil? - if value.is_a?(Array) + if plural and value.is_a?(Array) rv.concat([[uri, name]] * value.size) else rv << [uri, name] @@ -997,11 +1075,12 @@ EOC tags = tags.sort_by {|x| element_names.index(x) || tags_size} end + _tags = tags.dup if tags models.each_with_index do |model, i| name, model_uri, occurs, getter = model if DEBUG - p "before" + p "before" p tags p model end @@ -1095,7 +1174,7 @@ EOC if have_xml_content? __send__(self.class.xml_getter) else - @content + content end end @@ -1103,25 +1182,9 @@ EOC 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 + _content = content + _content = Base64.encode64(_content) if need_base64_encode? + h(_content) end end end diff --git a/lib/rss/utils.rb b/lib/rss/utils.rb index 031ff3072b..0b53a19d3b 100644 --- a/lib/rss/utils.rb +++ b/lib/rss/utils.rb @@ -2,14 +2,16 @@ module RSS module Utils module_function def to_class_name(name) - name.split(/_/).collect do |part| + name.split(/[_\-]/).collect do |part| "#{part[0, 1].upcase}#{part[1..-1]}" end.join("") end def get_file_and_line_from_caller(i=0) file, line, = caller[i].split(':') - [file, line.to_i] + line = line.to_i + line += 1 if i.zero? + [file, line] end def html_escape(s) @@ -28,5 +30,75 @@ module RSS def element_initialize_arguments?(args) [true, false].include?(args[0]) and args[1].is_a?(Hash) end + + module YesCleanOther + module_function + def parse(value) + if [true, false, nil].include?(value) + value + else + case value.to_s + when /\Ayes\z/i + true + when /\Aclean\z/i + false + else + nil + end + end + end + end + + module YesOther + module_function + def parse(value) + if [true, false].include?(value) + value + else + /\Ayes\z/i.match(value.to_s) ? true : false + end + end + end + + module CSV + module_function + def parse(value) + if value.is_a?(String) + value.strip.split(/\s*,\s*/) + else + value + end + end + end + + module InheritedReader + def inherited_reader(constant_name) + base_class = inherited_base + result = base_class.const_get(constant_name) + found_base_class = false + ancestors.reverse_each do |klass| + if found_base_class + if klass.const_defined?(constant_name) + result = yield(result, klass.const_get(constant_name)) + end + else + found_base_class = klass == base_class + end + end + result + end + + def inherited_array_reader(constant_name) + inherited_reader(constant_name) do |result, current| + current + result + end + end + + def inherited_hash_reader(constant_name) + inherited_reader(constant_name) do |result, current| + result.merge(current) + end + end + end end end diff --git a/sample/rss/blend.rb b/sample/rss/blend.rb index 7d6b239613..351f6f373f 100755 --- a/sample/rss/blend.rb +++ b/sample/rss/blend.rb @@ -66,7 +66,7 @@ rss = RSS::Maker.make("1.0") do |maker| end maker.items.each do |item| - item.title.content ||= "UNKNOWN" + item.title ||= "UNKNOWN" item.link ||= "UNKNOWN" end diff --git a/sample/rss/list_description.rb b/sample/rss/list_description.rb index 5f7069f3db..d4b98a9ac6 100755 --- a/sample/rss/list_description.rb +++ b/sample/rss/list_description.rb @@ -59,9 +59,9 @@ ARGV.each do |fname| rss = rss.to_rss("1.0") do |maker| maker.channel.about ||= maker.channel.link - maker.channel.description.content ||= "No description" + maker.channel.description ||= "No description" maker.items.each do |item| - item.title.content ||= "No title" + item.title ||= "No title" item.link ||= "UNKNOWN" end end diff --git a/sample/rss/rss_recent.rb b/sample/rss/rss_recent.rb index 1b02835ad1..38b57c37fa 100755 --- a/sample/rss/rss_recent.rb +++ b/sample/rss/rss_recent.rb @@ -58,8 +58,9 @@ ARGV.each do |fname| rss = rss.to_rss("1.0") do |maker| maker.channel.about ||= maker.channel.link - maker.channel.description.content ||= "No description" + maker.channel.description ||= "No description" maker.items.each do |item| + item.title ||= "UNKNOWN" item.link ||= "UNKNOWN" end end diff --git a/test/rss/rss-assertions.rb b/test/rss/rss-assertions.rb index 01f68678f0..3b212c794d 100644 --- a/test/rss/rss-assertions.rb +++ b/test/rss/rss-assertions.rb @@ -1,3 +1,5 @@ +require 'erb' + module RSS module Assertions def assert_parse(rss, assert_method, *args) @@ -90,6 +92,7 @@ module RSS flunk("Not raise NotSetError") rescue ::RSS::NotSetError => e assert_equal(name, e.name) + assert_kind_of(Array, variables) assert_equal(variables.sort, e.variables.sort) end end @@ -152,237 +155,6 @@ module RSS end end - - def assert_channel10(attrs, channel) - _wrap_assertion do - n_attrs = normalized_attrs(attrs) - - names = %w(about title link description) - assert_attributes(attrs, names, channel) - - %w(image items textinput).each do |name| - value = n_attrs[name] - if value - target = channel.__send__(name) - __send__("assert_channel10_#{name}", value, target) - end - end - end - end - - def assert_channel10_image(attrs, image) - _wrap_assertion do - assert_attributes(attrs, %w(resource), image) - end - end - - def assert_channel10_textinput(attrs, textinput) - _wrap_assertion do - assert_attributes(attrs, %w(resource), textinput) - end - end - - def assert_channel10_items(attrs, items) - _wrap_assertion do - assert_equal(items.resources, items.Seq.lis.collect {|x| x.resource}) - items.Seq.lis.each_with_index do |li, i| - assert_attributes(attrs[i], %w(resource), li) - end - end - end - - def assert_image10(attrs, image) - _wrap_assertion do - names = %w(about title url link) - assert_attributes(attrs, names, image) - end - end - - def assert_items10(attrs, items) - _wrap_assertion do - names = %w(about title link description) - items.each_with_index do |item, i| - assert_attributes(attrs[i], names, item) - end - end - end - - def assert_textinput10(attrs, textinput) - _wrap_assertion do - names = %w(about title description name link) - assert_attributes(attrs, names, textinput) - end - end - - - def assert_channel09(attrs, channel) - _wrap_assertion do - n_attrs = normalized_attrs(attrs) - - names = %w(title description link language rating - copyright pubDate lastBuildDate docs - managingEditor webMaster) - assert_attributes(attrs, names, channel) - - %w(skipHours skipDays).each do |name| - value = n_attrs[name] - if value - target = channel.__send__(name) - __send__("assert_channel09_#{name}", value, target) - end - end - end - end - - def assert_channel09_skipDays(contents, skipDays) - _wrap_assertion do - days = skipDays.days - contents.each_with_index do |content, i| - assert_equal(content, days[i].content) - end - end - end - - def assert_channel09_skipHours(contents, skipHours) - _wrap_assertion do - hours = skipHours.hours - contents.each_with_index do |content, i| - assert_equal(content.to_i, hours[i].content) - end - end - end - - def assert_image09(attrs, image) - _wrap_assertion do - names = %w(url link title description) - names << ["width", :integer] - names << ["height", :integer] - assert_attributes(attrs, names, image) - end - end - - def assert_items09(attrs, items) - _wrap_assertion do - names = %w(title link description) - items.each_with_index do |item, i| - assert_attributes(attrs[i], names, item) - end - end - end - - def assert_textinput09(attrs, textinput) - _wrap_assertion do - names = %w(title description name link) - assert_attributes(attrs, names, textinput) - end - end - - - def assert_channel20(attrs, channel) - _wrap_assertion do - n_attrs = normalized_attrs(attrs) - - names = %w(title link description language copyright - managingEditor webMaster pubDate - lastBuildDate generator docs rating) - names << ["ttl", :integer] - assert_attributes(attrs, names, channel) - - %w(cloud categories skipHours skipDays).each do |name| - value = n_attrs[name] - if value - target = channel.__send__(name) - __send__("assert_channel20_#{name}", value, target) - end - end - end - end - - def assert_channel20_skipDays(contents, skipDays) - assert_channel09_skipDays(contents, skipDays) - end - - def assert_channel20_skipHours(contents, skipHours) - assert_channel09_skipHours(contents, skipHours) - end - - def assert_channel20_cloud(attrs, cloud) - _wrap_assertion do - names = %w(domain path registerProcedure protocol) - names << ["port", :integer] - assert_attributes(attrs, names, cloud) - end - end - - def assert_channel20_categories(attrs, categories) - _wrap_assertion do - names = %w(domain content) - categories.each_with_index do |category, i| - assert_attributes(attrs[i], names, category) - end - end - end - - def assert_image20(attrs, image) - _wrap_assertion do - names = %w(url link title description) - names << ["width", :integer] - names << ["height", :integer] - assert_attributes(attrs, names, image) - end - end - - def assert_items20(attrs, items) - _wrap_assertion do - names = %w(about title link description) - items.each_with_index do |item, i| - assert_attributes(attrs[i], names, item) - - n_attrs = normalized_attrs(attrs[i]) - - %w(source enclosure categories guid).each do |name| - value = n_attrs[name] - if value - target = item.__send__(name) - __send__("assert_items20_#{name}", value, target) - end - end - end - end - end - - def assert_items20_source(attrs, source) - _wrap_assertion do - assert_attributes(attrs, %w(url content), source) - end - end - - def assert_items20_enclosure(attrs, enclosure) - _wrap_assertion do - names = ["url", ["length", :integer], "type"] - assert_attributes(attrs, names, enclosure) - end - end - - def assert_items20_categories(attrs, categories) - _wrap_assertion do - assert_channel20_categories(attrs, categories) - end - end - - def assert_items20_guid(attrs, guid) - _wrap_assertion do - names = [["isPermaLink", :boolean], ["content"]] - assert_attributes(attrs, names, guid) - end - end - - def assert_textinput20(attrs, textinput) - _wrap_assertion do - names = %w(title description name link) - assert_attributes(attrs, names, textinput) - end - end def assert_atom_person(tag_name, generator) _wrap_assertion do @@ -1146,401 +918,6 @@ EOA end end - - def assert_atom_person_to_s(target_class) - _wrap_assertion do - name = "A person" - uri = "http://example.com/person/" - email = "person@example.com" - - target = target_class.new - assert_equal("", target.to_s) - - target = target_class.new - person_name = target_class::Name.new - person_name.content = name - target.name = person_name - xml_target = REXML::Document.new(target.to_s).root - assert_equal(["name"], xml_target.elements.collect {|e| e.name}) - assert_equal([name], xml_target.elements.collect {|e| e.text}) - - person_uri = target_class::Uri.new - person_uri.content = uri - target.uri = person_uri - xml_target = REXML::Document.new(target.to_s).root - assert_equal(["name", "uri"], xml_target.elements.collect {|e| e.name}) - assert_equal([name, uri], xml_target.elements.collect {|e| e.text}) - - person_email = target_class::Email.new - person_email.content = email - target.email = person_email - xml_target = REXML::Document.new(target.to_s).root - assert_equal(["name", "uri", "email"], - xml_target.elements.collect {|e| e.name}) - assert_equal([name, uri, email], - xml_target.elements.collect {|e| e.text}) - end - end - - def assert_atom_category_to_s(target_class) - _wrap_assertion do - term = "music" - scheme = "http://example.com/music" - label = "Music" - - category = target_class.new - assert_equal("", category.to_s) - - category = target_class.new - category.scheme = scheme - assert_equal("", category.to_s) - - category = target_class.new - category.label = label - assert_equal("", category.to_s) - - category = target_class.new - category.scheme = scheme - category.label = label - assert_equal("", category.to_s) - - category = target_class.new - category.term = term - xml = REXML::Document.new(category.to_s).root - assert_rexml_element([], {"term" => term}, nil, xml) - - category = target_class.new - category.term = term - category.scheme = scheme - xml = REXML::Document.new(category.to_s).root - assert_rexml_element([], {"term" => term, "scheme" => scheme}, nil, xml) - - category = target_class.new - category.term = term - category.label = label - xml = REXML::Document.new(category.to_s).root - assert_rexml_element([], {"term" => term, "label" => label}, nil, xml) - - category = target_class.new - category.term = term - category.scheme = scheme - category.label = label - xml = REXML::Document.new(category.to_s).root - attrs = {"term" => term, "scheme" => scheme, "label" => label} - assert_rexml_element([], attrs, nil, xml) - end - end - - def assert_atom_generator_to_s(target_class) - _wrap_assertion do - content = "Feed generator" - uri = "http://example.com/generator" - version = "0.0.1" - - generator = target_class.new - assert_equal("", generator.to_s) - - generator = target_class.new - generator.uri = uri - assert_equal("", generator.to_s) - - generator = target_class.new - generator.version = version - assert_equal("", generator.to_s) - - generator = target_class.new - generator.uri = uri - generator.version = version - assert_equal("", generator.to_s) - - generator = target_class.new - generator.content = content - xml = REXML::Document.new(generator.to_s).root - assert_rexml_element([], {}, content, xml) - - generator = target_class.new - generator.content = content - generator.uri = uri - xml = REXML::Document.new(generator.to_s).root - assert_rexml_element([], {"uri" => uri}, content, xml) - - generator = target_class.new - generator.content = content - generator.version = version - xml = REXML::Document.new(generator.to_s).root - assert_rexml_element([], {"version" => version}, content, xml) - - generator = target_class.new - generator.content = content - generator.uri = uri - generator.version = version - xml = REXML::Document.new(generator.to_s).root - assert_rexml_element([], {"uri" => uri, "version" => version}, - content, xml) - end - end - - def assert_atom_icon_to_s(target_class) - _wrap_assertion do - content = "http://example.com/icon.png" - - icon = target_class.new - assert_equal("", icon.to_s) - - icon = target_class.new - icon.content = content - xml = REXML::Document.new(icon.to_s).root - assert_rexml_element([], {}, content, xml) - end - end - - def assert_atom_id_to_s(target_class) - _wrap_assertion do - content = "http://example.com/1" - - id = target_class.new - assert_equal("", id.to_s) - - id = target_class.new - id.content = content - xml = REXML::Document.new(id.to_s).root - assert_rexml_element([], {}, content, xml) - end - end - - def assert_atom_link_to_s(target_class) - _wrap_assertion do - href = "http://example.com/atom.xml" - rel = "self" - type = "application/atom+xml" - hreflang = "ja" - title = "Atom Feed" - length = "801" - - link = target_class.new - assert_equal("", link.to_s) - - link = target_class.new - link.href = href - xml = REXML::Document.new(link.to_s).root - assert_rexml_element([], {"href" => href}, nil, xml) - - optional_arguments = %w(rel type hreflang title length) - optional_arguments.each do |name| - rest = optional_arguments.reject {|x| x == name} - - link = target_class.new - link.__send__("#{name}=", eval(name)) - assert_equal("", link.to_s) - - rest.each do |n| - link.__send__("#{n}=", eval(n)) - assert_equal("", link.to_s) - end - - link = target_class.new - link.href = href - link.__send__("#{name}=", eval(name)) - attrs = [["href", href], [name, eval(name)]] - xml = REXML::Document.new(link.to_s).root - assert_rexml_element([], attrs, nil, xml) - - rest.each do |n| - link.__send__("#{n}=", eval(n)) - attrs << [n, eval(n)] - xml = REXML::Document.new(link.to_s).root - assert_rexml_element([], attrs, nil, xml) - end - end - end - end - - def assert_atom_logo_to_s(target_class) - _wrap_assertion do - content = "http://example.com/logo.png" - - logo = target_class.new - assert_equal("", logo.to_s) - - logo = target_class.new - logo.content = content - xml = REXML::Document.new(logo.to_s).root - assert_rexml_element([], {}, content, xml) - end - end - - def assert_atom_text_construct_to_s(target_class) - _wrap_assertion do - text_content = "plain text" - html_content = "#{text_content}" - xhtml_uri = "http://www.w3.org/1999/xhtml" - xhtml_em = RSS::XML::Element.new("em", nil, xhtml_uri, {}, text_content) - xhtml_content = RSS::XML::Element.new("div", nil, xhtml_uri, - {"xmlns" => xhtml_uri}, - [xhtml_em]) - - text = target_class.new - assert_equal("", text.to_s) - - text = target_class.new - text.type = "text" - assert_equal("", text.to_s) - - text = target_class.new - text.content = text_content - xml = REXML::Document.new(text.to_s).root - assert_rexml_element([], {}, text_content, xml) - - text = target_class.new - text.type = "text" - text.content = text_content - xml = REXML::Document.new(text.to_s).root - assert_rexml_element([], {"type" => "text"}, text_content, xml) - - text = target_class.new - text.type = "html" - text.content = html_content - xml = REXML::Document.new(text.to_s).root - assert_rexml_element([], {"type" => "html"}, html_content, xml) - - text = target_class.new - text.type = "xhtml" - text.content = xhtml_content - assert_equal("", text.to_s) - - text = target_class.new - text.type = "xhtml" - text.__send__(target_class.xml_setter, xhtml_content) - xml = REXML::Document.new(text.to_s).root - assert_rexml_element([[xhtml_uri, "div"]], {"type" => "xhtml"}, - nil, xml) - assert_rexml_element([[xhtml_uri, "em"]], nil, nil, xml.elements[1]) - assert_rexml_element([], {}, text_content, xml.elements[1].elements[1]) - - text = target_class.new - text.type = "xhtml" - text.__send__(target_class.xml_setter, xhtml_em) - xml = REXML::Document.new(text.to_s).root - assert_rexml_element([[xhtml_uri, "div"]], {"type" => "xhtml"}, - nil, xml) - assert_rexml_element([[xhtml_uri, "em"]], nil, nil, xml.elements[1]) - assert_rexml_element([], {}, text_content, xml.elements[1].elements[1]) - end - end - - def assert_atom_date_construct_to_s(target_class) - _wrap_assertion do - date = target_class.new - assert_equal("", date.to_s) - - [ - "2003-12-13T18:30:02Z", - "2003-12-13T18:30:02.25Z", - "2003-12-13T18:30:02+01:00", - "2003-12-13T18:30:02.25+01:00", - ].each do |content| - date = target_class.new - date.content = content - xml = REXML::Document.new(date.to_s).root - assert_rexml_element([], {}, content, xml, :time) - - date = target_class.new - date.content = Time.parse(content) - xml = REXML::Document.new(date.to_s).root - assert_rexml_element([], {}, content, xml, :time) - end - end - end - - def assert_atom_content_to_s(target_class) - _wrap_assertion do - assert_atom_text_construct_to_s(target_class) - assert_atom_content_inline_other_xml_to_s(target_class) - assert_atom_content_inline_other_text_to_s(target_class) - assert_atom_content_inline_other_base64_to_s(target_class) - assert_atom_content_out_of_line_to_s(target_class) - end - end - - def assert_atom_content_inline_other_xml_to_s(target_class) - _wrap_assertion do - content = target_class.new - content.type = "text/xml" - assert_equal("", content.to_s) - - content = target_class.new - content.type = "text/xml" - content.xml = RSS::XML::Element.new("em") - xml = REXML::Document.new(content.to_s).root - assert_rexml_element([["", "em"]], {"type" => "text/xml"}, nil, xml) - end - end - - def assert_atom_content_inline_other_text_to_s(target_class) - _wrap_assertion do - content = target_class.new - content.type = "text/plain" - assert_equal("", content.to_s) - - content = target_class.new - content.type = "text/plain" - content.xml = RSS::XML::Element.new("em") - assert_equal("", content.to_s) - - content = target_class.new - content.type = "text/plain" - content.content = "content" - xml = REXML::Document.new(content.to_s).root - assert_rexml_element([], {"type" => "text/plain"}, "content", xml) - end - end - - def assert_atom_content_inline_other_base64_to_s(target_class) - _wrap_assertion do - require "zlib" - - text = "" - char = "a" - 100.times do |i| - text << char - char.succ! - end - - type = "application/zip" - original_content = Zlib::Deflate.deflate(text) - - content = target_class.new - content.type = type - content.content = original_content - xml = REXML::Document.new(content.to_s).root - assert_rexml_element([], {"type" => type}, - Base64.encode64(original_content), xml) - end - end - - def assert_atom_content_out_of_line_to_s(target_class) - _wrap_assertion do - type = "application/zip" - src = "http://example.com/xxx.zip" - - content = target_class.new - assert(!content.out_of_line?) - content.src = src - assert(content.out_of_line?) - xml = REXML::Document.new(content.to_s).root - assert_rexml_element([], {"src" => src}, nil, xml) - - content = target_class.new - assert(!content.out_of_line?) - content.type = type - assert(!content.out_of_line?) - content.src = src - assert(content.out_of_line?) - xml = REXML::Document.new(content.to_s).root - assert_rexml_element([], {"type" => type, "src" => src}, nil, xml) - end - end - def _assert_maker_atom_persons(feed_type, maker_readers, feed_readers) _wrap_assertion do persons = [] @@ -1565,7 +942,6 @@ EOA :email => person.email ? person.email.content : nil, } end - p [feed_readers, feed.source.authors] unless persons == actual_persons assert_equal(persons, actual_persons) end end @@ -1765,7 +1141,7 @@ EOA assert_not_set_error(not_set_error_name, %w(content)) do _assert_maker_atom_text_construct(*args) do |maker| yield maker - target = chain_reader(maker, maker_readers) + target = chain_reader(maker, maker_readers) {|x| x} target.type = "text" end end @@ -1773,7 +1149,7 @@ EOA assert_not_set_error(not_set_error_name, %w(content)) do _assert_maker_atom_text_construct(*args) do |maker| yield maker - target = chain_reader(maker, maker_readers) + target = chain_reader(maker, maker_readers) {|x| x} target.type = "html" end end @@ -1781,7 +1157,7 @@ EOA assert_not_set_error(not_set_error_name, %w(xml_content)) do _assert_maker_atom_text_construct(*args) do |maker| yield maker - target = chain_reader(maker, maker_readers) + target = chain_reader(maker, maker_readers) {|x| x} target.type = "xhtml" end end @@ -1789,7 +1165,7 @@ EOA assert_not_set_error(not_set_error_name, %w(xml_content)) do _assert_maker_atom_text_construct(*args) do |maker| yield maker - target = chain_reader(maker, maker_readers) + target = chain_reader(maker, maker_readers) {|x| x} target.type = "xhtml" target.content = "Content" end @@ -1797,28 +1173,28 @@ EOA _assert_maker_atom_text_construct(*args) do |maker| yield maker - target = chain_reader(maker, maker_readers) + target = chain_reader(maker, maker_readers) {|x| x} target.type = "text" target.content = "Content" end _assert_maker_atom_text_construct(*args) do |maker| yield maker - target = chain_reader(maker, maker_readers) + target = chain_reader(maker, maker_readers) {|x| x} target.type = "html" target.content = "Content" end _assert_maker_atom_text_construct(*args) do |maker| yield maker - target = chain_reader(maker, maker_readers) + target = chain_reader(maker, maker_readers) {|x| x} target.type = "xhtml" target.xml_content = "text only" end _assert_maker_atom_text_construct(*args) do |maker| yield maker - target = chain_reader(maker, maker_readers) + target = chain_reader(maker, maker_readers) {|x| x} target.type = "xhtml" target.xml_content = RSS::XML::Element.new("unknown") end @@ -1877,7 +1253,7 @@ EOA element = nil feed = RSS::Maker.make("atom:#{feed_type}") do |maker| yield maker - target = chain_reader(maker, maker_readers) + target = chain_reader(maker, maker_readers) {|x| x} element = maker_extractor.call(target) end @@ -1942,7 +1318,7 @@ EOA __send__(assert_method_name, feed_type, maker_readers, feed_readers, *additional_args) do |maker| yield maker - target = chain_reader(maker, maker_readers) + target = chain_reader(maker, maker_readers) {|x| x} setup_target.call(target, have) end end @@ -1950,7 +1326,7 @@ EOA __send__(assert_method_name, feed_type, maker_readers, feed_readers, *additional_args) do |maker| yield maker - target = chain_reader(maker, maker_readers) + target = chain_reader(maker, maker_readers) {|x| x} setup_target.call(target, have_required_variable_too) end end @@ -2302,14 +1678,14 @@ EOA :src => target.src, :content => target.content, :xml => target.xml, - :inline_text => target.__send!(:inline_text?), - :inline_html => target.__send!(:inline_html?), - :inline_xhtml => target.__send!(:inline_xhtml?), - :inline_other => target.__send!(:inline_other?), - :inline_other_text => target.__send!(:inline_other_text?), - :inline_other_xml => target.__send!(:inline_other_xml?), - :inline_other_base64 => target.__send!(:inline_other_base64?), - :out_of_line => target.__send!(:out_of_line?), + :inline_text => target.inline_text?, + :inline_html => target.inline_html?, + :inline_xhtml => target.inline_xhtml?, + :inline_other => target.inline_other?, + :inline_other_text => target.inline_other_text?, + :inline_other_xml => target.inline_other_xml?, + :inline_other_base64 => target.inline_other_base64?, + :out_of_line => target.out_of_line?, } content[:src] = nil if content[:src] and content[:content] if content[:type] or content[:content] @@ -2642,10 +2018,10 @@ EOA end end - def chain_reader(target, readers) + def chain_reader(target, readers, &block) readers.inject(target) do |result, reader| return nil if result.nil? - result.__send__(reader) + result.__send__(reader, &block) end end @@ -2669,5 +2045,18 @@ EOA end + combination(rest, n) end end + + def tag(name, content=nil, attributes={}) + attributes = attributes.collect do |key, value| + "#{ERB::Util.h(key)}=\"#{ERB::Util.h(value)}\"" + end.join(" ") + begin_tag = "<#{name}" + begin_tag << " #{attributes}" unless attributes.empty? + if content + "#{begin_tag}>#{content}\n" + else + "#{begin_tag}/>\n" + end + end end end diff --git a/test/rss/test_atom.rb b/test/rss/test_atom.rb index aea05bebbe..c442c753b2 100644 --- a/test/rss/test_atom.rb +++ b/test/rss/test_atom.rb @@ -217,7 +217,7 @@ module RSS end - def test_to_xml + def test_to_xml(with_convenience_way=true) atom = RSS::Parser.parse(make_feed) assert_equal(atom.to_s, atom.to_xml) assert_equal(atom.to_s, atom.to_xml("atom")) @@ -228,7 +228,11 @@ module RSS rss09_xml = atom.to_xml("0.91") do |maker| maker.channel.language = "en-us" maker.channel.link = "http://example.com/" - maker.channel.description.content = atom.title.content + if with_convenience_way + maker.channel.description = atom.title.content + else + maker.channel.description {|d| d.content = atom.title.content} + end maker.image.url = "http://example.com/logo.png" maker.image.title = "Logo" @@ -238,13 +242,21 @@ module RSS rss20_xml = atom.to_xml("2.0") do |maker| maker.channel.link = "http://example.com/" - maker.channel.description.content = atom.title.content + if with_convenience_way + maker.channel.description = atom.title.content + else + maker.channel.description {|d| d.content = atom.title.content} + end end rss20 = RSS::Parser.parse(rss20_xml) assert_equal("2.0", rss20.rss_version) assert_equal(["rss", "2.0", nil], rss20.feed_info) end + def test_to_xml_with_new_api_since_018 + test_to_xml(false) + end + private def setup_entry(entry) _wrap_assertion do @@ -277,5 +289,400 @@ module RSS assert_not_equal("", entry.to_s) end end + + + def assert_atom_person_to_s(target_class) + _wrap_assertion do + name = "A person" + uri = "http://example.com/person/" + email = "person@example.com" + + target = target_class.new + assert_equal("", target.to_s) + + target = target_class.new + person_name = target_class::Name.new + person_name.content = name + target.name = person_name + xml_target = REXML::Document.new(target.to_s).root + assert_equal(["name"], xml_target.elements.collect {|e| e.name}) + assert_equal([name], xml_target.elements.collect {|e| e.text}) + + person_uri = target_class::Uri.new + person_uri.content = uri + target.uri = person_uri + xml_target = REXML::Document.new(target.to_s).root + assert_equal(["name", "uri"], xml_target.elements.collect {|e| e.name}) + assert_equal([name, uri], xml_target.elements.collect {|e| e.text}) + + person_email = target_class::Email.new + person_email.content = email + target.email = person_email + xml_target = REXML::Document.new(target.to_s).root + assert_equal(["name", "uri", "email"], + xml_target.elements.collect {|e| e.name}) + assert_equal([name, uri, email], + xml_target.elements.collect {|e| e.text}) + end + end + + def assert_atom_category_to_s(target_class) + _wrap_assertion do + term = "music" + scheme = "http://example.com/music" + label = "Music" + + category = target_class.new + assert_equal("", category.to_s) + + category = target_class.new + category.scheme = scheme + assert_equal("", category.to_s) + + category = target_class.new + category.label = label + assert_equal("", category.to_s) + + category = target_class.new + category.scheme = scheme + category.label = label + assert_equal("", category.to_s) + + category = target_class.new + category.term = term + xml = REXML::Document.new(category.to_s).root + assert_rexml_element([], {"term" => term}, nil, xml) + + category = target_class.new + category.term = term + category.scheme = scheme + xml = REXML::Document.new(category.to_s).root + assert_rexml_element([], {"term" => term, "scheme" => scheme}, nil, xml) + + category = target_class.new + category.term = term + category.label = label + xml = REXML::Document.new(category.to_s).root + assert_rexml_element([], {"term" => term, "label" => label}, nil, xml) + + category = target_class.new + category.term = term + category.scheme = scheme + category.label = label + xml = REXML::Document.new(category.to_s).root + attrs = {"term" => term, "scheme" => scheme, "label" => label} + assert_rexml_element([], attrs, nil, xml) + end + end + + def assert_atom_generator_to_s(target_class) + _wrap_assertion do + content = "Feed generator" + uri = "http://example.com/generator" + version = "0.0.1" + + generator = target_class.new + assert_equal("", generator.to_s) + + generator = target_class.new + generator.uri = uri + assert_equal("", generator.to_s) + + generator = target_class.new + generator.version = version + assert_equal("", generator.to_s) + + generator = target_class.new + generator.uri = uri + generator.version = version + assert_equal("", generator.to_s) + + generator = target_class.new + generator.content = content + xml = REXML::Document.new(generator.to_s).root + assert_rexml_element([], {}, content, xml) + + generator = target_class.new + generator.content = content + generator.uri = uri + xml = REXML::Document.new(generator.to_s).root + assert_rexml_element([], {"uri" => uri}, content, xml) + + generator = target_class.new + generator.content = content + generator.version = version + xml = REXML::Document.new(generator.to_s).root + assert_rexml_element([], {"version" => version}, content, xml) + + generator = target_class.new + generator.content = content + generator.uri = uri + generator.version = version + xml = REXML::Document.new(generator.to_s).root + assert_rexml_element([], {"uri" => uri, "version" => version}, + content, xml) + end + end + + def assert_atom_icon_to_s(target_class) + _wrap_assertion do + content = "http://example.com/icon.png" + + icon = target_class.new + assert_equal("", icon.to_s) + + icon = target_class.new + icon.content = content + xml = REXML::Document.new(icon.to_s).root + assert_rexml_element([], {}, content, xml) + end + end + + def assert_atom_id_to_s(target_class) + _wrap_assertion do + content = "http://example.com/1" + + id = target_class.new + assert_equal("", id.to_s) + + id = target_class.new + id.content = content + xml = REXML::Document.new(id.to_s).root + assert_rexml_element([], {}, content, xml) + end + end + + def assert_atom_link_to_s(target_class) + _wrap_assertion do + href = "http://example.com/atom.xml" + rel = "self" + type = "application/atom+xml" + hreflang = "ja" + title = "Atom Feed" + length = "801" + + link = target_class.new + assert_equal("", link.to_s) + + link = target_class.new + link.href = href + xml = REXML::Document.new(link.to_s).root + assert_rexml_element([], {"href" => href}, nil, xml) + + optional_arguments = %w(rel type hreflang title length) + optional_arguments.each do |name| + rest = optional_arguments.reject {|x| x == name} + + link = target_class.new + link.__send__("#{name}=", eval(name)) + assert_equal("", link.to_s) + + rest.each do |n| + link.__send__("#{n}=", eval(n)) + assert_equal("", link.to_s) + end + + link = target_class.new + link.href = href + link.__send__("#{name}=", eval(name)) + attrs = [["href", href], [name, eval(name)]] + xml = REXML::Document.new(link.to_s).root + assert_rexml_element([], attrs, nil, xml) + + rest.each do |n| + link.__send__("#{n}=", eval(n)) + attrs << [n, eval(n)] + xml = REXML::Document.new(link.to_s).root + assert_rexml_element([], attrs, nil, xml) + end + end + end + end + + def assert_atom_logo_to_s(target_class) + _wrap_assertion do + content = "http://example.com/logo.png" + + logo = target_class.new + assert_equal("", logo.to_s) + + logo = target_class.new + logo.content = content + xml = REXML::Document.new(logo.to_s).root + assert_rexml_element([], {}, content, xml) + end + end + + def assert_atom_text_construct_to_s(target_class) + _wrap_assertion do + text_content = "plain text" + html_content = "#{text_content}" + xhtml_uri = "http://www.w3.org/1999/xhtml" + xhtml_em = RSS::XML::Element.new("em", nil, xhtml_uri, {}, text_content) + xhtml_content = RSS::XML::Element.new("div", nil, xhtml_uri, + {"xmlns" => xhtml_uri}, + [xhtml_em]) + + text = target_class.new + assert_equal("", text.to_s) + + text = target_class.new + text.type = "text" + assert_equal("", text.to_s) + + text = target_class.new + text.content = text_content + xml = REXML::Document.new(text.to_s).root + assert_rexml_element([], {}, text_content, xml) + + text = target_class.new + text.type = "text" + text.content = text_content + xml = REXML::Document.new(text.to_s).root + assert_rexml_element([], {"type" => "text"}, text_content, xml) + + text = target_class.new + text.type = "html" + text.content = html_content + xml = REXML::Document.new(text.to_s).root + assert_rexml_element([], {"type" => "html"}, html_content, xml) + + text = target_class.new + text.type = "xhtml" + text.content = xhtml_content + assert_equal("", text.to_s) + + text = target_class.new + text.type = "xhtml" + text.__send__(target_class.xml_setter, xhtml_content) + xml = REXML::Document.new(text.to_s).root + assert_rexml_element([[xhtml_uri, "div"]], {"type" => "xhtml"}, + nil, xml) + assert_rexml_element([[xhtml_uri, "em"]], nil, nil, xml.elements[1]) + assert_rexml_element([], {}, text_content, xml.elements[1].elements[1]) + + text = target_class.new + text.type = "xhtml" + text.__send__(target_class.xml_setter, xhtml_em) + xml = REXML::Document.new(text.to_s).root + assert_rexml_element([[xhtml_uri, "div"]], {"type" => "xhtml"}, + nil, xml) + assert_rexml_element([[xhtml_uri, "em"]], nil, nil, xml.elements[1]) + assert_rexml_element([], {}, text_content, xml.elements[1].elements[1]) + end + end + + def assert_atom_date_construct_to_s(target_class) + _wrap_assertion do + date = target_class.new + assert_equal("", date.to_s) + + [ + "2003-12-13T18:30:02Z", + "2003-12-13T18:30:02.25Z", + "2003-12-13T18:30:02+01:00", + "2003-12-13T18:30:02.25+01:00", + ].each do |content| + date = target_class.new + date.content = content + xml = REXML::Document.new(date.to_s).root + assert_rexml_element([], {}, content, xml, :time) + + date = target_class.new + date.content = Time.parse(content) + xml = REXML::Document.new(date.to_s).root + assert_rexml_element([], {}, content, xml, :time) + end + end + end + + def assert_atom_content_to_s(target_class) + _wrap_assertion do + assert_atom_text_construct_to_s(target_class) + assert_atom_content_inline_other_xml_to_s(target_class) + assert_atom_content_inline_other_text_to_s(target_class) + assert_atom_content_inline_other_base64_to_s(target_class) + assert_atom_content_out_of_line_to_s(target_class) + end + end + + def assert_atom_content_inline_other_xml_to_s(target_class) + _wrap_assertion do + content = target_class.new + content.type = "text/xml" + assert_equal("", content.to_s) + + content = target_class.new + content.type = "text/xml" + content.xml = RSS::XML::Element.new("em") + xml = REXML::Document.new(content.to_s).root + assert_rexml_element([["", "em"]], {"type" => "text/xml"}, nil, xml) + end + end + + def assert_atom_content_inline_other_text_to_s(target_class) + _wrap_assertion do + content = target_class.new + content.type = "text/plain" + assert_equal("", content.to_s) + + content = target_class.new + content.type = "text/plain" + content.xml = RSS::XML::Element.new("em") + assert_equal("", content.to_s) + + content = target_class.new + content.type = "text/plain" + content.content = "content" + xml = REXML::Document.new(content.to_s).root + assert_rexml_element([], {"type" => "text/plain"}, "content", xml) + end + end + + def assert_atom_content_inline_other_base64_to_s(target_class) + _wrap_assertion do + require "zlib" + + text = "" + char = "a" + 100.times do |i| + text << char + char.succ! + end + + type = "application/zip" + original_content = Zlib::Deflate.deflate(text) + + content = target_class.new + content.type = type + content.content = original_content + xml = REXML::Document.new(content.to_s).root + assert_rexml_element([], {"type" => type}, + Base64.encode64(original_content), xml) + end + end + + def assert_atom_content_out_of_line_to_s(target_class) + _wrap_assertion do + type = "application/zip" + src = "http://example.com/xxx.zip" + + content = target_class.new + assert(!content.out_of_line?) + content.src = src + assert(content.out_of_line?) + xml = REXML::Document.new(content.to_s).root + assert_rexml_element([], {"src" => src}, nil, xml) + + content = target_class.new + assert(!content.out_of_line?) + content.type = type + assert(!content.out_of_line?) + content.src = src + assert(content.out_of_line?) + xml = REXML::Document.new(content.to_s).root + assert_rexml_element([], {"type" => type, "src" => src}, nil, xml) + end + end end end diff --git a/test/rss/test_image.rb b/test/rss/test_image.rb index e4e50c0115..45e99c3c23 100644 --- a/test/rss/test_image.rb +++ b/test/rss/test_image.rb @@ -65,12 +65,12 @@ EOR @rss = Parser.parse(@rss_source) end - + def test_parser assert_nothing_raised do Parser.parse(@rss_source) end - + assert_too_much_tag("favicon", "channel") do Parser.parse(make_RDF(<<-EOR, @ns)) #{make_channel(@channel_nodes * 2)} @@ -88,7 +88,7 @@ EOR EOR end end - + def test_favicon_accessor favicon = @rss.channel.image_favicon [ @@ -145,7 +145,7 @@ EOR image_item.__send__("#{name}=", attrs[full_name]) assert_equal(attrs[full_name], image_item.__send__(name)) end - + [ ["width", "image:width", "111"], ["image_width", "image:width", "44"], @@ -188,7 +188,7 @@ EOR actual = REXML::Document.new(item.image_item.to_s(false, "")) assert_equal(expected[0].attributes, actual[0].attributes) - + %w(image:height image:width dc:title).each do |name| actual_target = actual.elements["//#{name}"] expected_target = expected.elements["//#{name}"] diff --git a/test/rss/test_inherit.rb b/test/rss/test_inherit.rb index fc4bbbe76a..f73096f478 100644 --- a/test/rss/test_inherit.rb +++ b/test/rss/test_inherit.rb @@ -9,7 +9,7 @@ module RSS def self.indent_size; 1; end def self.tag_name; 'image'; end end - + def setup @rss = make_RDF(<<-EOR) #{make_channel} @@ -18,7 +18,7 @@ module RSS #{make_textinput} EOR end - + def test_inherit rss = RSS::Parser.parse(@rss) orig_image = rss.image @@ -36,6 +36,5 @@ EOR assert_equal("#{prefix} #{orig_image.url}", new_image.url) assert_equal("#{prefix} #{orig_image.link}", new_image.link) end - end end diff --git a/test/rss/test_itunes.rb b/test/rss/test_itunes.rb new file mode 100644 index 0000000000..3663e2c77a --- /dev/null +++ b/test/rss/test_itunes.rb @@ -0,0 +1,347 @@ +require "cgi" +require "rexml/document" + +require "rss-testcase" + +require "rss/2.0" +require "rss/itunes" + +module RSS + class TestITunes < TestCase + def test_author + assert_itunes_author(%w(channel)) do |content, xmlns| + make_rss20(make_channel20(content), xmlns) + end + + assert_itunes_author(%w(items last)) do |content, xmlns| + make_rss20(make_channel20(make_item20(content)), xmlns) + end + end + + def test_block + assert_itunes_block(%w(items last)) do |content, xmlns| + make_rss20(make_channel20(make_item20(content)), xmlns) + end + end + + def test_category + assert_itunes_category(%w(channel)) do |content, xmlns| + make_rss20(make_channel20(content), xmlns) + end + end + + def test_image + assert_itunes_image(%w(channel)) do |content, xmlns| + make_rss20(make_channel20(content), xmlns) + end + end + + def test_duration + assert_itunes_duration(%w(items last)) do |content, xmlns| + make_rss20(make_channel20(make_item20(content)), xmlns) + end + end + + def test_explicit + assert_itunes_explicit(%w(channel)) do |content, xmlns| + make_rss20(make_channel20(content), xmlns) + end + + assert_itunes_explicit(%w(items last)) do |content, xmlns| + make_rss20(make_channel20(make_item20(content)), xmlns) + end + end + + def test_keywords + assert_itunes_keywords(%w(channel)) do |content, xmlns| + make_rss20(make_channel20(content), xmlns) + end + + assert_itunes_keywords(%w(items last)) do |content, xmlns| + make_rss20(make_channel20(make_item20(content)), xmlns) + end + end + + def test_new_feed_url + assert_itunes_new_feed_url(%w(channel)) do |content, xmlns| + make_rss20(make_channel20(content), xmlns) + end + end + + def test_owner + assert_itunes_owner(%w(channel)) do |content, xmlns| + make_rss20(make_channel20(content), xmlns) + end + end + + def test_subtitle + assert_itunes_subtitle(%w(channel)) do |content, xmlns| + make_rss20(make_channel20(content), xmlns) + end + + assert_itunes_subtitle(%w(items last)) do |content, xmlns| + make_rss20(make_channel20(make_item20(content)), xmlns) + end + end + + def test_summary + assert_itunes_summary(%w(channel)) do |content, xmlns| + make_rss20(make_channel20(content), xmlns) + end + + assert_itunes_summary(%w(items last)) do |content, xmlns| + make_rss20(make_channel20(make_item20(content)), xmlns) + end + end + + private + def itunes_rss20_parse(content, &maker) + xmlns = {"itunes" => "http://www.itunes.com/dtds/podcast-1.0.dtd"} + rss20_xml = maker.call(content, xmlns) + ::RSS::Parser.parse(rss20_xml) + end + + def assert_itunes_author(readers, &rss20_maker) + _wrap_assertion do + author = "John Lennon" + rss20 = itunes_rss20_parse(tag("itunes:author", author), &rss20_maker) + target = chain_reader(rss20, readers) + assert_equal(author, target.itunes_author) + end + end + + def _assert_itunes_block(value, boolean_value, readers, &rss20_maker) + rss20 = itunes_rss20_parse(tag("itunes:block", value), &rss20_maker) + target = chain_reader(rss20, readers) + assert_equal(value, target.itunes_block) + assert_equal(boolean_value, target.itunes_block?) + end + + def assert_itunes_block(readers, &rss20_maker) + _wrap_assertion do + _assert_itunes_block("yes", true, readers, &rss20_maker) + _assert_itunes_block("Yes", true, readers, &rss20_maker) + _assert_itunes_block("no", false, readers, &rss20_maker) + _assert_itunes_block("", false, readers, &rss20_maker) + end + end + + def _assert_itunes_category(categories, readers, &rss20_maker) + cats = categories.collect do |category| + if category.is_a?(Array) + category, sub_category = category + tag("itunes:category", + tag("itunes:category", nil, {"text" => sub_category}), + {"text" => category}) + else + tag("itunes:category", nil, {"text" => category}) + end + end.join + rss20 = itunes_rss20_parse(cats, &rss20_maker) + target = chain_reader(rss20, readers) + actual_categories = target.itunes_categories.collect do |category| + cat = category.text + if category.itunes_categories.empty? + cat + else + [cat, *category.itunes_categories.collect {|c| c.text}] + end + end + assert_equal(categories, actual_categories) + end + + def assert_itunes_category(readers, &rss20_maker) + _wrap_assertion do + _assert_itunes_category(["Audio Blogs"], readers, &rss20_maker) + _assert_itunes_category([["Arts & Entertainment", "Games"]], + readers, &rss20_maker) + _assert_itunes_category([["Arts & Entertainment", "Games"], + ["Technology", "Computers"], + "Audio Blogs"], + readers, &rss20_maker) + end + end + + def assert_itunes_image(readers, &rss20_maker) + _wrap_assertion do + url = "http://example.com/podcasts/everything/AllAboutEverything.jpg" + content = tag("itunes:image", nil, {"href" => url}) + rss20 = itunes_rss20_parse(content, &rss20_maker) + target = chain_reader(rss20, readers) + assert_not_nil(target.itunes_image) + assert_equal(url, target.itunes_image.href) + + assert_missing_attribute("image", "href") do + content = tag("itunes:image") + itunes_rss20_parse(content, &rss20_maker) + end + end + end + + def _assert_itunes_duration(hour, minute, second, value, + readers, &rss20_maker) + content = tag("itunes:duration", value) + rss20 = itunes_rss20_parse(content, &rss20_maker) + duration = chain_reader(rss20, readers).itunes_duration + assert_equal(value, duration.content) + assert_equal(hour, duration.hour) + assert_equal(minute, duration.minute) + assert_equal(second, duration.second) + end + + def _assert_itunes_duration_not_available_value(value, &rss20_maker) + assert_not_available_value("duration", value) do + content = tag("itunes:duration", value) + itunes_rss20_parse(content, &rss20_maker) + end + end + + def assert_itunes_duration(readers, &rss20_maker) + _wrap_assertion do + _assert_itunes_duration(7, 14, 5, "07:14:05", readers, &rss20_maker) + _assert_itunes_duration(7, 14, 5, "7:14:05", readers, &rss20_maker) + _assert_itunes_duration(0, 4, 55, "04:55", readers, &rss20_maker) + _assert_itunes_duration(0, 4, 5, "4:05", readers, &rss20_maker) + + _assert_itunes_duration_not_available_value("5", &rss20_maker) + _assert_itunes_duration_not_available_value("09:07:14:05", &rss20_maker) + _assert_itunes_duration_not_available_value("10:5", &rss20_maker) + _assert_itunes_duration_not_available_value("10:03:5", &rss20_maker) + _assert_itunes_duration_not_available_value("10:3:05", &rss20_maker) + + _assert_itunes_duration_not_available_value("xx:xx:xx", &rss20_maker) + end + end + + def _assert_itunes_explicit(explicit, value, readers, &rss20_maker) + content = tag("itunes:explicit", value) + rss20 = itunes_rss20_parse(content, &rss20_maker) + target = chain_reader(rss20, readers) + assert_equal(value, target.itunes_explicit) + assert_equal(explicit, target.itunes_explicit?) + end + + def assert_itunes_explicit(readers, &rss20_maker) + _wrap_assertion do + _assert_itunes_explicit(true, "yes", readers, &rss20_maker) + _assert_itunes_explicit(false, "clean", readers, &rss20_maker) + _assert_itunes_explicit(nil, "no", readers, &rss20_maker) + end + end + + def _assert_itunes_keywords(keywords, value, readers, &rss20_maker) + content = tag("itunes:keywords", value) + rss20 = itunes_rss20_parse(content, &rss20_maker) + target = chain_reader(rss20, readers) + assert_equal(keywords, target.itunes_keywords) + end + + def assert_itunes_keywords(readers, &rss20_maker) + _wrap_assertion do + _assert_itunes_keywords(["salt"], "salt", readers, &rss20_maker) + _assert_itunes_keywords(["salt"], " salt ", readers, &rss20_maker) + _assert_itunes_keywords(["salt", "pepper", "shaker", "exciting"], + "salt, pepper, shaker, exciting", + readers, &rss20_maker) + _assert_itunes_keywords(["metric", "socket", "wrenches", "toolsalt"], + "metric, socket, wrenches, toolsalt", + readers, &rss20_maker) + _assert_itunes_keywords(["olitics", "red", "blue", "state"], + "olitics, red, blue, state", + readers, &rss20_maker) + end + end + + def assert_itunes_new_feed_url(readers, &rss20_maker) + _wrap_assertion do + url = "http://newlocation.com/example.rss" + content = tag("itunes:new-feed-url", url) + rss20 = itunes_rss20_parse(content, &rss20_maker) + target = chain_reader(rss20, readers) + assert_equal(url, target.itunes_new_feed_url) + end + end + + def _assert_itunes_owner(name, email, readers, &rss20_maker) + content = tag("itunes:owner", + tag("itunes:name", name) + tag("itunes:email", email)) + rss20 = itunes_rss20_parse(content, &rss20_maker) + owner = chain_reader(rss20, readers).itunes_owner + assert_equal(name, owner.itunes_name) + assert_equal(email, owner.itunes_email) + end + + def assert_itunes_owner(readers, &rss20_maker) + _wrap_assertion do + _assert_itunes_owner("John Doe", "john.doe@example.com", + readers, &rss20_maker) + + assert_missing_tag("name", "owner") do + content = tag("itunes:owner") + itunes_rss20_parse(content, &rss20_maker) + end + + assert_missing_tag("name", "owner") do + content = tag("itunes:owner", + tag("itunes:email", "john.doe@example.com")) + itunes_rss20_parse(content, &rss20_maker) + end + + assert_missing_tag("email", "owner") do + content = tag("itunes:owner", tag("itunes:name", "John Doe")) + itunes_rss20_parse(content, &rss20_maker) + end + end + end + + def _assert_itunes_subtitle(value, readers, &rss20_maker) + content = tag("itunes:subtitle", value) + rss20 = itunes_rss20_parse(content, &rss20_maker) + target = chain_reader(rss20, readers) + assert_equal(value, target.itunes_subtitle) + end + + def assert_itunes_subtitle(readers, &rss20_maker) + _wrap_assertion do + _assert_itunes_subtitle("A show about everything", readers, &rss20_maker) + _assert_itunes_subtitle("A short primer on table spices", + readers, &rss20_maker) + _assert_itunes_subtitle("Comparing socket wrenches is fun!", + readers, &rss20_maker) + _assert_itunes_subtitle("Red + Blue != Purple", readers, &rss20_maker) + end + end + + def _assert_itunes_summary(value, readers, &rss20_maker) + content = tag("itunes:summary", value) + rss20 = itunes_rss20_parse(content, &rss20_maker) + target = chain_reader(rss20, readers) + assert_equal(value, target.itunes_summary) + end + + def assert_itunes_summary(readers, &rss20_maker) + _wrap_assertion do + _assert_itunes_summary("All About Everything is a show about " + + "everything. Each week we dive into any " + + "subject known to man and talk about it as " + + "much as we can. Look for our Podcast in " + + "the iTunes Music Store", + readers, &rss20_maker) + _assert_itunes_summary("This week we talk about salt and pepper " + + "shakers, comparing and contrasting pour " + + "rates, construction materials, and overall " + + "aesthetics. Come and join the party!", + readers, &rss20_maker) + _assert_itunes_summary("This week we talk about metric vs. old " + + "english socket wrenches. Which one is " + + "better? Do you really need both? Get all " + + "of your answers here.", + readers, &rss20_maker) + _assert_itunes_summary("This week we talk about surviving in a " + + "Red state if you’re a Blue person. Or " + + "vice versa.", + readers, &rss20_maker) + end + end + end +end diff --git a/test/rss/test_maker_0.9.rb b/test/rss/test_maker_0.9.rb index 0551dc2800..df92945a86 100644 --- a/test/rss/test_maker_0.9.rb +++ b/test/rss/test_maker_0.9.rb @@ -267,7 +267,7 @@ module RSS end end - def test_items + def test_items(with_convenience_way=true) title = "TITLE" link = "http://hoge.com/" description = "text hoge fuga" @@ -301,10 +301,10 @@ module RSS setup_dummy_channel(maker) item_size.times do |i| - maker.items.new_item do |item| - item.title = "#{title}#{i}" - item.link = "#{link}#{i}" - item.description = "#{description}#{i}" + maker.items.new_item do |_item| + _item.title = "#{title}#{i}" + _item.link = "#{link}#{i}" + _item.description = "#{description}#{i}" end end maker.items.do_sort = true @@ -312,36 +312,44 @@ module RSS setup_dummy_image(maker) end assert_equal(item_size, rss.items.size) - rss.channel.items.each_with_index do |item, i| - assert_equal("#{title}#{i}", item.title) - assert_equal("#{link}#{i}", item.link) - assert_equal("#{description}#{i}", item.description) + rss.channel.items.each_with_index do |_item, i| + assert_equal("#{title}#{i}", _item.title) + assert_equal("#{link}#{i}", _item.link) + assert_equal("#{description}#{i}", _item.description) end rss = RSS::Maker.make("0.91") do |maker| setup_dummy_channel(maker) item_size.times do |i| - maker.items.new_item do |item| - item.title = "#{title}#{i}" - item.link = "#{link}#{i}" - item.description = "#{description}#{i}" + maker.items.new_item do |_item| + _item.title = "#{title}#{i}" + _item.link = "#{link}#{i}" + _item.description = "#{description}#{i}" end end maker.items.do_sort = Proc.new do |x, y| - y.title.content[-1] <=> x.title.content[-1] + if with_convenience_way + y.title[-1] <=> x.title[-1] + else + y.title {|t| t.content[-1]} <=> x.title {|t| t.content[-1]} + end end setup_dummy_image(maker) end assert_equal(item_size, rss.items.size) - rss.channel.items.reverse.each_with_index do |item, i| - assert_equal("#{title}#{i}", item.title) - assert_equal("#{link}#{i}", item.link) - assert_equal("#{description}#{i}", item.description) + rss.channel.items.reverse.each_with_index do |_item, i| + assert_equal("#{title}#{i}", _item.title) + assert_equal("#{link}#{i}", _item.link) + assert_equal("#{description}#{i}", _item.description) end end + def test_items_with_new_api_since_018 + test_items(false) + end + def test_textInput title = "fugafuga" description = "text hoge fuga" diff --git a/test/rss/test_maker_1.0.rb b/test/rss/test_maker_1.0.rb index cc1e58c1a2..60cc3708a7 100644 --- a/test/rss/test_maker_1.0.rb +++ b/test/rss/test_maker_1.0.rb @@ -209,7 +209,7 @@ module RSS end end - def test_items + def test_items(with_convenience_way=true) title = "TITLE" link = "http://hoge.com/" description = "text hoge fuga" @@ -242,42 +242,46 @@ module RSS setup_dummy_channel(maker) item_size.times do |i| - maker.items.new_item do |item| - item.title = "#{title}#{i}" - item.link = "#{link}#{i}" - item.description = "#{description}#{i}" + maker.items.new_item do |_item| + _item.title = "#{title}#{i}" + _item.link = "#{link}#{i}" + _item.description = "#{description}#{i}" end end maker.items.do_sort = true end assert_equal(item_size, rss.items.size) - rss.items.each_with_index do |item, i| - assert_equal("#{link}#{i}", item.about) - assert_equal("#{title}#{i}", item.title) - assert_equal("#{link}#{i}", item.link) - assert_equal("#{description}#{i}", item.description) + rss.items.each_with_index do |_item, i| + assert_equal("#{link}#{i}", _item.about) + assert_equal("#{title}#{i}", _item.title) + assert_equal("#{link}#{i}", _item.link) + assert_equal("#{description}#{i}", _item.description) end rss = RSS::Maker.make("1.0") do |maker| setup_dummy_channel(maker) item_size.times do |i| - maker.items.new_item do |item| - item.title = "#{title}#{i}" - item.link = "#{link}#{i}" - item.description = "#{description}#{i}" + maker.items.new_item do |_item| + _item.title = "#{title}#{i}" + _item.link = "#{link}#{i}" + _item.description = "#{description}#{i}" end end maker.items.do_sort = Proc.new do |x, y| - y.title.content[-1] <=> x.title.content[-1] + if with_convenience_way + y.title[-1] <=> x.title[-1] + else + y.title {|t| t.content[-1]} <=> x.title {|t| t.content[-1]} + end end end assert_equal(item_size, rss.items.size) - rss.items.reverse.each_with_index do |item, i| - assert_equal("#{link}#{i}", item.about) - assert_equal("#{title}#{i}", item.title) - assert_equal("#{link}#{i}", item.link) - assert_equal("#{description}#{i}", item.description) + rss.items.reverse.each_with_index do |_item, i| + assert_equal("#{link}#{i}", _item.about) + assert_equal("#{title}#{i}", _item.title) + assert_equal("#{link}#{i}", _item.link) + assert_equal("#{description}#{i}", _item.description) end max_size = item_size / 2 @@ -285,20 +289,20 @@ module RSS setup_dummy_channel(maker) item_size.times do |i| - maker.items.new_item do |item| - item.title = "#{title}#{i}" - item.link = "#{link}#{i}" - item.description = "#{description}#{i}" + maker.items.new_item do |_item| + _item.title = "#{title}#{i}" + _item.link = "#{link}#{i}" + _item.description = "#{description}#{i}" end end maker.items.max_size = max_size end assert_equal(max_size, rss.items.size) - rss.items.each_with_index do |item, i| - assert_equal("#{link}#{i}", item.about) - assert_equal("#{title}#{i}", item.title) - assert_equal("#{link}#{i}", item.link) - assert_equal("#{description}#{i}", item.description) + rss.items.each_with_index do |_item, i| + assert_equal("#{link}#{i}", _item.about) + assert_equal("#{title}#{i}", _item.title) + assert_equal("#{link}#{i}", _item.link) + assert_equal("#{description}#{i}", _item.description) end max_size = 0 @@ -307,10 +311,10 @@ module RSS setup_dummy_channel(maker) item_size.times do |i| - maker.items.new_item do |item| - item.title = "#{title}#{i}" - item.link = "#{link}#{i}" - item.description = "#{description}#{i}" + maker.items.new_item do |_item| + _item.title = "#{title}#{i}" + _item.link = "#{link}#{i}" + _item.description = "#{description}#{i}" end end maker.items.max_size = max_size @@ -322,23 +326,27 @@ module RSS setup_dummy_channel(maker) item_size.times do |i| - maker.items.new_item do |item| - item.title = "#{title}#{i}" - item.link = "#{link}#{i}" - item.description = "#{description}#{i}" + maker.items.new_item do |_item| + _item.title = "#{title}#{i}" + _item.link = "#{link}#{i}" + _item.description = "#{description}#{i}" end end maker.items.max_size = max_size end assert_equal(item_size + max_size + 1, rss.items.size) - rss.items.each_with_index do |item, i| - assert_equal("#{link}#{i}", item.about) - assert_equal("#{title}#{i}", item.title) - assert_equal("#{link}#{i}", item.link) - assert_equal("#{description}#{i}", item.description) + rss.items.each_with_index do |_item, i| + assert_equal("#{link}#{i}", _item.about) + assert_equal("#{title}#{i}", _item.title) + assert_equal("#{link}#{i}", _item.link) + assert_equal("#{description}#{i}", _item.description) end end + def test_items_with_new_api_since_018 + test_items(false) + end + def test_not_valid_items title = "TITLE" link = "http://hoge.com/" diff --git a/test/rss/test_maker_2.0.rb b/test/rss/test_maker_2.0.rb index 605d439a68..8fd9134f70 100644 --- a/test/rss/test_maker_2.0.rb +++ b/test/rss/test_maker_2.0.rb @@ -353,7 +353,7 @@ module RSS assert_nil(rss.image) end - def test_items + def test_items(with_convenience_way=true) title = "TITLE" link = "http://hoge.com/" description = "text hoge fuga" @@ -407,7 +407,11 @@ module RSS end end maker.items.do_sort = Proc.new do |x, y| - y.title.content[-1] <=> x.title.content[-1] + if with_convenience_way + y.title[-1] <=> x.title[-1] + else + y.title {|t| t.content[-1]} <=> x.title {|t| t.content[-1]} + end end end assert_equal(item_size, rss.items.size) @@ -422,6 +426,10 @@ module RSS end end + def test_items_with_new_api_since_018 + test_items(false) + end + def test_guid isPermaLink = "true" content = "http://inessential.com/2002/09/01.php#a2" diff --git a/test/rss/test_maker_atom_feed.rb b/test/rss/test_maker_atom_feed.rb index f4c5cc2a3d..e5bf0e23b2 100644 --- a/test/rss/test_maker_atom_feed.rb +++ b/test/rss/test_maker_atom_feed.rb @@ -268,7 +268,16 @@ module RSS nil, nil, "maker.channel.description") do |maker| setup_dummy_channel_atom(maker) - maker.channel.description.content = nil + maker.channel.description = nil + end + + assert_maker_atom_text_construct("feed", + ["channel", "subtitle"], + ["subtitle"], + nil, nil, + "maker.channel.description") do |maker| + setup_dummy_channel_atom(maker) + maker.channel.description {|d| d.content = nil} end assert_maker_atom_text_construct("feed", diff --git a/test/rss/test_maker_itunes.rb b/test/rss/test_maker_itunes.rb new file mode 100644 index 0000000000..1d4e323057 --- /dev/null +++ b/test/rss/test_maker_itunes.rb @@ -0,0 +1,471 @@ +require "rss-testcase" + +require "rss/maker" + +module RSS + class TestMakerITunes < TestCase + def test_author + assert_maker_itunes_author(%w(channel)) + assert_maker_itunes_author(%w(items last)) + end + + def test_block + assert_maker_itunes_block(%w(channel)) + assert_maker_itunes_block(%w(items last)) + end + + def test_category + assert_maker_itunes_category(%w(channel)) + end + + def test_image + assert_maker_itunes_image(%w(channel)) + end + + def test_duration + assert_maker_itunes_duration(%w(items last)) + end + + def test_explicit + assert_maker_itunes_explicit(%w(channel)) + assert_maker_itunes_explicit(%w(items last)) + end + + def test_keywords + assert_maker_itunes_keywords(%w(channel)) + assert_maker_itunes_keywords(%w(items last)) + end + + def test_new_feed_url + assert_maker_itunes_new_feed_url(%w(channel)) + end + + def test_owner + assert_maker_itunes_owner(%w(channel)) + end + + def test_subtitle + assert_maker_itunes_subtitle(%w(channel)) + assert_maker_itunes_subtitle(%w(items last)) + end + + def test_summary + assert_maker_itunes_summary(%w(channel)) + assert_maker_itunes_summary(%w(items last)) + end + + private + + def assert_maker_itunes_author(maker_readers, feed_readers=nil) + _wrap_assertion do + feed_readers ||= maker_readers + author = "John Doe" + rss20 = ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + target.itunes_author = author + end + target = chain_reader(rss20, feed_readers) + assert_equal(author, target.itunes_author) + end + end + + def _assert_maker_itunes_block(value, boolean_value, maker_readers, + feed_readers) + rss20 = ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + target.itunes_block = value + assert_equal(value, target.itunes_block) + assert_equal(boolean_value, target.itunes_block?) + end + target = chain_reader(rss20, feed_readers) + if [true, false].include?(value) + feed_expected_value = value = value ? "yes" : "no" + else + feed_expected_value = value + end + assert_equal(value, target.itunes_block) + assert_equal(boolean_value, target.itunes_block?) + end + + def assert_maker_itunes_block(maker_readers, feed_readers=nil) + _wrap_assertion do + feed_readers ||= maker_readers + _assert_maker_itunes_block("yes", true, maker_readers, feed_readers) + _assert_maker_itunes_block("Yes", true, maker_readers, feed_readers) + _assert_maker_itunes_block("no", false, maker_readers, feed_readers) + _assert_maker_itunes_block("", false, maker_readers, feed_readers) + _assert_maker_itunes_block(true, true, maker_readers, feed_readers) + _assert_maker_itunes_block(false, false, maker_readers, feed_readers) + _assert_maker_itunes_block(nil, false, maker_readers, feed_readers) + end + end + + def _assert_maker_itunes_category(categories, maker_readers, feed_readers) + rss20 = ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + categories.each do |category| + sub_target = target.itunes_categories + if category.is_a?(Array) + category.each do |sub_category| + sub_target = sub_target.new_category + sub_target.text = sub_category + end + else + sub_target.new_category.text = category + end + end + end + + target = chain_reader(rss20, feed_readers) + actual_categories = target.itunes_categories.collect do |category| + cat = category.text + if category.itunes_categories.empty? + cat + else + [cat, *category.itunes_categories.collect {|c| c.text}] + end + end + assert_equal(categories, actual_categories) + end + + def assert_maker_itunes_category(maker_readers, feed_readers=nil) + _wrap_assertion do + feed_readers ||= maker_readers + _assert_maker_itunes_category(["Audio Blogs"], + maker_readers, feed_readers) + _assert_maker_itunes_category([["Arts & Entertainment", "Games"]], + maker_readers, feed_readers) + _assert_maker_itunes_category([["Arts & Entertainment", "Games"], + ["Technology", "Computers"], + "Audio Blogs"], + maker_readers, feed_readers) + end + end + + def assert_maker_itunes_image(maker_readers, feed_readers=nil) + _wrap_assertion do + feed_readers ||= maker_readers + url = "http://example.com/podcasts/everything/AllAboutEverything.jpg" + + rss20 = ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + target.itunes_image = url + end + + target = chain_reader(rss20, feed_readers) + assert_not_nil(target.itunes_image) + assert_equal(url, target.itunes_image.href) + end + end + + def _assert_maker_itunes_duration(hour, minute, second, value, + maker_readers, feed_readers) + _assert_maker_itunes_duration_by_value(hour, minute, second, value, + maker_readers, feed_readers) + _assert_maker_itunes_duration_by_hour_minute_second(hour, minute, second, + value, + maker_readers, + feed_readers) + end + + def _assert_maker_itunes_duration_by(hour, minute, second, value, + maker_readers, feed_readers) + expected_value = nil + rss20 = ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + expected_value = yield(target) + assert_equal(expected_value, target.itunes_duration) + target.itunes_duration do |duration| + assert_equal([hour, minute, second, expected_value], + [duration.hour, duration.minute, + duration.second, duration.content]) + end + end + target = chain_reader(rss20, feed_readers) + duration = target.itunes_duration + assert_not_nil(duration) + assert_equal([hour, minute, second, expected_value], + [duration.hour, duration.minute, + duration.second, duration.content]) + end + + def _assert_maker_itunes_duration_by_value(hour, minute, second, value, + maker_readers, feed_readers) + _assert_maker_itunes_duration_by(hour, minute, second, value, + maker_readers, feed_readers) do |target| + target.itunes_duration = value + value + end + end + + def _assert_maker_itunes_duration_by_hour_minute_second(hour, minute, second, + value, + maker_readers, + feed_readers) + _assert_maker_itunes_duration_by(hour, minute, second, value, + maker_readers, feed_readers) do |target| + target.itunes_duration do |duration| + duration.hour = hour + duration.minute = minute + duration.second = second + end + value.split(":").collect {|v| "%02d" % v.to_i}.join(":") + end + end + + def _assert_maker_itunes_duration_invalid_value(value, maker_readers) + assert_raise(ArgumentError) do + ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + target.itunes_duration = value + end + end + end + + def assert_maker_itunes_duration(maker_readers, feed_readers=nil) + _wrap_assertion do + feed_readers ||= maker_readers + _assert_maker_itunes_duration(7, 14, 5, "07:14:05", maker_readers, + feed_readers) + _assert_maker_itunes_duration(7, 14, 5, "7:14:05", maker_readers, + feed_readers) + _assert_maker_itunes_duration(0, 4, 55, "04:55", maker_readers, + feed_readers) + _assert_maker_itunes_duration(0, 4, 5, "4:05", maker_readers, + feed_readers) + + _assert_maker_itunes_duration_invalid_value("5", maker_readers) + _assert_maker_itunes_duration_invalid_value("09:07:14:05", maker_readers) + _assert_maker_itunes_duration_invalid_value("10:5", maker_readers) + _assert_maker_itunes_duration_invalid_value("10:03:5", maker_readers) + _assert_maker_itunes_duration_invalid_value("10:3:05", maker_readers) + + _assert_maker_itunes_duration_invalid_value("xx:xx:xx", maker_readers) + end + end + + def _assert_maker_itunes_explicit(explicit, value, + maker_readers, feed_readers) + rss20 = ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + target.itunes_explicit = value + assert_equal(explicit, target.itunes_explicit?) + end + target = chain_reader(rss20, feed_readers) + assert_equal(value, target.itunes_explicit) + assert_equal(explicit, target.itunes_explicit?) + end + + def assert_maker_itunes_explicit(maker_readers, feed_readers=nil) + _wrap_assertion do + feed_readers ||= maker_readers + _assert_maker_itunes_explicit(true, "yes", maker_readers, feed_readers) + _assert_maker_itunes_explicit(false, "clean", + maker_readers, feed_readers) + _assert_maker_itunes_explicit(nil, "no", maker_readers, feed_readers) + end + end + + def _assert_maker_itunes_keywords(keywords, value, + maker_readers, feed_readers) + _assert_maker_itunes_keywords_by_value(keywords, value, + maker_readers, feed_readers) + _assert_maker_itunes_keywords_by_keywords(keywords, maker_readers, + feed_readers) + end + + def _assert_maker_itunes_keywords_by(keywords, maker_readers, feed_readers) + rss20 = ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + yield(target) + end + assert_nothing_raised do + rss20 = ::RSS::Parser.parse(rss20.to_s) + end + target = chain_reader(rss20, feed_readers) + assert_equal(keywords, target.itunes_keywords) + end + + def _assert_maker_itunes_keywords_by_value(keywords, value, + maker_readers, feed_readers) + _assert_maker_itunes_keywords_by(keywords, maker_readers, + feed_readers) do |target| + target.itunes_keywords = value + end + end + + def _assert_maker_itunes_keywords_by_keywords(keywords, + maker_readers, feed_readers) + _assert_maker_itunes_keywords_by(keywords, maker_readers, + feed_readers) do |target| + target.itunes_keywords = keywords + end + end + + def assert_maker_itunes_keywords(maker_readers, feed_readers=nil) + _wrap_assertion do + feed_readers ||= maker_readers + _assert_maker_itunes_keywords(["salt"], "salt", + maker_readers, feed_readers) + _assert_maker_itunes_keywords(["salt"], " salt ", + maker_readers, feed_readers) + _assert_maker_itunes_keywords(["salt", "pepper", "shaker", "exciting"], + "salt, pepper, shaker, exciting", + maker_readers, feed_readers) + _assert_maker_itunes_keywords(["metric", "socket", "wrenches", + "toolsalt"], + "metric, socket, wrenches, toolsalt", + maker_readers, feed_readers) + _assert_maker_itunes_keywords(["olitics", "red", "blue", "state"], + "olitics, red, blue, state", + maker_readers, feed_readers) + end + end + + def assert_maker_itunes_new_feed_url(maker_readers, feed_readers=nil) + feed_readers ||= maker_readers + url = "http://newlocation.com/example.rss" + + rss20 = ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + target.itunes_new_feed_url = url + end + target = chain_reader(rss20, feed_readers) + assert_equal(url, target.itunes_new_feed_url) + end + + def _assert_maker_itunes_owner(name, email, maker_readers, feed_readers) + rss20 = ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + owner = target.itunes_owner + owner.itunes_name = name + owner.itunes_email = email + end + owner = chain_reader(rss20, feed_readers).itunes_owner + if name.nil? and email.nil? + assert_nil(owner) + else + assert_not_nil(owner) + assert_equal(name, owner.itunes_name) + assert_equal(email, owner.itunes_email) + end + end + + def assert_maker_itunes_owner(maker_readers, feed_readers=nil) + _wrap_assertion do + feed_readers ||= maker_readers + _assert_maker_itunes_owner("John Doe", "john.doe@example.com", + maker_readers, feed_readers) + + not_set_name = (["maker"] + maker_readers + ["itunes_owner"]).join(".") + assert_not_set_error(not_set_name, ["itunes_name"]) do + _assert_maker_itunes_owner(nil, "john.doe@example.com", + maker_readers, feed_readers) + end + assert_not_set_error(not_set_name, ["itunes_email"]) do + _assert_maker_itunes_owner("John Doe", nil, + maker_readers, feed_readers) + end + + _assert_maker_itunes_owner(nil, nil, maker_readers, feed_readers) + end + end + + def _assert_maker_itunes_subtitle(subtitle, maker_readers, feed_readers) + rss20 = ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + target.itunes_subtitle = subtitle + end + + target = chain_reader(rss20, feed_readers) + assert_equal(subtitle, target.itunes_subtitle) + end + + def assert_maker_itunes_subtitle(maker_readers, feed_readers=nil) + _wrap_assertion do + feed_readers ||= maker_readers + _assert_maker_itunes_subtitle("A show about everything", + maker_readers, feed_readers) + _assert_maker_itunes_subtitle("A short primer on table spices", + maker_readers, feed_readers) + _assert_maker_itunes_subtitle("Comparing socket wrenches is fun!", + maker_readers, feed_readers) + _assert_maker_itunes_subtitle("Red + Blue != Purple", + maker_readers, feed_readers) + end + end + + def _assert_maker_itunes_summary(summary, maker_readers, feed_readers) + rss20 = ::RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + target = chain_reader(maker, maker_readers) + target.itunes_summary = summary + end + + target = chain_reader(rss20, feed_readers) + assert_equal(summary, target.itunes_summary) + end + + def assert_maker_itunes_summary(maker_readers, feed_readers=nil) + _wrap_assertion do + feed_readers ||= maker_readers + _assert_maker_itunes_summary("All About Everything is a show about " + + "everything. Each week we dive into any " + + "subject known to man and talk about it " + + "as much as we can. Look for our Podcast " + + "in the iTunes Music Store", + maker_readers, feed_readers) + _assert_maker_itunes_summary("This week we talk about salt and pepper " + + "shakers, comparing and contrasting pour " + + "rates, construction materials, and " + + "overall aesthetics. Come and join the " + + "party!", + maker_readers, feed_readers) + _assert_maker_itunes_summary("This week we talk about metric vs. old " + + "english socket wrenches. Which one is " + + "better? Do you really need both? Get " + + "all of your answers here.", + maker_readers, feed_readers) + _assert_maker_itunes_summary("This week we talk about surviving in a " + + "Red state if you’re a Blue person. Or " + + "vice versa.", + maker_readers, feed_readers) + end + end + end +end diff --git a/test/rss/test_maker_xml-stylesheet.rb b/test/rss/test_maker_xml-stylesheet.rb index 9d45e52038..81d97ddc0e 100644 --- a/test/rss/test_maker_xml-stylesheet.rb +++ b/test/rss/test_maker_xml-stylesheet.rb @@ -39,8 +39,8 @@ module RSS href = 'http://example.com/index.xsl' type = 'text/xsl' rss = RSS::Maker.make("1.0") do |maker| - maker.xml_stylesheets.new_xml_stylesheet do |xss| - xss.href = href + maker.xml_stylesheets.new_xml_stylesheet do |_xss| + _xss.href = href end setup_dummy_channel(maker) diff --git a/test/rss/test_parser.rb b/test/rss/test_parser.rb index 49b441bf96..45f31e2014 100644 --- a/test/rss/test_parser.rb +++ b/test/rss/test_parser.rb @@ -1,14 +1,4 @@ -begin - require "fileutils" -rescue LoadError - module FileUtils - module_function - def rm_f(target) - File.unlink(target) - rescue Errno::ENOENT - end - end -end +require 'fileutils' require "rss-testcase" @@ -28,7 +18,7 @@ EOR @rss_file = "rss10.rdf" File.open(@rss_file, "w") {|f| f.print(@rss10)} end - + def teardown Parser.default_parser = @_default_parser FileUtils.rm_f(@rss_file) diff --git a/test/rss/test_setup_maker_1.0.rb b/test/rss/test_setup_maker_1.0.rb index 539749bf20..0b960d91c8 100644 --- a/test/rss/test_setup_maker_1.0.rb +++ b/test/rss/test_setup_maker_1.0.rb @@ -262,8 +262,8 @@ module RSS assert_equal(@trackback_elems[:ping], item.trackback_ping) assert_equal(@trackback_elems[:about].size, item.trackback_abouts.size) - item.trackback_abouts.each_with_index do |about, i| - assert_equal(@trackback_elems[:about][i], about.value) + item.trackback_abouts.each_with_index do |about, j| + assert_equal(@trackback_elems[:about][j], about.value) end end end @@ -538,8 +538,8 @@ module RSS assert_equal(@trackback_elems[:ping], item.trackback_ping) assert_equal(@trackback_elems[:about].size, item.trackback_abouts.size) - item.trackback_abouts.each_with_index do |about, i| - assert_equal(@trackback_elems[:about][i], about.value) + item.trackback_abouts.each_with_index do |about, j| + assert_equal(@trackback_elems[:about][j], about.value) end end diff --git a/test/rss/test_setup_maker_atom_feed.rb b/test/rss/test_setup_maker_atom_feed.rb index 0caed6cec8..03d33dbb9c 100644 --- a/test/rss/test_setup_maker_atom_feed.rb +++ b/test/rss/test_setup_maker_atom_feed.rb @@ -104,7 +104,9 @@ module RSS end generator.each do |key, value| - maker.channel.generator.__send__("#{key}=", value) + maker.channel.generator do |g| + g.__send__("#{key}=", value) + end end maker.channel.icon = icon diff --git a/test/rss/test_setup_maker_itunes.rb b/test/rss/test_setup_maker_itunes.rb new file mode 100644 index 0000000000..1f0372d6e7 --- /dev/null +++ b/test/rss/test_setup_maker_itunes.rb @@ -0,0 +1,144 @@ +require "rss-testcase" + +require "rss/maker" + +module RSS + class TestSetupMakerITunes < TestCase + def test_setup_maker_simple + author = "John Doe" + block = true + categories = ["Audio Blogs"] + image = "http://example.com/podcasts/everything/AllAboutEverything.jpg" + duration = "4:05" + duration_components = [0, 4, 5] + explicit = true + keywords = ["salt", "pepper", "shaker", "exciting"] + new_feed_url = "http://newlocation.com/example.rss" + owner = {:name => "John Doe", :email => "john.doe@example.com"} + subtitle = "A show about everything" + summary = "All About Everything is a show about " + + "everything. Each week we dive into any " + + "subject known to man and talk about it " + + "as much as we can. Look for our Podcast " + + "in the iTunes Music Store" + + feed = RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + channel = maker.channel + channel.itunes_author = author + channel.itunes_block = block + categories.each do |category| + channel.itunes_categories.new_category.text = category + end + channel.itunes_image = image + channel.itunes_explicit = explicit + channel.itunes_keywords = keywords + channel.itunes_owner.itunes_name = owner[:name] + channel.itunes_owner.itunes_email = owner[:email] + channel.itunes_subtitle = subtitle + channel.itunes_summary = summary + + item = maker.items.last + item.itunes_author = author + item.itunes_block = block + item.itunes_duration = duration + item.itunes_explicit = explicit + item.itunes_keywords = keywords + item.itunes_subtitle = subtitle + item.itunes_summary = summary + end + assert_not_nil(feed) + + new_feed = RSS::Maker.make("rss2.0") do |maker| + feed.setup_maker(maker) + end + assert_not_nil(new_feed) + + channel = new_feed.channel + item = new_feed.items.last + + assert_equal(author, channel.itunes_author) + assert_equal(author, item.itunes_author) + + assert_equal(block, channel.itunes_block?) + assert_equal(block, item.itunes_block?) + + assert_equal(categories, + collect_itunes_categories(channel.itunes_categories)) + + assert_equal(image, channel.itunes_image.href) + + assert_equal(duration_components, + [item.itunes_duration.hour, + item.itunes_duration.minute, + item.itunes_duration.second]) + + assert_equal(explicit, channel.itunes_explicit?) + assert_equal(explicit, item.itunes_explicit?) + + assert_equal(keywords, channel.itunes_keywords) + assert_equal(keywords, item.itunes_keywords) + + assert_equal(owner, + { + :name => channel.itunes_owner.itunes_name, + :email => channel.itunes_owner.itunes_email + }) + + assert_equal(subtitle, channel.itunes_subtitle) + assert_equal(subtitle, item.itunes_subtitle) + + assert_equal(summary, channel.itunes_summary) + assert_equal(summary, item.itunes_summary) + end + + def test_setup_maker_with_nested_categories + categories = [["Arts & Entertainment", "Games"], + ["Technology", "Computers"], + "Audio Blogs"] + + feed = RSS::Maker.make("rss2.0") do |maker| + setup_dummy_channel(maker) + setup_dummy_item(maker) + + channel = maker.channel + categories.each do |category| + target = channel.itunes_categories + if category.is_a?(Array) + category.each do |sub_category| + target = target.new_category + target.text = sub_category + end + else + target.new_category.text = category + end + end + end + assert_not_nil(feed) + + new_feed = RSS::Maker.make("rss2.0") do |maker| + feed.setup_maker(maker) + end + assert_not_nil(new_feed) + + channel = new_feed.channel + + assert_equal(categories, + collect_itunes_categories(channel.itunes_categories)) + end + + private + def collect_itunes_categories(categories) + categories.collect do |c| + rest = collect_itunes_categories(c.itunes_categories) + if rest.empty? + c.text + else + [c.text, *rest] + end + end + end + end +end diff --git a/test/rss/test_to_s.rb b/test/rss/test_to_s.rb index 1538534034..66739591ea 100644 --- a/test/rss/test_to_s.rb +++ b/test/rss/test_to_s.rb @@ -12,7 +12,6 @@ require "rss/trackback" module RSS class TestToS < TestCase - def setup @image_url = "http://example.com/foo.png" @textinput_link = "http://example.com/search.cgi" @@ -435,6 +434,237 @@ module RSS new_about.value = about end end + + + def assert_channel10(attrs, channel) + _wrap_assertion do + n_attrs = normalized_attrs(attrs) + + names = %w(about title link description) + assert_attributes(attrs, names, channel) + + %w(image items textinput).each do |name| + value = n_attrs[name] + if value + target = channel.__send__(name) + __send__("assert_channel10_#{name}", value, target) + end + end + end + end + + def assert_channel10_image(attrs, image) + _wrap_assertion do + assert_attributes(attrs, %w(resource), image) + end + end + + def assert_channel10_textinput(attrs, textinput) + _wrap_assertion do + assert_attributes(attrs, %w(resource), textinput) + end + end + + def assert_channel10_items(attrs, items) + _wrap_assertion do + assert_equal(items.resources, items.Seq.lis.collect {|x| x.resource}) + items.Seq.lis.each_with_index do |li, i| + assert_attributes(attrs[i], %w(resource), li) + end + end + end + + def assert_image10(attrs, image) + _wrap_assertion do + names = %w(about title url link) + assert_attributes(attrs, names, image) + end + end + + def assert_items10(attrs, items) + _wrap_assertion do + names = %w(about title link description) + items.each_with_index do |item, i| + assert_attributes(attrs[i], names, item) + end + end + end + + def assert_textinput10(attrs, textinput) + _wrap_assertion do + names = %w(about title description name link) + assert_attributes(attrs, names, textinput) + end + end + + + def assert_channel09(attrs, channel) + _wrap_assertion do + n_attrs = normalized_attrs(attrs) + + names = %w(title description link language rating + copyright pubDate lastBuildDate docs + managingEditor webMaster) + assert_attributes(attrs, names, channel) + + %w(skipHours skipDays).each do |name| + value = n_attrs[name] + if value + target = channel.__send__(name) + __send__("assert_channel09_#{name}", value, target) + end + end + end + end + + def assert_channel09_skipDays(contents, skipDays) + _wrap_assertion do + days = skipDays.days + contents.each_with_index do |content, i| + assert_equal(content, days[i].content) + end + end + end + + def assert_channel09_skipHours(contents, skipHours) + _wrap_assertion do + hours = skipHours.hours + contents.each_with_index do |content, i| + assert_equal(content.to_i, hours[i].content) + end + end + end + + def assert_image09(attrs, image) + _wrap_assertion do + names = %w(url link title description) + names << ["width", :integer] + names << ["height", :integer] + assert_attributes(attrs, names, image) + end + end + + def assert_items09(attrs, items) + _wrap_assertion do + names = %w(title link description) + items.each_with_index do |item, i| + assert_attributes(attrs[i], names, item) + end + end + end + def assert_textinput09(attrs, textinput) + _wrap_assertion do + names = %w(title description name link) + assert_attributes(attrs, names, textinput) + end + end + + + def assert_channel20(attrs, channel) + _wrap_assertion do + n_attrs = normalized_attrs(attrs) + + names = %w(title link description language copyright + managingEditor webMaster pubDate + lastBuildDate generator docs rating) + names << ["ttl", :integer] + assert_attributes(attrs, names, channel) + + %w(cloud categories skipHours skipDays).each do |name| + value = n_attrs[name] + if value + target = channel.__send__(name) + __send__("assert_channel20_#{name}", value, target) + end + end + end + end + + def assert_channel20_skipDays(contents, skipDays) + assert_channel09_skipDays(contents, skipDays) + end + + def assert_channel20_skipHours(contents, skipHours) + assert_channel09_skipHours(contents, skipHours) + end + + def assert_channel20_cloud(attrs, cloud) + _wrap_assertion do + names = %w(domain path registerProcedure protocol) + names << ["port", :integer] + assert_attributes(attrs, names, cloud) + end + end + + def assert_channel20_categories(attrs, categories) + _wrap_assertion do + names = %w(domain content) + categories.each_with_index do |category, i| + assert_attributes(attrs[i], names, category) + end + end + end + + def assert_image20(attrs, image) + _wrap_assertion do + names = %w(url link title description) + names << ["width", :integer] + names << ["height", :integer] + assert_attributes(attrs, names, image) + end + end + + def assert_items20(attrs, items) + _wrap_assertion do + names = %w(about title link description) + items.each_with_index do |item, i| + assert_attributes(attrs[i], names, item) + + n_attrs = normalized_attrs(attrs[i]) + + %w(source enclosure categories guid).each do |name| + value = n_attrs[name] + if value + target = item.__send__(name) + __send__("assert_items20_#{name}", value, target) + end + end + end + end + end + + def assert_items20_source(attrs, source) + _wrap_assertion do + assert_attributes(attrs, %w(url content), source) + end + end + + def assert_items20_enclosure(attrs, enclosure) + _wrap_assertion do + names = ["url", ["length", :integer], "type"] + assert_attributes(attrs, names, enclosure) + end + end + + def assert_items20_categories(attrs, categories) + _wrap_assertion do + assert_channel20_categories(attrs, categories) + end + end + + def assert_items20_guid(attrs, guid) + _wrap_assertion do + names = [["isPermaLink", :boolean], ["content"]] + assert_attributes(attrs, names, guid) + end + end + + def assert_textinput20(attrs, textinput) + _wrap_assertion do + names = %w(title description name link) + assert_attributes(attrs, names, textinput) + end + end end end diff --git a/test/rss/test_version.rb b/test/rss/test_version.rb index 2c7d2e0f7a..cb60faae79 100644 --- a/test/rss/test_version.rb +++ b/test/rss/test_version.rb @@ -3,7 +3,7 @@ require "rss-testcase" module RSS class TestVersion < TestCase def test_version - assert_equal("0.1.7", ::RSS::VERSION) + assert_equal("0.1.8", ::RSS::VERSION) end end end -- cgit v1.2.3