diff options
author | kou <kou@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2007-10-21 12:19:43 +0000 |
---|---|---|
committer | kou <kou@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2007-10-21 12:19:43 +0000 |
commit | 57a639494a2a002f496a945979e2bf499b0d9fdb (patch) | |
tree | 53fadfc1666459e1286735a37171539883d66d70 /lib/rss/maker/base.rb | |
parent | 754b1fac44487454cc25a14443615c0fba3da6ad (diff) |
* lib/rss.rb, lib/rss/, test/rss/, sample/rss/: merged from trunk.
- 0.1.6 -> 2.0.0.
- fixed image module URI. Thanks to Dmitry Borodaenko.
- supported Atom.
- supported ITunes module.
- supported Slash module.
* NEWS: added an entry for RSS Parser.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@13747 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/rss/maker/base.rb')
-rw-r--r-- | lib/rss/maker/base.rb | 840 |
1 files changed, 581 insertions, 259 deletions
diff --git a/lib/rss/maker/base.rb b/lib/rss/maker/base.rb index 2327dd98e4..fdb8946efc 100644 --- a/lib/rss/maker/base.rb +++ b/lib/rss/maker/base.rb @@ -4,70 +4,184 @@ require 'rss/rss' module RSS module Maker + class Base + extend Utils::InheritedReader - module Base + OTHER_ELEMENTS = [] + NEED_INITIALIZE_VARIABLES = [] - def self.append_features(klass) - super - - klass.module_eval(<<-EOC, __FILE__, __LINE__) + class << self + def other_elements + inherited_array_reader("OTHER_ELEMENTS") + end + def need_initialize_variables + inherited_array_reader("NEED_INITIALIZE_VARIABLES") + end - OTHER_ELEMENTS = [] - NEED_INITIALIZE_VARIABLES = [] + def inherited_base + ::RSS::Maker::Base + end - def self.inherited(subclass) + 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") + self::NEED_INITIALIZE_VARIABLES << [variable_name, init_value] + end - subclass.module_eval(<<-EOEOC, __FILE__, __LINE__) - def self.other_elements - OTHER_ELEMENTS + super + 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 self.need_initialize_variables - NEED_INITIALIZE_VARIABLES + super + def to_feed(*args) + @#{plural}.each do |#{name}| + #{name}.to_feed(*args) + end end - EOEOC - end - def self.add_other_element(variable_name) - OTHER_ELEMENTS << variable_name + def replace(elements) + @#{plural}.replace(elements.to_a) + end + EOC end - def self.other_elements - OTHER_ELEMENTS + 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 self.add_need_initialize_variable(variable_name, init_value="nil") - NEED_INITIALIZE_VARIABLES << [variable_name, init_value] + 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 self.need_initialize_variables - NEED_INITIALIZE_VARIABLES + def def_other_element(name) + attr_accessor name + def_other_element_without_accessor(name) end - def self.def_array_element(name) - include Enumerable - extend Forwardable + 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_delegators("@\#{name}", :<<, :[], :[]=, :first, :last) - def_delegators("@\#{name}", :push, :pop, :shift, :unshift) - def_delegators("@\#{name}", :each, :size) - - add_need_initialize_variable(name, "[]") + 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 - EOC end - + + attr_reader :maker def initialize(maker) @maker = maker + @default_values_are_set = false initialize_variables end def have_required_values? - true + 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| @@ -75,16 +189,32 @@ module RSS end end - def setup_other_elements(rss) + def setup_other_elements(feed, current=nil) + current ||= current_element(feed) self.class.other_elements.each do |element| - __send__("setup_#{element}", rss, current_element(rss)) + __send__("setup_#{element}", feed, current) end end - def current_element(rss) - rss + 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? @@ -102,6 +232,10 @@ module RSS 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| "nil" == init @@ -110,10 +244,6 @@ module RSS end end - def variable_is_set? - variables.find {|var| !__send__(var).nil?} - end - def not_set_required_variables required_variable_names.find_all do |var| __send__(var).nil? @@ -126,12 +256,106 @@ module RSS end true end - end - class RSSBase - include Base + 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(&block) new.make(&block) @@ -143,22 +367,25 @@ module RSS add_need_initialize_variable(element, "make_#{element}") module_eval(<<-EOC, __FILE__, __LINE__) private - def setup_#{element}(rss) - @#{element}.to_rss(rss) + def setup_#{element}(feed) + @#{element}.to_feed(feed) end def make_#{element} self.class::#{Utils.to_class_name(element)}.new(self) end -EOC + EOC end - attr_reader :rss_version + attr_reader :feed_version + alias_method(:rss_version, :feed_version) attr_accessor :version, :encoding, :standalone - - def initialize(rss_version) + + def initialize(feed_version) super(self) - @rss_version = rss_version + @feed_type = nil + @feed_subtype = nil + @feed_version = feed_version @version = "1.0" @encoding = "UTF-8" @standalone = nil @@ -167,19 +394,19 @@ EOC def make if block_given? yield(self) - to_rss + to_feed else nil end end - def to_rss - rss = make_rss - setup_xml_stylesheets(rss) - setup_elements(rss) - setup_other_elements(rss) - if rss.channel - rss + def to_feed + feed = make_feed + setup_xml_stylesheets(feed) + setup_elements(feed) + setup_other_elements(feed) + if feed.valid? + feed else nil end @@ -190,51 +417,27 @@ EOC def make_xml_stylesheets XMLStyleSheets.new(self) end - end - class XMLStyleSheets - include Base - - def_array_element("xml_stylesheets") - - def to_rss(rss) - @xml_stylesheets.each do |xss| - xss.to_rss(rss) - end - end - - def new_xml_stylesheet - xss = XMLStyleSheet.new(@maker) - @xml_stylesheets << xss - if block_given? - yield xss - else - xss - end - end + 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 add_need_initialize_variable(attribute) end - def to_rss(rss) + def to_feed(feed) xss = ::RSS::XMLStyleSheet.new guess_type_if_need(xss) set = setup_values(xss) if set - rss.xml_stylesheets << xss + feed.xml_stylesheets << xss end end - def have_required_values? - @href and @type - end - private def guess_type_if_need(xss) if @type.nil? @@ -242,172 +445,183 @@ EOC @type = xss.type end end + + def required_variable_names + %w(href type) + end end end - class ChannelBase - include Base + class ChannelBase < Base + include SetupDefaultDate - %w(cloud categories skipDays skipHours).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}(rss, current) - @#{element}.to_rss(rss, 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 - %w(about title link description language copyright + [ + ["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 generator ttl).each do |element| + lastBuildDate ttl).each do |element| attr_accessor element add_need_initialize_variable(element) end - alias_method(:pubDate, :date) - alias_method(:pubDate=, :date=) + def pubDate + date + end - def current_element(rss) - rss.channel + def pubDate=(date) + self.date = date end - class SkipDaysBase - include Base + def updated + date + end - def_array_element("days") + def updated=(date) + self.date = date + end - def new_day - day = self.class::Day.new(@maker) - @days << day - if block_given? - yield day - else - day - end - end - - def current_element(rss) - rss.channel.skipDays - end + alias_method(:rights, :copyright) + alias_method(:rights=, :copyright=) - class DayBase - include Base - - %w(content).each do |element| - attr_accessor element - add_need_initialize_variable(element) - end + alias_method(:subtitle, :description) + alias_method(:subtitle=, :description=) - def current_element(rss) - rss.channel.skipDays.last - end + def icon + image_favicon.about + end - end + def icon=(url) + image_favicon.about = url end - - class SkipHoursBase - include Base - def_array_element("hours") + def logo + maker.image.url + end - def new_hour - hour = self.class::Hour.new(@maker) - @hours << hour - if block_given? - yield hour - else - hour - end - end - - def current_element(rss) - rss.channel.skipHours - end + def logo=(url) + maker.image.url = url + end - class HourBase - include Base - + 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") - def current_element(rss) - rss.channel.skipHours.last + class HourBase < Base + %w(content).each do |element| + attr_accessor element + add_need_initialize_variable(element) end - 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 - - def current_element(rss) - rss.channel.cloud - end - end - class CategoriesBase - include Base - - def_array_element("categories") + class CategoriesBase < Base + def_array_element("category", "categories") - def new_category - category = self.class::Category.new(@maker) - @categories << category - if block_given? - yield category - else - category + 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 CategoryBase - include Base + class LinksBase < Base + def_array_element("link") - %w(domain content).each do |element| + 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 - include Base - + 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 - - def current_element(rss) - rss.image - end end - class ItemsBase - include Base + class ItemsBase < Base + def_array_element("item") - def_array_element("items") - attr_accessor :do_sort, :max_size def initialize(maker) @@ -423,21 +637,7 @@ EOC sort_if_need[0..@max_size] end end - - def current_element(rss) - rss.items - end - def new_item - item = self.class::Item.new(@maker) - @items << item - if block_given? - yield item - else - item - end - end - private def sort_if_need if @do_sort.respond_to?(:call) @@ -453,94 +653,216 @@ EOC end end - class ItemBase - include Base - - %w(guid enclosure source categories).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}(rss, current) - @#{element}.to_rss(rss, current) - end + class ItemBase < Base + include SetupDefaultDate - def make_#{element} - self.class::#{Utils.to_class_name(element)}.new(@maker) - end -EOC + %w(guid enclosure source categories content).each do |name| + def_classed_element(name) end - - %w(title link description date author comments).each do |element| + + %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 - alias_method(:pubDate, :date) - alias_method(:pubDate=, :date=) + 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) - if date and other.date - date <=> other.date - elsif date + _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 + elsif _other_date -1 else 0 end end - - def current_element(rss) - rss.items.last - 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 - %w(url content).each do |element| + 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 - include Base - + class TextinputBase < Base %w(title description name link).each do |element| attr_accessor element add_need_initialize_variable(element) end - - def current_element(rss) - rss.textinput - end - end - end end |