From 0dc342de848a642ecce8db697b8fecd83a63e117 Mon Sep 17 00:00:00 2001 From: yugui Date: Mon, 25 Aug 2008 15:02:05 +0000 Subject: added tag v1_9_0_4 git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/tags/v1_9_0_4@18845 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- trunk/lib/rss/maker/base.rb | 880 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 880 insertions(+) create mode 100644 trunk/lib/rss/maker/base.rb (limited to 'trunk/lib/rss/maker/base.rb') diff --git a/trunk/lib/rss/maker/base.rb b/trunk/lib/rss/maker/base.rb new file mode 100644 index 0000000000..2262a764ec --- /dev/null +++ b/trunk/lib/rss/maker/base.rb @@ -0,0 +1,880 @@ +require 'forwardable' + +require 'rss/rss' + +module RSS + module Maker + class Base + extend Utils::InheritedReader + + OTHER_ELEMENTS = [] + NEED_INITIALIZE_VARIABLES = [] + + class << self + def other_elements + inherited_array_reader("OTHER_ELEMENTS") + end + def need_initialize_variables + inherited_array_reader("NEED_INITIALIZE_VARIABLES") + end + + def inherited_base + ::RSS::Maker::Base + end + + def inherited(subclass) + subclass.const_set("OTHER_ELEMENTS", []) + subclass.const_set("NEED_INITIALIZE_VARIABLES", []) + end + + def add_other_element(variable_name) + self::OTHER_ELEMENTS << variable_name + end + + def add_need_initialize_variable(variable_name, init_value=nil, + &init_block) + init_value ||= init_block + self::NEED_INITIALIZE_VARIABLES << [variable_name, init_value] + end + + def def_array_element(name, plural=nil, klass_name=nil) + include Enumerable + extend Forwardable + + 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(<<-EOC, __FILE__, __LINE__ + 1) + def new_#{name} + #{name} = self.class::#{klass_name}.new(@maker) + @#{plural} << #{name} + if block_given? + yield #{name} + else + #{name} + end + end + alias new_child new_#{name} + + def to_feed(*args) + @#{plural}.each do |#{name}| + #{name}.to_feed(*args) + end + end + + def replace(elements) + @#{plural}.replace(elements.to_a) + end + 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) do |object| + object.send("make_#{name}") + end + 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 + + def def_csv_element(name, type=nil) + def_other_element_without_accessor(name) + attr_reader(name) + converter = "" + if type == :integer + converter = "{|v| Integer(v)}" + end + module_eval(<<-EOC, __FILE__, __LINE__ + 1) + def #{name}=(value) + @#{name} = Utils::CSV.parse(value)#{converter} + end + EOC + end + end + + attr_reader :maker + def initialize(maker) + @maker = maker + @default_values_are_set = false + initialize_variables + end + + def have_required_values? + not_set_required_variables.empty? + end + + def variable_is_set? + variables.any? {|var| not __send__(var).nil?} + end + + private + def initialize_variables + self.class.need_initialize_variables.each do |variable_name, init_value| + if init_value.nil? + value = nil + else + if init_value.respond_to?(:call) + value = init_value.call(self) + elsif init_value.is_a?(String) + # just for backward compatibility + value = instance_eval(init_value, __FILE__, __LINE__) + else + value = init_value + end + end + instance_variable_set("@#{variable_name}", value) + end + end + + def setup_other_elements(feed, current=nil) + current ||= current_element(feed) + self.class.other_elements.each do |element| + __send__("setup_#{element}", feed, current) + end + end + + def current_element(feed) + feed + end + + def set_default_values(&block) + return yield if @default_values_are_set + + begin + @default_values_are_set = true + _set_default_values(&block) + ensure + @default_values_are_set = false + end + end + + def _set_default_values(&block) + yield + end + + def setup_values(target) + set = false + if have_required_values? + variables.each do |var| + setter = "#{var}=" + if target.respond_to?(setter) + value = __send__(var) + if value + target.__send__(setter, value) + set = true + end + end + end + end + set + end + + def set_parent(target, parent) + target.parent = parent if target.class.need_parent? + end + + def variables + self.class.need_initialize_variables.find_all do |name, init| + # init == "nil" is just for backward compatibility + init.nil? or init == "nil" + end.collect do |name, init| + name + end + end + + def not_set_required_variables + required_variable_names.find_all do |var| + __send__(var).nil? + end + end + + def required_variables_are_set? + required_variable_names.each do |var| + return false if __send__(var).nil? + end + true + end + end + + module AtomPersonConstructBase + def self.append_features(klass) + super + + klass.class_eval(<<-EOC, __FILE__, __LINE__ + 1) + %w(name uri email).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + 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 + ["div", xhtml_uri] == [content.name, content.uri] + children = content + children = [children] unless content.is_a?(Array) + children = set_xhtml_uri_as_default_uri(children) + content = RSS::XML::Element.new("div", nil, xhtml_uri, + {"xmlns" => xhtml_uri}, + children) + end + 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| + if child.is_a?(RSS::XML::Element) and + child.prefix.nil? and child.uri.nil? + RSS::XML::Element.new(child.name, nil, ::RSS::Atom::XHTML_URI, + child.attributes.dup, + set_xhtml_uri_as_default_uri(child.children)) + else + child + end + end + end + end + + def self.append_features(klass) + super + + klass.class_eval do + include EnsureXMLContent + end + end + end + + module SetupDefaultDate + private + def _set_default_values(&block) + keep = { + :date => date, + :dc_dates => dc_dates.to_a.dup, + } + _date = date + if _date and !dc_dates.any? {|dc_date| dc_date.value == _date} + dc_date = self.class::DublinCoreDates::DublinCoreDate.new(self) + dc_date.value = _date.dup + dc_dates.unshift(dc_date) + end + self.date ||= self.dc_date + super(&block) + ensure + date = keep[:date] + dc_dates.replace(keep[:dc_dates]) + end + end + + class RSSBase < Base + class << self + def make(version, &block) + new(version).make(&block) + end + end + + %w(xml_stylesheets channel image items textinput).each do |element| + attr_reader element + add_need_initialize_variable(element) do |object| + object.send("make_#{element}") + end + module_eval(<<-EOC, __FILE__, __LINE__) + private + def setup_#{element}(feed) + @#{element}.to_feed(feed) + end + + def make_#{element} + self.class::#{Utils.to_class_name(element)}.new(self) + end + EOC + end + + attr_reader :feed_version + alias_method(:rss_version, :feed_version) + attr_accessor :version, :encoding, :standalone + + def initialize(feed_version) + super(self) + @feed_type = nil + @feed_subtype = nil + @feed_version = feed_version + @version = "1.0" + @encoding = "UTF-8" + @standalone = nil + end + + def make + yield(self) + to_feed + end + + def to_feed + feed = make_feed + setup_xml_stylesheets(feed) + setup_elements(feed) + setup_other_elements(feed) + feed.validate + feed + end + + private + remove_method :make_xml_stylesheets + def make_xml_stylesheets + XMLStyleSheets.new(self) + end + end + + class XMLStyleSheets < Base + def_array_element("xml_stylesheet", nil, "XMLStyleSheet") + + class XMLStyleSheet < Base + + ::RSS::XMLStyleSheet::ATTRIBUTES.each do |attribute| + attr_accessor attribute + add_need_initialize_variable(attribute) + end + + def to_feed(feed) + xss = ::RSS::XMLStyleSheet.new + guess_type_if_need(xss) + set = setup_values(xss) + if set + feed.xml_stylesheets << xss + end + end + + private + def guess_type_if_need(xss) + if @type.nil? + xss.href = @href + @type = xss.type + end + end + + def required_variable_names + %w(href type) + end + end + end + + class ChannelBase < Base + include SetupDefaultDate + + %w(cloud categories skipDays skipHours).each do |name| + def_classed_element(name) + end + + %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 + managingEditor webMaster rating docs date + lastBuildDate ttl).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + + def pubDate + date + end + + def pubDate=(date) + self.date = date + end + + def updated + date + end + + def updated=(date) + self.date = date + end + + alias_method(:rights, :copyright) + alias_method(:rights=, :copyright=) + + alias_method(:subtitle, :description) + alias_method(:subtitle=, :description=) + + def icon + image_favicon.about + end + + def icon=(url) + image_favicon.about = url + end + + def logo + maker.image.url + end + + def logo=(url) + maker.image.url = url + end + + class SkipDaysBase < Base + def_array_element("day") + + class DayBase < Base + %w(content).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + end + + class SkipHoursBase < Base + def_array_element("hour") + + class HourBase < Base + %w(content).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + end + + class CloudBase < Base + %w(domain port path registerProcedure protocol).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + class CategoriesBase < Base + def_array_element("category", "categories") + + class CategoryBase < Base + %w(domain content label).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + + alias_method(:term, :domain) + alias_method(:term=, :domain=) + alias_method(:scheme, :content) + alias_method(:scheme=, :content=) + end + end + + class LinksBase < Base + def_array_element("link") + + class LinkBase < Base + %w(href rel type hreflang title length).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + end + + class AuthorsBase < Base + def_array_element("author") + + class AuthorBase < Base + include AtomPersonConstructBase + end + end + + class ContributorsBase < Base + def_array_element("contributor") + + class ContributorBase < Base + include AtomPersonConstructBase + end + end + + class GeneratorBase < Base + %w(uri version content).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + class CopyrightBase < Base + include AtomTextConstructBase + end + + class DescriptionBase < Base + include AtomTextConstructBase + end + + class TitleBase < Base + include AtomTextConstructBase + end + end + + class ImageBase < Base + %w(title url width height description).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + + def link + @maker.channel.link + end + end + + class ItemsBase < Base + def_array_element("item") + + attr_accessor :do_sort, :max_size + + def initialize(maker) + super + @do_sort = false + @max_size = -1 + end + + def normalize + if @max_size >= 0 + sort_if_need[0...@max_size] + else + sort_if_need[0..@max_size] + end + end + + private + def sort_if_need + if @do_sort.respond_to?(:call) + @items.sort do |x, y| + @do_sort.call(x, y) + end + elsif @do_sort + @items.sort do |x, y| + y <=> x + end + else + @items + end + end + + class ItemBase < Base + include SetupDefaultDate + + %w(guid enclosure source categories content).each do |name| + def_classed_element(name) + end + + %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) + end + + def pubDate + date + end + + def pubDate=(date) + self.date = date + end + + def updated + date + end + + def updated=(date) + self.date = date + end + + alias_method(:summary, :description) + alias_method(:summary=, :description=) + + def <=>(other) + _date = date || dc_date + _other_date = other.date || other.dc_date + if _date and _other_date + _date <=> _other_date + elsif _date + 1 + elsif _other_date + -1 + else + 0 + end + end + + class GuidBase < Base + %w(isPermaLink content).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + class EnclosureBase < Base + %w(url length type).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + class SourceBase < Base + %w(authors categories contributors generator icon + logo rights subtitle title).each do |name| + def_classed_element(name) + end + + [ + ["link", "href"], + ].each do |name, attribute| + def_classed_elements(name, attribute) + end + + %w(id content date).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + + alias_method(:url, :link) + alias_method(:url=, :link=) + + def updated + date + end + + def updated=(date) + self.date = date + end + + private + AuthorsBase = ChannelBase::AuthorsBase + CategoriesBase = ChannelBase::CategoriesBase + ContributorsBase = ChannelBase::ContributorsBase + GeneratorBase = ChannelBase::GeneratorBase + + class IconBase < Base + %w(url).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + LinksBase = ChannelBase::LinksBase + + class LogoBase < Base + %w(uri).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + class RightsBase < Base + include AtomTextConstructBase + end + + class SubtitleBase < Base + include AtomTextConstructBase + end + + class TitleBase < Base + include AtomTextConstructBase + end + end + + CategoriesBase = ChannelBase::CategoriesBase + AuthorsBase = ChannelBase::AuthorsBase + LinksBase = ChannelBase::LinksBase + ContributorsBase = ChannelBase::ContributorsBase + + class RightsBase < Base + include AtomTextConstructBase + end + + class DescriptionBase < Base + include AtomTextConstructBase + end + + class ContentBase < Base + include AtomTextConstructBase::EnsureXMLContent + + %w(src).each do |element| + attr_accessor(element) + add_need_initialize_variable(element) + end + + def xml_content=(content) + content = ensure_xml_content(content) if inline_xhtml? + @xml_content = content + end + + alias_method(:xml, :xml_content) + alias_method(:xml=, :xml_content=) + + def inline_text? + [nil, "text", "html"].include?(@type) + end + + def inline_html? + @type == "html" + end + + def inline_xhtml? + @type == "xhtml" + end + + def inline_other? + !out_of_line? and ![nil, "text", "html", "xhtml"].include?(@type) + end + + def inline_other_text? + return false if @type.nil? or out_of_line? + /\Atext\//i.match(@type) ? true : false + end + + def inline_other_xml? + return false if @type.nil? or out_of_line? + /[\+\/]xml\z/i.match(@type) ? true : false + end + + def inline_other_base64? + return false if @type.nil? or out_of_line? + @type.include?("/") and !inline_other_text? and !inline_other_xml? + end + + def out_of_line? + not @src.nil? and @content.nil? + end + end + + class TitleBase < Base + include AtomTextConstructBase + end + end + end + + class TextinputBase < Base + %w(title description name link).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + end +end -- cgit v1.2.3