From 966a25465aab5c2972e6c453f631a15fc2223256 Mon Sep 17 00:00:00 2001 From: kou Date: Sat, 17 Mar 2007 10:13:25 +0000 Subject: * lib/rss, test/rss: - supported Atom. - bumped version 0.1.6 to 0.1.7. * sample/rss/convert.rb: added new sample. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12087 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- lib/rss/maker/0.9.rb | 372 +++++++++++++++++++---- lib/rss/maker/1.0.rb | 328 +++++++++++++++++--- lib/rss/maker/2.0.rb | 134 ++++++--- lib/rss/maker/atom.rb | 172 +++++++++++ lib/rss/maker/base.rb | 710 ++++++++++++++++++++++++++++++++++---------- lib/rss/maker/dublincore.rb | 100 +++---- lib/rss/maker/entry.rb | 167 +++++++++++ lib/rss/maker/feed.rb | 429 ++++++++++++++++++++++++++ lib/rss/maker/image.rb | 100 +++---- lib/rss/maker/taxonomy.rb | 73 ++--- lib/rss/maker/trackback.rb | 85 ++---- 11 files changed, 2134 insertions(+), 536 deletions(-) create mode 100644 lib/rss/maker/atom.rb create mode 100644 lib/rss/maker/entry.rb create mode 100644 lib/rss/maker/feed.rb (limited to 'lib/rss/maker') diff --git a/lib/rss/maker/0.9.rb b/lib/rss/maker/0.9.rb index b82585fb96..dd75c9289b 100644 --- a/lib/rss/maker/0.9.rb +++ b/lib/rss/maker/0.9.rb @@ -7,13 +7,14 @@ module RSS class RSS09 < RSSBase - def initialize(rss_version="0.91") + def initialize(feed_version="0.91") super + @feed_type = "rss" end private - def make_rss - Rss.new(@rss_version, @version, @encoding, @standalone) + def make_feed + Rss.new(@feed_version, @version, @encoding, @standalone) end def setup_elements(rss) @@ -22,40 +23,34 @@ module RSS class Channel < ChannelBase - def to_rss(rss) + def to_feed(rss) channel = Rss::Channel.new set = setup_values(channel) - if set + _not_set_required_variables = not_set_required_variables + if _not_set_required_variables.empty? rss.channel = channel + set_parent(channel, rss) setup_items(rss) setup_image(rss) setup_textinput(rss) - setup_other_elements(rss) - if rss.channel.image - rss - else - nil - end - elsif variable_is_set? - raise NotSetError.new("maker.channel", not_set_required_variables) + setup_other_elements(rss, channel) + rss + else + raise NotSetError.new("maker.channel", _not_set_required_variables) end end - def have_required_values? - @title and @link and @description and @language - end - private def setup_items(rss) - @maker.items.to_rss(rss) + @maker.items.to_feed(rss) end def setup_image(rss) - @maker.image.to_rss(rss) + @maker.image.to_feed(rss) end def setup_textinput(rss) - @maker.textinput.to_rss(rss) + @maker.textinput.to_feed(rss) end def variables @@ -63,162 +58,409 @@ module RSS end def required_variable_names - %w(title link description language) + %w(link language) end - + + def not_set_required_variables + vars = super + vars << "description" unless description.have_required_values? + vars << "title" unless title.have_required_values? + vars + end + class SkipDays < SkipDaysBase - def to_rss(rss, channel) + def to_feed(rss, channel) unless @days.empty? skipDays = Rss::Channel::SkipDays.new channel.skipDays = skipDays + set_parent(skipDays, channel) @days.each do |day| - day.to_rss(rss, skipDays.days) + day.to_feed(rss, skipDays.days) end end end class Day < DayBase - def to_rss(rss, days) + def to_feed(rss, days) day = Rss::Channel::SkipDays::Day.new set = setup_values(day) if set days << day - setup_other_elements(rss) + set_parent(day, days) + setup_other_elements(rss, day) end end - def have_required_values? - @content + private + def required_variable_names + %w(content) end end end class SkipHours < SkipHoursBase - def to_rss(rss, channel) + def to_feed(rss, channel) unless @hours.empty? skipHours = Rss::Channel::SkipHours.new channel.skipHours = skipHours + set_parent(skipHours, channel) @hours.each do |hour| - hour.to_rss(rss, skipHours.hours) + hour.to_feed(rss, skipHours.hours) end end end class Hour < HourBase - def to_rss(rss, hours) + def to_feed(rss, hours) hour = Rss::Channel::SkipHours::Hour.new set = setup_values(hour) if set hours << hour - setup_other_elements(rss) + set_parent(hour, hours) + setup_other_elements(rss, hour) end end - def have_required_values? - @content + private + def required_variable_names + %w(content) end end end class Cloud < CloudBase - def to_rss(*args) + def to_feed(*args) end end class Categories < CategoriesBase - def to_rss(*args) + def to_feed(*args) end class Category < CategoryBase end end + + class Links < LinksBase + def to_feed(rss, channel) + return if @links.empty? + @links.first.to_feed(rss, channel) + end + + class Link < LinkBase + def to_feed(rss, channel) + if have_required_values? + channel.link = href + else + raise NotSetError.new("maker.channel.link", + not_set_required_variables) + end + end + + private + def required_variable_names + %w(href) + end + end + end + + class Authors < AuthorsBase + def to_feed(rss, channel) + end + + class Author < AuthorBase + def to_feed(rss, channel) + end + end + end + + class Contributors < ContributorsBase + def to_feed(rss, channel) + end + + class Contributor < ContributorBase + end + end + + class Generator < GeneratorBase + def to_feed(rss, channel) + end + end + + class Copyright < CopyrightBase + def to_feed(rss, channel) + channel.copyright = content if have_required_values? + end + + private + def required_variable_names + %w(content) + end + end + + class Description < DescriptionBase + def to_feed(rss, channel) + channel.description = content if have_required_values? + end + + private + def required_variable_names + %w(content) + end + end + + class Title < TitleBase + def to_feed(rss, channel) + channel.title = content if have_required_values? + end + + private + def required_variable_names + %w(content) + end + end end - + class Image < ImageBase - def to_rss(rss) + def to_feed(rss) image = Rss::Channel::Image.new set = setup_values(image) if set image.link = link rss.channel.image = image - setup_other_elements(rss) + set_parent(image, rss.channel) + setup_other_elements(rss, image) + elsif required_element? + raise NotSetError.new("maker.image", not_set_required_variables) end end - - def have_required_values? - @url and @title and link + + private + def required_variable_names + %w(url title link) + end + + def required_element? + true end end class Items < ItemsBase - def to_rss(rss) + def to_feed(rss) if rss.channel normalize.each do |item| - item.to_rss(rss) + item.to_feed(rss) end - setup_other_elements(rss) + setup_other_elements(rss, rss.items) end end class Item < ItemBase - def to_rss(rss) + def to_feed(rss) item = Rss::Channel::Item.new set = setup_values(item) - if set + if set or title.have_required_values? rss.items << item - setup_other_elements(rss) + set_parent(item, rss.channel) + setup_other_elements(rss, item) + elsif variable_is_set? + raise NotSetError.new("maker.items", not_set_required_variables) end end - - private + def have_required_values? - @title and @link + super and title.have_required_values? + end + + private + def required_variable_names + %w(link) + end + + def not_set_required_variables + vars = super + vars << "title" unless title.have_required_values? + vars end class Guid < GuidBase - def to_rss(*args) + def to_feed(*args) end end - + class Enclosure < EnclosureBase - def to_rss(*args) + def to_feed(*args) end end - + class Source < SourceBase - def to_rss(*args) + def to_feed(*args) + end + + class Authors < AuthorsBase + def to_feed(*args) + end + + class Author < AuthorBase + end + end + + class Categories < CategoriesBase + def to_feed(*args) + end + + class Category < CategoryBase + end + end + + class Contributors < ContributorsBase + def to_feed(*args) + end + + class Contributor < ContributorBase + end + end + + class Generator < GeneratorBase + def to_feed(*args) + end + end + + class Icon < IconBase + def to_feed(*args) + end + end + + class Links < LinksBase + def to_feed(*args) + end + + class Link < LinkBase + end + end + + class Logo < LogoBase + def to_feed(*args) + end + end + + class Rights < RightsBase + def to_feed(*args) + end + end + + class Subtitle < SubtitleBase + def to_feed(*args) + end + end + + class Title < TitleBase + def to_feed(*args) + end end end - + class Categories < CategoriesBase - def to_rss(*args) + def to_feed(*args) end class Category < CategoryBase end end - + + class Authors < AuthorsBase + def to_feed(*args) + end + + class Author < AuthorBase + end + end + + class Links < LinksBase + def to_feed(rss, item) + return if @links.empty? + @links.first.to_feed(rss, item) + end + + class Link < LinkBase + def to_feed(rss, item) + if have_required_values? + item.link = href + else + raise NotSetError.new("maker.link", + not_set_required_variables) + end + end + + private + def required_variable_names + %w(href) + end + end + end + + class Contributors < ContributorsBase + def to_feed(rss, item) + end + + class Contributor < ContributorBase + end + end + + class Rights < RightsBase + def to_feed(rss, item) + end + end + + class Description < DescriptionBase + def to_feed(rss, item) + item.description = content if have_required_values? + end + + private + def required_variable_names + %w(content) + end + end + + class Content < ContentBase + def to_feed(rss, item) + end + end + + class Title < TitleBase + def to_feed(rss, item) + item.title = content if have_required_values? + end + + private + def required_variable_names + %w(content) + end + end end end class Textinput < TextinputBase - def to_rss(rss) + def to_feed(rss) textInput = Rss::Channel::TextInput.new set = setup_values(textInput) if set rss.channel.textInput = textInput - setup_other_elements(rss) + set_parent(textInput, rss.channel) + setup_other_elements(rss, textInput) end end private - def have_required_values? - @title and @description and @name and @link + def required_variable_names + %w(title description name link) end end end - add_maker(filename_to_version(__FILE__), RSS09) - add_maker(filename_to_version(__FILE__) + "1", RSS09) + add_maker("0.9", RSS09) + add_maker("0.91", RSS09) + add_maker("rss0.91", RSS09) end end diff --git a/lib/rss/maker/1.0.rb b/lib/rss/maker/1.0.rb index 3e6542a007..12608ad94a 100644 --- a/lib/rss/maker/1.0.rb +++ b/lib/rss/maker/1.0.rb @@ -9,10 +9,11 @@ module RSS def initialize super("1.0") + @feed_type = "rss" end private - def make_rss + def make_feed RDF.new(@version, @encoding, @standalone) end @@ -25,43 +26,46 @@ module RSS class Channel < ChannelBase - def to_rss(rss) - set = false - if @about - channel = RDF::Channel.new(@about) - set = setup_values(channel) - if set + def to_feed(rss) + set_default_values do + _not_set_required_variables = not_set_required_variables + if _not_set_required_variables.empty? + channel = RDF::Channel.new(@about) + set = setup_values(channel) channel.dc_dates.clear rss.channel = channel + set_parent(channel, rss) setup_items(rss) setup_image(rss) setup_textinput(rss) - setup_other_elements(rss) + setup_other_elements(rss, channel) + else + raise NotSetError.new("maker.channel", _not_set_required_variables) end end - - if (!@about or !set) and variable_is_set? - raise NotSetError.new("maker.channel", not_set_required_variables) - end - end - - def have_required_values? - @about and @title and @link and @description end private def setup_items(rss) items = RDF::Channel::Items.new seq = items.Seq - @maker.items.normalize.each do |item| - seq.lis << RDF::Channel::Items::Seq::Li.new(item.link) + set_parent(items, seq) + target_items = @maker.items.normalize + raise NotSetError.new("maker", ["items"]) if target_items.empty? + target_items.each do |item| + li = RDF::Channel::Items::Seq::Li.new(item.link) + seq.lis << li + set_parent(li, seq) end rss.channel.items = items + set_parent(rss.channel, items) end def setup_image(rss) if @maker.image.have_required_values? - rss.channel.image = RDF::Channel::Image.new(@maker.image.url) + image = RDF::Channel::Image.new(@maker.image.url) + rss.channel.image = image + set_parent(image, rss.channel) end end @@ -69,15 +73,23 @@ module RSS if @maker.textinput.have_required_values? textinput = RDF::Channel::Textinput.new(@maker.textinput.link) rss.channel.textinput = textinput + set_parent(textinput, rss.channel) end end def required_variable_names - %w(about title link description) + %w(about link) end - + + def not_set_required_variables + vars = super + vars << "description" unless description.have_required_values? + vars << "title" unless title.have_required_values? + vars + end + class SkipDays < SkipDaysBase - def to_rss(*args) + def to_feed(*args) end class Day < DayBase @@ -85,7 +97,7 @@ module RSS end class SkipHours < SkipHoursBase - def to_rss(*args) + def to_feed(*args) end class Hour < HourBase @@ -93,112 +105,330 @@ module RSS end class Cloud < CloudBase - def to_rss(*args) + def to_feed(*args) end end class Categories < CategoriesBase - def to_rss(*args) + def to_feed(*args) end class Category < CategoryBase end end + + class Links < LinksBase + def to_feed(rss, channel) + return if @links.empty? + @links.first.to_feed(rss, channel) + end + + class Link < LinkBase + def to_feed(rss, channel) + if have_required_values? + channel.link = href + else + raise NotSetError.new("maker.channel.link", + not_set_required_variables) + end + end + + private + def required_variable_names + %w(href) + end + end + end + + class Authors < AuthorsBase + def to_feed(rss, channel) + end + + class Author < AuthorBase + def to_feed(rss, channel) + end + end + end + + class Contributors < ContributorsBase + def to_feed(rss, channel) + end + + class Contributor < ContributorBase + end + end + + class Generator < GeneratorBase + def to_feed(rss, channel) + end + end + + class Copyright < CopyrightBase + def to_feed(rss, channel) + end + end + + class Description < DescriptionBase + def to_feed(rss, channel) + channel.description = content if have_required_values? + end + + private + def required_variable_names + %w(content) + end + end + + class Title < TitleBase + def to_feed(rss, channel) + channel.title = content if have_required_values? + end + + private + def required_variable_names + %w(content) + end + end end class Image < ImageBase - def to_rss(rss) + def to_feed(rss) if @url image = RDF::Image.new(@url) set = setup_values(image) if set rss.image = image - setup_other_elements(rss) + set_parent(image, rss) + setup_other_elements(rss, image) end end end def have_required_values? - @url and @title and link and @maker.channel.have_required_values? + super and @maker.channel.have_required_values? end private def variables super + ["link"] end + + def required_variable_names + %w(url title link) + end end class Items < ItemsBase - def to_rss(rss) + def to_feed(rss) if rss.channel normalize.each do |item| - item.to_rss(rss) + item.to_feed(rss) end - setup_other_elements(rss) + setup_other_elements(rss, rss.items) end end class Item < ItemBase - def to_rss(rss) - if @link - item = RDF::Item.new(@link) + def to_feed(rss) + set_default_values do + item = RDF::Item.new(link) set = setup_values(item) if set item.dc_dates.clear rss.items << item - setup_other_elements(rss) + set_parent(item, rss) + setup_other_elements(rss, item) + elsif !have_required_values? + raise NotSetError.new("maker.item", not_set_required_variables) end end end - def have_required_values? - @title and @link + private + def required_variable_names + %w(link) + end + + def variables + super + %w(link) + end + + def not_set_required_variables + set_default_values do + vars = super + vars << "title" unless title.have_required_values? + vars + end end class Guid < GuidBase - def to_rss(*args) + def to_feed(*args) end end - + class Enclosure < EnclosureBase - def to_rss(*args) + def to_feed(*args) end end - + class Source < SourceBase - def to_rss(*args) + def to_feed(*args) + end + + class Authors < AuthorsBase + def to_feed(*args) + end + + class Author < AuthorBase + end + end + + class Categories < CategoriesBase + def to_feed(*args) + end + + class Category < CategoryBase + end + end + + class Contributors < ContributorsBase + def to_feed(*args) + end + + class Contributor < ContributorBase + end + end + + class Generator < GeneratorBase + def to_feed(*args) + end + end + + class Icon < IconBase + def to_feed(*args) + end + end + + class Links < LinksBase + def to_feed(*args) + end + + class Link < LinkBase + end + end + + class Logo < LogoBase + def to_feed(*args) + end + end + + class Rights < RightsBase + def to_feed(*args) + end + end + + class Subtitle < SubtitleBase + def to_feed(*args) + end + end + + class Title < TitleBase + def to_feed(*args) + end end end - + class Categories < CategoriesBase - def to_rss(*args) + def to_feed(*args) end class Category < CategoryBase end end + + class Authors < AuthorsBase + def to_feed(*args) + end + + class Author < AuthorBase + end + end + + class Links < LinksBase + def to_feed(*args) + end + + class Link < LinkBase + end + end + + class Contributors < ContributorsBase + def to_feed(rss, item) + end + + class Contributor < ContributorBase + end + end + + class Rights < RightsBase + def to_feed(rss, item) + end + end + + class Description < DescriptionBase + def to_feed(rss, item) + item.description = content if have_required_values? + end + + private + def required_variable_names + %w(content) + end + end + + class Content < ContentBase + def to_feed(rss, item) + end + end + + class Title < TitleBase + def to_feed(rss, item) + item.title = content if have_required_values? + end + + private + def required_variable_names + %w(content) + end + end end end class Textinput < TextinputBase - def to_rss(rss) + def to_feed(rss) if @link textinput = RDF::Textinput.new(@link) set = setup_values(textinput) if set rss.textinput = textinput - setup_other_elements(rss) + set_parent(textinput, rss) + setup_other_elements(rss, textinput) end end end def have_required_values? - @title and @description and @name and @link and - @maker.channel.have_required_values? + super and @maker.channel.have_required_values? + end + + private + def required_variable_names + %w(title description name link) end end end - add_maker(filename_to_version(__FILE__), RSS10) + add_maker("1.0", RSS10) + add_maker("rss1.0", RSS10) end end diff --git a/lib/rss/maker/2.0.rb b/lib/rss/maker/2.0.rb index a958661614..d93ba94d4a 100644 --- a/lib/rss/maker/2.0.rb +++ b/lib/rss/maker/2.0.rb @@ -7,16 +7,13 @@ module RSS class RSS20 < RSS09 - def initialize(rss_version="2.0") + def initialize(feed_version="2.0") super end class Channel < RSS09::Channel - def have_required_values? - @title and @link and @description - end - + private def required_variable_names %w(title link description) end @@ -32,47 +29,64 @@ module RSS end class Cloud < RSS09::Channel::Cloud - def to_rss(rss, channel) + def to_feed(rss, channel) cloud = Rss::Channel::Cloud.new set = setup_values(cloud) if set channel.cloud = cloud - setup_other_elements(rss) + set_parent(cloud, channel) + setup_other_elements(rss, cloud) end end - def have_required_values? - @domain and @port and @path and - @registerProcedure and @protocol + private + def required_variable_names + %w(domain port path registerProcedure protocol) end end class Categories < RSS09::Channel::Categories - def to_rss(rss, channel) + def to_feed(rss, channel) @categories.each do |category| - category.to_rss(rss, channel) + category.to_feed(rss, channel) end end class Category < RSS09::Channel::Categories::Category - def to_rss(rss, channel) + def to_feed(rss, channel) category = Rss::Channel::Category.new set = setup_values(category) if set channel.categories << category - setup_other_elements(rss) + set_parent(category, channel) + setup_other_elements(rss, category) end end - - def have_required_values? - @content + + private + def required_variable_names + %w(content) end end end - + + class Generator < GeneratorBase + def to_feed(rss, channel) + channel.generator = content + end + + private + def required_variable_names + %w(content) + end + end end class Image < RSS09::Image + private + def required_element? + false + end end class Items < RSS09::Items @@ -84,85 +98,123 @@ module RSS end private + def required_variable_names + %w(title description) + end + def variables super + ["pubDate"] end class Guid < RSS09::Items::Item::Guid - def to_rss(rss, item) + def to_feed(rss, item) guid = Rss::Channel::Item::Guid.new set = setup_values(guid) if set item.guid = guid - setup_other_elements(rss) + set_parent(guid, item) + setup_other_elements(rss, guid) end end - - def have_required_values? - @content + + private + def required_variable_names + %w(content) end end class Enclosure < RSS09::Items::Item::Enclosure - def to_rss(rss, item) + def to_feed(rss, item) enclosure = Rss::Channel::Item::Enclosure.new set = setup_values(enclosure) if set item.enclosure = enclosure - setup_other_elements(rss) + set_parent(enclosure, item) + setup_other_elements(rss, enclosure) end end - - def have_required_values? - @url and @length and @type + + private + def required_variable_names + %w(url length type) end end class Source < RSS09::Items::Item::Source - def to_rss(rss, item) + def to_feed(rss, item) source = Rss::Channel::Item::Source.new set = setup_values(source) if set item.source = source - setup_other_elements(rss) + set_parent(source, item) + setup_other_elements(rss, source) end end - - def have_required_values? - @url and @content + + private + def required_variable_names + %w(url content) + end + + class Links < RSS09::Items::Item::Source::Links + def to_feed(rss, source) + return if @links.empty? + @links.first.to_feed(rss, source) + end + + class Link < RSS09::Items::Item::Source::Links::Link + def to_feed(rss, source) + source.url = href + end + end end end class Categories < RSS09::Items::Item::Categories - def to_rss(rss, item) + def to_feed(rss, item) @categories.each do |category| - category.to_rss(rss, item) + category.to_feed(rss, item) end end class Category < RSS09::Items::Item::Categories::Category - def to_rss(rss, item) + def to_feed(rss, item) category = Rss::Channel::Item::Category.new set = setup_values(category) if set item.categories << category + set_parent(category, item) setup_other_elements(rss) end end - - def have_required_values? - @content + + private + def required_variable_names + %w(content) + end + end + end + + class Authors < RSS09::Items::Item::Authors + def to_feed(rss, item) + return if @authors.empty? + @authors.first.to_feed(rss, item) + end + + class Author < RSS09::Items::Item::Authors::Author + def to_feed(rss, item) + item.author = name end end end end - end class Textinput < RSS09::Textinput end end - add_maker(filename_to_version(__FILE__), RSS20) + add_maker("2.0", RSS20) + add_maker("rss2.0", RSS20) end end diff --git a/lib/rss/maker/atom.rb b/lib/rss/maker/atom.rb new file mode 100644 index 0000000000..27d30c6d89 --- /dev/null +++ b/lib/rss/maker/atom.rb @@ -0,0 +1,172 @@ +require "rss/atom" + +require "rss/maker/base" + +module RSS + module Maker + module AtomPersons + module_function + def def_atom_persons(klass, name, maker_name, plural=nil) + plural ||= "#{name}s" + klass_name = Utils.to_class_name(name) + plural_klass_name = Utils.to_class_name(plural) + + klass.class_eval(<<-EOC, __FILE__, __LINE__ + 1) + class #{plural_klass_name} < #{plural_klass_name}Base + class #{klass_name} < #{klass_name}Base + def to_feed(feed, current) + #{name} = feed.class::#{klass_name}.new + set = setup_values(#{name}) + unless set + raise NotSetError.new(#{maker_name.dump}, + not_set_required_variables) + end + current.#{plural} << #{name} + set_parent(#{name}, current) + setup_other_elements(#{name}) + end + + private + def required_variable_names + %w(name) + end + end + end +EOC + end + end + + module AtomTextConstruct + class << self + def def_atom_text_construct(klass, name, maker_name, klass_name=nil, + atom_klass_name=nil) + klass_name ||= Utils.to_class_name(name) + atom_klass_name ||= Utils.to_class_name(name) + + klass.class_eval(<<-EOC, __FILE__, __LINE__ + 1) + class #{klass_name} < #{klass_name}Base + include #{self.name} + def to_feed(feed, current) + #{name} = current.class::#{atom_klass_name}.new + if setup_values(#{name}) + current.#{name} = #{name} + set_parent(#{name}, current) + setup_other_elements(feed) + elsif variable_is_set? + raise NotSetError.new(#{maker_name.dump}, + not_set_required_variables) + end + end + end + EOC + end + end + + private + def required_variable_names + if type == "xhtml" + %w(xml_content) + else + %w(content) + end + end + + def variables + if type == "xhtml" + super + %w(xhtml) + else + super + end + end + end + + module AtomCategory + def to_feed(feed, current) + category = feed.class::Category.new + set = setup_values(category) + if set + current.categories << category + set_parent(category, current) + setup_other_elements(feed) + else + raise NotSetError.new(self.class.not_set_name, + not_set_required_variables) + end + end + + private + def required_variable_names + %w(term) + end + + def variables + super + ["term", "scheme"] + end + end + + module AtomLink + def to_feed(feed, current) + link = feed.class::Link.new + set = setup_values(link) + if set + current.links << link + set_parent(link, current) + setup_other_elements(feed) + else + raise NotSetError.new(self.class.not_set_name, + not_set_required_variables) + end + end + + private + def required_variable_names + %w(href) + end + end + + module AtomGenerator + def to_feed(feed, current) + generator = current.class::Generator.new + if setup_values(generator) + current.generator = generator + set_parent(generator, current) + setup_other_elements(feed) + elsif variable_is_set? + raise NotSetError.new(self.class.not_set_name, + not_set_required_variables) + end + end + + private + def required_variable_names + %w(content) + end + end + + module AtomLogo + def to_feed(feed, current) + logo = current.class::Logo.new + class << logo + alias uri= content= + end + set = setup_values(logo) + class << logo + undef uri= + end + if set + current.logo = logo + set_parent(logo, current) + setup_other_elements(feed) + elsif variable_is_set? + raise NotSetError.new(self.class.not_set_name, + not_set_required_variables) + end + end + + private + def required_variable_names + %w(uri) + end + end + end +end diff --git a/lib/rss/maker/base.rb b/lib/rss/maker/base.rb index 6d7dd557bf..ad47ff29cc 100644 --- a/lib/rss/maker/base.rb +++ b/lib/rss/maker/base.rb @@ -4,9 +4,7 @@ require 'rss/rss' module RSS module Maker - module Base - def self.append_features(klass) super @@ -46,28 +44,60 @@ module RSS NEED_INITIALIZE_VARIABLES end - def self.def_array_element(name) + def self.def_array_element(name, plural=nil, klass=nil) include Enumerable extend Forwardable - def_delegators("@\#{name}", :<<, :[], :[]=, :first, :last) - def_delegators("@\#{name}", :push, :pop, :shift, :unshift) - def_delegators("@\#{name}", :each, :size) - - add_need_initialize_variable(name, "[]") + 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) + + add_need_initialize_variable(plural, "[]") + + module_eval(<<-EOM, __FILE__, __LINE__ + 1) + def new_\#{name} + \#{name} = \#{klass}.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 +EOM 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 +105,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 +148,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 +160,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,7 +172,92 @@ module RSS 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 + 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 + + 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(<<-EOC, __FILE__, __LINE__ + 1) + 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 + + 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::Date.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 @@ -143,8 +274,8 @@ 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} @@ -153,12 +284,15 @@ module RSS 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 +301,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,25 +324,12 @@ 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 - xss - end + def_array_element("xml_stylesheet", nil, "XMLStyleSheet") class XMLStyleSheet include Base @@ -218,19 +339,15 @@ EOC 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? @@ -238,20 +355,27 @@ EOC @type = xss.type end end + + def required_variable_names + %w(href type) + end end end class ChannelBase include Base + include SetupDefaultDate - %w(cloud categories skipDays skipHours).each do |element| + %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}(rss, current) - @#{element}.to_rss(rss, current) + def setup_#{element}(feed, current) + @#{element}.to_feed(feed, current) end def make_#{element} @@ -260,34 +384,102 @@ EOC EOC end - %w(about title link description language copyright + %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 pubDate=(date) + self.date = date + end + + def updated + date + end + + def updated=(date) + 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 current_element(rss) - rss.channel + 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 include Base - def_array_element("days") - - def new_day - day = self.class::Day.new(@maker) - @days << day - day - end - - def current_element(rss) - rss.channel.skipDays - end + def_array_element("day") class DayBase include Base @@ -296,28 +488,13 @@ EOC attr_accessor element add_need_initialize_variable(element) end - - def current_element(rss) - rss.channel.skipDays.last - end - end end class SkipHoursBase include Base - def_array_element("hours") - - def new_hour - hour = self.class::Hour.new(@maker) - @hours << hour - hour - end - - def current_element(rss) - rss.channel.skipHours - end + def_array_element("hour") class HourBase include Base @@ -326,11 +503,6 @@ EOC attr_accessor element add_need_initialize_variable(element) end - - def current_element(rss) - rss.channel.skipHours.last - end - end end @@ -341,33 +513,88 @@ EOC 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") - def new_category - category = self.class::Category.new(@maker) - @categories << category - category - end + def_array_element("category", "categories") class CategoryBase include Base - %w(domain content).each do |element| + %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 + include Base + + def_array_element("link") + + class LinkBase + include Base + + %w(href rel type hreflang title length).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + end + + class AuthorsBase + include Base + + def_array_element("author") + + class AuthorBase + include Base + include AtomPersonConstructBase + end + end + + class ContributorsBase + include Base + + def_array_element("contributor") + + class ContributorBase + include Base + include AtomPersonConstructBase + end + end + + class GeneratorBase + include Base + + %w(uri version content).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + class CopyrightBase + include Base + include AtomTextConstructBase + end + + class DescriptionBase + include Base + include AtomTextConstructBase + end + + class TitleBase + include Base + include AtomTextConstructBase + end end class ImageBase @@ -377,21 +604,17 @@ EOC 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 - def_array_element("items") - + def_array_element("item") + attr_accessor :do_sort, :max_size def initialize(maker) @@ -407,17 +630,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 - item - end - private def sort_if_need if @do_sort.respond_to?(:call) @@ -435,15 +648,17 @@ EOC class ItemBase include Base - - %w(guid enclosure source categories).each do |element| + 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}(rss, current) - @#{element}.to_rss(rss, current) + def setup_#{element}(feed, current) + @#{element}.to_feed(feed, current) end def make_#{element} @@ -451,30 +666,77 @@ EOC end EOC end - - %w(title link description date author comments).each do |element| + + %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 + + 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) - 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 @@ -484,7 +746,7 @@ EOC add_need_initialize_variable(element) end end - + class EnclosureBase include Base @@ -493,18 +755,168 @@ EOC add_need_initialize_variable(element) end end - + class SourceBase include Base - %w(url content).each do |element| + %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 + + def make_#{element} + self.class::#{Utils.to_class_name(element)}.new(@maker) + end + EOC + end + + %w(id content date).each do |element| attr_accessor element 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 + + 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 + include Base + + %w(url).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + LinksBase = ChannelBase::LinksBase + + class LogoBase + include Base + + %w(uri).each do |element| + attr_accessor element + add_need_initialize_variable(element) + end + end + + class RightsBase + include Base + include AtomTextConstructBase + end + + class SubtitleBase + include Base + include AtomTextConstructBase + end + + class TitleBase + include Base + include AtomTextConstructBase + end end - + CategoriesBase = ChannelBase::CategoriesBase - + AuthorsBase = ChannelBase::AuthorsBase + LinksBase = ChannelBase::LinksBase + ContributorsBase = ChannelBase::ContributorsBase + + class RightsBase + include Base + include AtomTextConstructBase + end + + class DescriptionBase + include Base + include AtomTextConstructBase + end + + class ContentBase + include Base + include AtomTextConstructBase::EnsureXMLContent + + %w(type src content xml_content).each do |element| + attr element, element != "xml_content" + add_need_initialize_variable(element) + end + + def xml_content=(content) + content = ensure_xml_content(content) if inline_xhtml? + @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 + + 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 + include Base + include AtomTextConstructBase + end end end @@ -515,12 +927,6 @@ EOC attr_accessor element add_need_initialize_variable(element) end - - def current_element(rss) - rss.textinput - end - end - end end diff --git a/lib/rss/maker/dublincore.rb b/lib/rss/maker/dublincore.rb index b208d5fcb2..088ae60942 100644 --- a/lib/rss/maker/dublincore.rb +++ b/lib/rss/maker/dublincore.rb @@ -24,8 +24,8 @@ module RSS #{full_plural_klass_name}.new(@maker) end - def setup_#{full_plural_name}(rss, current) - @#{full_plural_name}.to_rss(rss, current) + def setup_#{full_plural_name}(feed, current) + @#{full_plural_name}.to_feed(feed, current) end def #{full_name} @@ -36,6 +36,17 @@ module RSS @#{full_plural_name}[0] = #{full_klass_name}.new(self) @#{full_plural_name}[0].value = new_value end + + def new_#{full_name}(value=nil) + #{full_name} = #{full_klass_name}.new(self) + #{full_name}.value = value + @#{full_plural_name} << #{full_name} + if block_given? + yield #{full_name} + else + #{full_name} + end + end EOC end @@ -48,25 +59,14 @@ 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)}" module_eval(<<-EOC, __FILE__, __LINE__) class #{plural_klass_name}Base include Base - def_array_element(#{plural_name.dump}) - - def new_#{name} - #{name} = self.class::#{klass_name}.new(self) - @#{plural_name} << #{name} - #{name} - end + def_array_element(#{name.dump}, #{plural_name.dump}) - def to_rss(rss, current) - @#{plural_name}.each do |#{name}| - #{name}.to_rss(rss, current) - end - end - class #{klass_name}Base include Base @@ -78,6 +78,13 @@ EOC def have_required_values? @value end + + def to_feed(feed, current) + if value and current.respond_to?(:dc_#{name}) + new_item = current.class::#{full_klass_name}.new(value) + current.dc_#{plural_name} << new_item + end + end end end EOC @@ -88,16 +95,9 @@ EOC plural_name ||= "#{name}s" klass_name = Utils.to_class_name(name) plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}" - full_klass_name = "DublinCore#{klass_name}" - klass.module_eval(<<-EOC, *Utils.get_file_and_line_from_caller(1)) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) class #{plural_klass_name} < #{plural_klass_name}Base class #{klass_name} < #{klass_name}Base - def to_rss(rss, current) - if value and current.respond_to?(:dc_#{name}) - new_item = current.class::#{full_klass_name}.new(value) - current.dc_#{plural_name} << new_item - end - end end end EOC @@ -107,64 +107,36 @@ EOC class ChannelBase include DublinCoreModel - - remove_method(:date) - remove_method(:date=) - alias_method(:date, :dc_date) - alias_method(:date=, :dc_date=) end class ImageBase; include DublinCoreModel; end class ItemsBase class ItemBase include DublinCoreModel - - remove_method(:date) - remove_method(:date=) - alias_method(:date, :dc_date) - alias_method(:date=, :dc_date=) end end class TextinputBase; include DublinCoreModel; end - class RSS10 - class Channel - DublinCoreModel.install_dublin_core(self) - end - - class Image - DublinCoreModel.install_dublin_core(self) - end - - class Items - class Item + makers.each do |maker| + maker.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + class Channel DublinCoreModel.install_dublin_core(self) end - end - class Textinput - DublinCoreModel.install_dublin_core(self) - end - end - - class RSS09 - class Channel - DublinCoreModel.install_dublin_core(self) - end + class Image + DublinCoreModel.install_dublin_core(self) + end - class Image - DublinCoreModel.install_dublin_core(self) - end + class Items + class Item + DublinCoreModel.install_dublin_core(self) + end + end - class Items - class Item + class Textinput DublinCoreModel.install_dublin_core(self) end - end - - class Textinput - DublinCoreModel.install_dublin_core(self) - end + EOC end end end diff --git a/lib/rss/maker/entry.rb b/lib/rss/maker/entry.rb new file mode 100644 index 0000000000..baa22c5bf1 --- /dev/null +++ b/lib/rss/maker/entry.rb @@ -0,0 +1,167 @@ +require "rss/maker/atom" +require "rss/maker/feed" + +module RSS + module Maker + module Atom + class Entry < RSSBase + def initialize + super("1.0") + @feed_type = "atom" + @feed_subtype = "entry" + end + + private + def make_feed + ::RSS::Atom::Entry.new(@version, @encoding, @standalone) + end + + def setup_elements(entry) + setup_items(entry) + end + + class Channel < ChannelBase + class SkipDays < SkipDaysBase + class Day < DayBase + end + end + + class SkipHours < SkipHoursBase + class Hour < HourBase + end + end + + class Cloud < CloudBase + end + + Categories = Feed::Channel::Categories + Links = Feed::Channel::Links + Authors = Feed::Channel::Authors + Contributors = Feed::Channel::Contributors + + class Generator < GeneratorBase + include AtomGenerator + + def self.not_set_name + "maker.channel.generator" + end + end + + Copyright = Feed::Channel::Copyright + + class Description < DescriptionBase + end + + Title = Feed::Channel::Title + end + + class Image < ImageBase + end + + class Items < ItemsBase + def to_feed(entry) + (normalize.first || Item.new(@maker)).to_feed(entry) + end + + class Item < ItemBase + def to_feed(entry) + set_default_values do + setup_values(entry) + entry.dc_dates.clear + setup_other_elements(entry) + unless have_required_values? + raise NotSetError.new("maker.item", not_set_required_variables) + end + 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) + end + + def variables + super + ["updated"] + end + + def variable_is_set? + super or !authors.empty? + end + + def not_set_required_variables + set_default_values do + vars = super + if authors.all? {|author| !author.have_required_values?} + vars << "author" + end + vars << "title" unless title.have_required_values? + vars + end + end + + def _set_default_values(&block) + keep = { + :authors => authors.to_a.dup, + :contributors => contributors.to_a.dup, + :categories => categories.to_a.dup, + :id => id, + :links => links.to_a.dup, + :rights => @rights, + :title => @title, + :updated => updated, + } + authors.replace(@maker.channel.authors) if keep[:authors].empty? + if keep[:contributors].empty? + contributors.replace(@maker.channel.contributors) + end + if keep[:categories].empty? + categories.replace(@maker.channel.categories) + end + 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 + end + @title = @maker.channel.title unless keep[:title].variable_is_set? + self.updated ||= @maker.channel.updated + super(&block) + ensure + authors.replace(keep[:authors]) + contributors.replace(keep[:contributors]) + categories.replace(keep[:categories]) + links.replace(keep[:links]) + self.id = keep[:id] + @rights = keep[:rights] + @title = keep[:title] + self.updated = keep[:prev_updated] + end + + Guid = Feed::Items::Item::Guid + Enclosure = Feed::Items::Item::Enclosure + Source = Feed::Items::Item::Source + Categories = Feed::Items::Item::Categories + Authors = Feed::Items::Item::Authors + Contributors = Feed::Items::Item::Contributors + Links = Feed::Items::Item::Links + Rights = Feed::Items::Item::Rights + Description = Feed::Items::Item::Description + Title = Feed::Items::Item::Title + Content = Feed::Items::Item::Content + end + end + + class Textinput < TextinputBase + end + end + end + + add_maker("atom:entry", Atom::Entry) + add_maker("atom1.0:entry", Atom::Entry) + end +end diff --git a/lib/rss/maker/feed.rb b/lib/rss/maker/feed.rb new file mode 100644 index 0000000000..ac26788102 --- /dev/null +++ b/lib/rss/maker/feed.rb @@ -0,0 +1,429 @@ +require "rss/maker/atom" + +module RSS + module Maker + module Atom + class Feed < RSSBase + def initialize + super("1.0") + @feed_type = "atom" + @feed_subtype = "feed" + end + + private + def make_feed + ::RSS::Atom::Feed.new(@version, @encoding, @standalone) + end + + def setup_elements(feed) + setup_channel(feed) + setup_image(feed) + setup_items(feed) + end + + class Channel < ChannelBase + def to_feed(feed) + set_default_values do + setup_values(feed) + feed.dc_dates.clear + setup_other_elements(feed) + if image_favicon.about + icon = feed.class::Icon.new + icon.content = image_favicon.about + feed.icon = icon + end + unless have_required_values? + raise NotSetError.new("maker.channel", + not_set_required_variables) + end + end + end + + def have_required_values? + super and + (!authors.empty? or + @maker.items.any? {|item| !item.authors.empty?}) + end + + private + def required_variable_names + %w(id updated) + end + + def variables + super + %w(id updated) + end + + def variable_is_set? + super or !authors.empty? + end + + def not_set_required_variables + vars = super + if authors.empty? and + @maker.items.all? {|item| item.author.to_s.empty?} + vars << "author" + end + vars << "title" unless title.have_required_values? + vars + end + + def _set_default_values(&block) + keep = { + :id => id, + :updated => updated, + } + self.id ||= about + self.updated ||= dc_date + super(&block) + ensure + self.id = keep[:id] + self.updated = keep[:updated] + end + + class SkipDays < SkipDaysBase + def to_feed(*args) + end + + class Day < DayBase + end + end + + class SkipHours < SkipHoursBase + def to_feed(*args) + end + + class Hour < HourBase + end + end + + class Cloud < CloudBase + def to_feed(*args) + end + end + + class Categories < CategoriesBase + class Category < CategoryBase + include AtomCategory + + def self.not_set_name + "maker.channel.category" + end + end + end + + class Links < LinksBase + class Link < LinkBase + include AtomLink + + def self.not_set_name + "maker.channel.link" + end + end + end + + AtomPersons.def_atom_persons(self, "author", "maker.channel.author") + AtomPersons.def_atom_persons(self, "contributor", + "maker.channel.contributor") + + class Generator < GeneratorBase + include AtomGenerator + + def self.not_set_name + "maker.channel.generator" + end + end + + AtomTextConstruct.def_atom_text_construct(self, "rights", + "maker.channel.copyright", + "Copyright") + AtomTextConstruct.def_atom_text_construct(self, "subtitle", + "maker.channel.description", + "Description") + AtomTextConstruct.def_atom_text_construct(self, "title", + "maker.channel.title") + end + + class Image < ImageBase + def to_feed(feed) + logo = feed.class::Logo.new + class << logo + alias url= content= + end + set = setup_values(logo) + class << logo + undef url= + end + if set + feed.logo = logo + set_parent(logo, feed) + setup_other_elements(feed, logo) + elsif variable_is_set? + raise NotSetError.new("maker.image", not_set_required_variables) + end + end + + private + def required_variable_names + %w(url) + end + end + + class Items < ItemsBase + def to_feed(feed) + normalize.each do |item| + item.to_feed(feed) + end + setup_other_elements(feed, feed.entries) + end + + class Item < ItemBase + def to_feed(feed) + set_default_values do + entry = feed.class::Entry.new + set = setup_values(entry) + setup_other_elements(feed, entry) + if set + feed.entries << entry + set_parent(entry, feed) + elsif variable_is_set? + raise NotSetError.new("maker.item", not_set_required_variables) + end + 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) + end + + def variables + super + ["updated"] + end + + def not_set_required_variables + vars = super + vars << "title" unless title.have_required_values? + vars + end + + def _set_default_values(&block) + keep = { + :id => id, + :updated => updated, + } + self.id ||= link + self.updated ||= dc_date + super(&block) + ensure + self.id = keep[:id] + self.updated = keep[:updated] + end + + class Guid < GuidBase + def to_feed(feed, current) + end + end + + class Enclosure < EnclosureBase + def to_feed(feed, current) + end + end + + class Source < SourceBase + def to_feed(feed, current) + source = current.class::Source.new + setup_values(source) + current.source = source + set_parent(source, current) + setup_other_elements(feed, source) + current.source = nil if source.to_s == "" + end + + private + def required_variable_names + [] + end + + def variables + super + ["updated"] + end + + AtomPersons.def_atom_persons(self, "author", + "maker.item.source.author") + AtomPersons.def_atom_persons(self, "contributor", + "maker.item.source.contributor") + + class Categories < CategoriesBase + class Category < CategoryBase + include AtomCategory + + def self.not_set_name + "maker.item.source.category" + end + end + end + + class Generator < GeneratorBase + include AtomGenerator + + def self.not_set_name + "maker.item.source.generator" + end + end + + class Icon < IconBase + def to_feed(feed, current) + icon = current.class::Icon.new + class << icon + alias url= content= + end + set = setup_values(icon) + class << icon + undef url= + end + if set + current.icon = icon + set_parent(icon, current) + setup_other_elements(feed, icon) + elsif variable_is_set? + raise NotSetError.new("maker.item.source.icon", + not_set_required_variables) + end + end + + private + def required_variable_names + %w(url) + end + end + + class Links < LinksBase + class Link < LinkBase + include AtomLink + + def self.not_set_name + "maker.item.source.link" + end + end + end + + class Logo < LogoBase + include AtomLogo + + def self.not_set_name + "maker.item.source.logo" + end + end + + maker_name_base = "maker.item.source." + maker_name = "#{maker_name_base}rights" + AtomTextConstruct.def_atom_text_construct(self, "rights", + maker_name) + maker_name = "#{maker_name_base}subtitle" + AtomTextConstruct.def_atom_text_construct(self, "subtitle", + maker_name) + maker_name = "#{maker_name_base}title" + AtomTextConstruct.def_atom_text_construct(self, "title", + maker_name) + end + + class Categories < CategoriesBase + class Category < CategoryBase + include AtomCategory + + def self.not_set_name + "maker.item.category" + end + end + end + + AtomPersons.def_atom_persons(self, "author", "maker.item.author") + AtomPersons.def_atom_persons(self, "contributor", + "maker.item.contributor") + + class Links < LinksBase + class Link < LinkBase + include AtomLink + + def self.not_set_name + "maker.item.link" + end + end + end + + AtomTextConstruct.def_atom_text_construct(self, "rights", + "maker.item.rights") + AtomTextConstruct.def_atom_text_construct(self, "summary", + "maker.item.description", + "Description") + AtomTextConstruct.def_atom_text_construct(self, "title", + "maker.item.title") + + class Content < ContentBase + def to_feed(feed, current) + content = current.class::Content.new + if setup_values(content) + content.src = nil if content.src and content.content + current.content = content + set_parent(content, current) + setup_other_elements(feed, content) + elsif variable_is_set? + raise NotSetError.new("maker.item.content", + not_set_required_variables) + end + end + + alias_method(:xml, :xml_content) + + private + def required_variable_names + if out_of_line? + %w(type) + elsif xml_type? + %w(xml_content) + else + %w(content) + end + end + + def variables + if out_of_line? + super + elsif xml_type? + super + %w(xml) + else + super + end + end + + def xml_type? + _type = type + return false if _type.nil? + _type == "xhtml" or + /(?:\+xml|\/xml)$/i =~ _type or + %w(text/xml-external-parsed-entity + application/xml-external-parsed-entity + application/xml-dtd).include?(_type.downcase) + end + end + end + end + + class Textinput < TextinputBase + end + end + end + + add_maker("atom", Atom::Feed) + add_maker("atom:feed", Atom::Feed) + add_maker("atom1.0", Atom::Feed) + add_maker("atom1.0:feed", Atom::Feed) + end +end diff --git a/lib/rss/maker/image.rb b/lib/rss/maker/image.rb index ed51c8ecba..e3469d0597 100644 --- a/lib/rss/maker/image.rb +++ b/lib/rss/maker/image.rb @@ -11,11 +11,11 @@ module RSS 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) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) attr_reader :#{name} - def setup_#{name}(rss, current) + def setup_#{name}(feed, current) if @#{name} - @#{name}.to_rss(rss, current) + @#{name}.to_feed(feed, current) end end @@ -25,6 +25,14 @@ module RSS EOC end + def self.install_image_item(klass) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + class ImageItem < ImageItemBase + DublinCoreModel.install_dublin_core(self) + end +EOC + end + class ImageItemBase include Base include Maker::DublinCoreModel @@ -42,6 +50,15 @@ EOC def have_required_values? @about end + + def to_feed(feed, current) + if current.respond_to?(:image_item=) and have_required_values? + item = current.class::ImageItem.new + setup_values(item) + setup_other_elements(item) + current.image_item = item + end + end end end @@ -54,9 +71,9 @@ EOC klass.add_other_element(name) klass.module_eval(<<-EOC, __FILE__, __LINE__+1) attr_reader :#{name} - def setup_#{name}(rss, current) + def setup_#{name}(feed, current) if @#{name} - @#{name}.to_rss(rss, current) + @#{name}.to_feed(feed, current) end end @@ -66,6 +83,14 @@ EOC EOC end + def self.install_image_favicon(klass) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + class ImageFavicon < ImageFaviconBase + DublinCoreModel.install_dublin_core(self) + end +EOC + end + class ImageFaviconBase include Base include Maker::DublinCoreModel @@ -79,6 +104,15 @@ EOC def have_required_values? @about and @image_size end + + def to_feed(feed, current) + if current.respond_to?(:image_favicon=) and have_required_values? + favicon = current.class::ImageFavicon.new + setup_values(favicon) + setup_other_elements(favicon) + current.image_favicon = favicon + end + end end end @@ -88,58 +122,18 @@ EOC class ItemBase; include Maker::ImageItemModel; end end - class RSS10 - class Items - class Item - class ImageItem < ImageItemBase - DublinCoreModel.install_dublin_core(self) - def to_rss(rss, current) - if @about - item = ::RSS::ImageItemModel::ImageItem.new(@about, @resource) - setup_values(item) - setup_other_elements(item) - current.image_item = item - end - end - end + makers.each do |maker| + maker.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + class Channel + ImageFaviconModel.install_image_favicon(self) end - end - - class Channel - class ImageFavicon < ImageFaviconBase - DublinCoreModel.install_dublin_core(self) - def to_rss(rss, current) - if @about and @image_size - args = [@about, @image_size] - favicon = ::RSS::ImageFaviconModel::ImageFavicon.new(*args) - setup_values(favicon) - setup_other_elements(favicon) - current.image_favicon = favicon - end - end - end - end - end - class RSS09 - class Items - class Item - class ImageItem < ImageItemBase - DublinCoreModel.install_dublin_core(self) - def to_rss(*args) - end + class Items + class Item + ImageItemModel.install_image_item(self) end end - end - - class Channel - class ImageFavicon < ImageFaviconBase - DublinCoreModel.install_dublin_core(self) - def to_rss(*args) - end - end - end + EOC end - end end diff --git a/lib/rss/maker/taxonomy.rb b/lib/rss/maker/taxonomy.rb index 2e54ea66eb..2e53a4e1f4 100644 --- a/lib/rss/maker/taxonomy.rb +++ b/lib/rss/maker/taxonomy.rb @@ -15,17 +15,17 @@ module RSS def make_taxo_topics self.class::TaxonomyTopics.new(@maker) end - - def setup_taxo_topics(rss, current) - @taxo_topics.to_rss(rss, current) + + def setup_taxo_topics(feed, current) + @taxo_topics.to_feed(feed, current) end EOC end def self.install_taxo_topics(klass) - klass.module_eval(<<-EOC, *Utils.get_file_and_line_from_caller(1)) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) class TaxonomyTopics < TaxonomyTopicsBase - def to_rss(rss, current) + def to_feed(feed, current) if current.respond_to?(:taxo_topics) topics = current.class::TaxonomyTopics.new bag = topics.Bag @@ -43,7 +43,8 @@ EOC include Base attr_reader :resources - def_array_element("resources") + def_array_element("resource") + remove_method :new_resource end end @@ -59,8 +60,8 @@ EOC self.class::TaxonomyTopics.new(@maker) end - def setup_taxo_topics(rss, current) - @taxo_topics.to_rss(rss, current) + def setup_taxo_topics(feed, current) + @taxo_topics.to_feed(feed, current) end def taxo_topic @@ -75,25 +76,21 @@ EOC end def self.install_taxo_topic(klass) - klass.module_eval(<<-EOC, *Utils.get_file_and_line_from_caller(1)) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) class TaxonomyTopics < TaxonomyTopicsBase class TaxonomyTopic < TaxonomyTopicBase DublinCoreModel.install_dublin_core(self) TaxonomyTopicsModel.install_taxo_topics(self) - def to_rss(rss, current) + def to_feed(feed, current) if current.respond_to?(:taxo_topics) topic = current.class::TaxonomyTopic.new(value) topic.taxo_link = value - taxo_topics.to_rss(rss, topic) if taxo_topics + taxo_topics.to_feed(feed, topic) if taxo_topics current.taxo_topics << topic - setup_other_elements(rss) + setup_other_elements(feed, topic) end end - - def current_element(rss) - super.taxo_topics.last - end end end EOC @@ -102,20 +99,8 @@ EOC class TaxonomyTopicsBase include Base - def_array_element("taxo_topics") - - def new_taxo_topic - taxo_topic = self.class::TaxonomyTopic.new(self) - @taxo_topics << taxo_topic - taxo_topic - end + def_array_element("taxo_topic", nil, "self.class::TaxonomyTopic") - def to_rss(rss, current) - @taxo_topics.each do |taxo_topic| - taxo_topic.to_rss(rss, current) - end - end - class TaxonomyTopicBase include Base include DublinCoreModel @@ -147,32 +132,20 @@ EOC end end - class RSS10 - TaxonomyTopicModel.install_taxo_topic(self) - - class Channel - TaxonomyTopicsModel.install_taxo_topics(self) - end + makers.each do |maker| + maker.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + TaxonomyTopicModel.install_taxo_topic(self) - class Items - class Item + class Channel TaxonomyTopicsModel.install_taxo_topics(self) end - end - end - - class RSS09 - TaxonomyTopicModel.install_taxo_topic(self) - - class Channel - TaxonomyTopicsModel.install_taxo_topics(self) - end - class Items - class Item - TaxonomyTopicsModel.install_taxo_topics(self) + class Items + class Item + TaxonomyTopicsModel.install_taxo_topics(self) + end end - end + EOC end end end diff --git a/lib/rss/maker/trackback.rb b/lib/rss/maker/trackback.rb index 32254a040c..09a2fceb2d 100644 --- a/lib/rss/maker/trackback.rb +++ b/lib/rss/maker/trackback.rb @@ -11,9 +11,9 @@ module RSS name = "#{RSS::TRACKBACK_PREFIX}_ping" klass.add_need_initialize_variable(name) klass.add_other_element(name) - klass.module_eval(<<-EOC, __FILE__, __LINE__+1) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) attr_accessor :#{name} - def setup_#{name}(rss, current) + def setup_#{name}(feed, current) if #{name} and current.respond_to?(:#{name}=) current.#{name} = #{name} end @@ -23,14 +23,14 @@ module RSS 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) + klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) attr_accessor :#{name} def make_#{name} self.class::TrackBackAbouts.new(self) end - def setup_#{name}(rss, current) - @#{name}.to_rss(rss, current) + def setup_#{name}(feed, current) + @#{name}.to_feed(feed, current) end EOC end @@ -38,20 +38,8 @@ module RSS class TrackBackAboutsBase include Base - def_array_element("abouts") - - def new_about - about = self.class::TrackBackAbout.new(@maker) - @abouts << about - about - end + def_array_element("about", nil, "self.class::TrackBackAbout") - def to_rss(rss, current) - @abouts.each do |about| - about.to_rss(rss, current) - end - end - class TrackBackAboutBase include Base @@ -62,65 +50,38 @@ module RSS alias_method(:resource=, :value=) alias_method(:content, :value) alias_method(:content=, :value=) - + def have_required_values? @value end - - end - end - end - - class ItemsBase - class ItemBase; include TrackBackModel; end - end - class RSS10 - class Items - class Item - class TrackBackAbouts < TrackBackAboutsBase - class TrackBackAbout < TrackBackAboutBase - def to_rss(rss, current) - if resource - about = ::RSS::TrackBackModel10::TrackBackAbout.new(resource) - current.trackback_abouts << about - end - end + def to_feed(feed, current) + if current.respond_to?(:trackback_abouts) and have_required_values? + about = current.class::TrackBackAbout.new + setup_values(about) + setup_other_elements(about) + current.trackback_abouts << about end end end end end - class RSS09 - class Items - class Item - class TrackBackAbouts < TrackBackAboutsBase - def to_rss(*args) - end - class TrackBackAbout < TrackBackAboutBase - end - end - end - end + class ItemsBase + class ItemBase; include TrackBackModel; end end - - class RSS20 - class Items - class Item - class TrackBackAbouts < TrackBackAboutsBase - class TrackBackAbout < TrackBackAboutBase - def to_rss(rss, current) - if content - about = ::RSS::TrackBackModel20::TrackBackAbout.new(content) - current.trackback_abouts << about - end + + makers.each do |maker| + maker.module_eval(<<-EOC, __FILE__, __LINE__ + 1) + class Items + class Item + class TrackBackAbouts < TrackBackAboutsBase + class TrackBackAbout < TrackBackAboutBase end end end end - end + EOC end - end end -- cgit v1.2.3