eaiovnaovbqoebvqoeavibavo atom.rb000064400000054646147634040010006047 0ustar00require 'rss/parser' module RSS ## # Atom is an XML-based document format that is used to describe 'feeds' of related information. # A typical use is in a news feed where the information is periodically updated and which users # can subscribe to. The Atom format is described in http://tools.ietf.org/html/rfc4287 # # The Atom module provides support in reading and creating feeds. # # See the RSS module for examples consuming and creating feeds module Atom ## # The Atom URI W3C Namespace URI = "http://www.w3.org/2005/Atom" ## # The XHTML URI W3C Namespace XHTML_URI = "http://www.w3.org/1999/xhtml" module CommonModel NSPOOL = {} ELEMENTS = [] def self.append_features(klass) super klass.install_must_call_validator("atom", URI) [ ["lang", :xml], ["base", :xml], ].each do |name, uri, required| klass.install_get_attribute(name, uri, required, [nil, :inherit]) end klass.class_eval do class << self def required_uri URI end def need_parent? true end end end end end module ContentModel module ClassMethods def content_type @content_type ||= nil end end class << self def append_features(klass) super klass.extend(ClassMethods) klass.content_setup(klass.content_type, klass.tag_name) end end def maker_target(target) target end private def setup_maker_element_writer "#{self.class.name.split(/::/).last.downcase}=" end def setup_maker_element(target) target.__send__(setup_maker_element_writer, content) super end end module URIContentModel class << self def append_features(klass) super klass.class_eval do @content_type = [nil, :uri] include(ContentModel) end end end end # The TextConstruct module is used to define a Text construct Atom element, # which is used to store small quantities of human-readable text # # The TextConstruct has a type attribute, e.g. text, html, xhtml module TextConstruct def self.append_features(klass) super klass.class_eval do [ ["type", ""], ].each do |name, uri, required| install_get_attribute(name, uri, required, :text_type) end content_setup add_need_initialize_variable("xhtml") class << self def xml_getter "xhtml" end def xml_setter "xhtml=" end end end end attr_writer :xhtml def xhtml return @xhtml if @xhtml.nil? if @xhtml.is_a?(XML::Element) and [@xhtml.name, @xhtml.uri] == ["div", XHTML_URI] return @xhtml end children = @xhtml children = [children] unless children.is_a?(Array) XML::Element.new("div", nil, XHTML_URI, {"xmlns" => XHTML_URI}, children) end # Returns true if type is "xhtml" def have_xml_content? @type == "xhtml" end def atom_validate(ignore_unknown_element, tags, uri) if have_xml_content? if @xhtml.nil? raise MissingTagError.new("div", tag_name) end unless [@xhtml.name, @xhtml.uri] == ["div", XHTML_URI] raise NotExpectedTagError.new(@xhtml.name, @xhtml.uri, tag_name) end end end private def maker_target(target) target.__send__(self.class.name.split(/::/).last.downcase) {|x| x} end def setup_maker_attributes(target) target.type = type target.content = content target.xml_content = @xhtml end end # The PersonConstruct module is used to define a Person Atom element that can be # used to describe a person, corporation, or similar entity # # The PersonConstruct has a Name, Uri, and Email child elements module PersonConstruct # Adds attributes for name, uri, and email to the +klass+ def self.append_features(klass) super klass.class_eval do [ ["name", nil], ["uri", "?"], ["email", "?"], ].each do |tag, occurs| install_have_attribute_element(tag, URI, occurs, nil, :content) end end end def maker_target(target) target.__send__("new_#{self.class.name.split(/::/).last.downcase}") end # The name of the person or entity class Name < RSS::Element include CommonModel include ContentModel end # The URI of the person or entity class Uri < RSS::Element include CommonModel include URIContentModel end # The email of the person or entity class Email < RSS::Element include CommonModel include ContentModel end end # Element used to describe an Atom date and time in the ISO 8601 format # # Examples: # * 2013-03-04T15:30:02Z # * 2013-03-04T10:30:02-05:00 module DateConstruct def self.append_features(klass) super klass.class_eval do @content_type = :w3cdtf include(ContentModel) end end # Raises NotAvailableValueError if element content is nil def atom_validate(ignore_unknown_element, tags, uri) raise NotAvailableValueError.new(tag_name, "") if content.nil? end end module DuplicateLinkChecker # Checks if there are duplicate links with the same type and hreflang attributes # that have an alternate (or empty) rel attribute # # Raises a TooMuchTagError if there are duplicates found def validate_duplicate_links(links) link_infos = {} links.each do |link| rel = link.rel || "alternate" next unless rel == "alternate" key = [link.hreflang, link.type] if link_infos.has_key?(key) raise TooMuchTagError.new("link", tag_name) end link_infos[key] = true end end end # Atom feed element # # A Feed has several metadata attributes in addition to a number of Entry child elements class Feed < RSS::Element include RootElementMixin include CommonModel include DuplicateLinkChecker install_ns('', URI) [ ["author", "*", :children], ["category", "*", :children, "categories"], ["contributor", "*", :children], ["generator", "?"], ["icon", "?", nil, :content], ["id", nil, nil, :content], ["link", "*", :children], ["logo", "?"], ["rights", "?"], ["subtitle", "?", nil, :content], ["title", nil, nil, :content], ["updated", nil, nil, :content], ["entry", "*", :children, "entries"], ].each do |tag, occurs, type, *args| type ||= :child __send__("install_have_#{type}_element", tag, URI, occurs, tag, *args) end # Creates a new Atom feed def initialize(version=nil, encoding=nil, standalone=nil) super("1.0", version, encoding, standalone) @feed_type = "atom" @feed_subtype = "feed" end alias_method :items, :entries # Returns true if there are any authors for the feed or any of the Entry # child elements have an author def have_author? authors.any? {|author| !author.to_s.empty?} or entries.any? {|entry| entry.have_author?(false)} end private def atom_validate(ignore_unknown_element, tags, uri) unless have_author? raise MissingTagError.new("author", tag_name) end validate_duplicate_links(links) end def have_required_elements? super and have_author? end def maker_target(maker) maker.channel end def setup_maker_element(channel) prev_dc_dates = channel.dc_dates.to_a.dup super channel.about = id.content if id channel.dc_dates.replace(prev_dc_dates) end def setup_maker_elements(channel) super items = channel.maker.items entries.each do |entry| entry.setup_maker(items) end end class Author < RSS::Element include CommonModel include PersonConstruct end class Category < RSS::Element include CommonModel [ ["term", "", true], ["scheme", "", false, [nil, :uri]], ["label", ""], ].each do |name, uri, required, type| install_get_attribute(name, uri, required, type) end private def maker_target(target) target.new_category end end class Contributor < RSS::Element include CommonModel include PersonConstruct end class Generator < RSS::Element include CommonModel include ContentModel [ ["uri", "", false, [nil, :uri]], ["version", ""], ].each do |name, uri, required, type| install_get_attribute(name, uri, required, type) end private def setup_maker_attributes(target) target.generator do |generator| generator.uri = uri if uri generator.version = version if version end end end # Atom Icon element # # Image that provides a visual identification for the Feed. Image should have an aspect # ratio of 1:1 class Icon < RSS::Element include CommonModel include URIContentModel end # Atom ID element # # Universally Unique Identifier (UUID) for the Feed class Id < RSS::Element include CommonModel include URIContentModel end # Defines an Atom Link element # # A Link has the following attributes: # * href # * rel # * type # * hreflang # * title # * length class Link < RSS::Element include CommonModel [ ["href", "", true, [nil, :uri]], ["rel", ""], ["type", ""], ["hreflang", ""], ["title", ""], ["length", ""], ].each do |name, uri, required, type| install_get_attribute(name, uri, required, type) end private def maker_target(target) target.new_link end end # Atom Logo element # # Image that provides a visual identification for the Feed. Image should have an aspect # ratio of 2:1 (horizontal:vertical) class Logo < RSS::Element include CommonModel include URIContentModel def maker_target(target) target.maker.image end private def setup_maker_element_writer "url=" end end # Atom Rights element # # TextConstruct that contains copyright information regarding the content in an Entry or Feed class Rights < RSS::Element include CommonModel include TextConstruct end # Atom Subtitle element # # TextConstruct that conveys a description or subtitle for a Feed class Subtitle < RSS::Element include CommonModel include TextConstruct end # Atom Title element # # TextConstruct that conveys a description or title for a feed or Entry class Title < RSS::Element include CommonModel include TextConstruct end # Atom Updated element # # DateConstruct indicating the most recent time when an Entry or Feed was modified # in a way the publisher considers significant class Updated < RSS::Element include CommonModel include DateConstruct end # Defines a child Atom Entry element for an Atom Feed class Entry < RSS::Element include CommonModel include DuplicateLinkChecker [ ["author", "*", :children], ["category", "*", :children, "categories"], ["content", "?", :child], ["contributor", "*", :children], ["id", nil, nil, :content], ["link", "*", :children], ["published", "?", :child, :content], ["rights", "?", :child], ["source", "?"], ["summary", "?", :child], ["title", nil], ["updated", nil, :child, :content], ].each do |tag, occurs, type, *args| type ||= :attribute __send__("install_have_#{type}_element", tag, URI, occurs, tag, *args) end # Returns whether any of the following are true # * There are any authors in the feed # * If the parent element has an author and the +check_parent+ parameter was given. # * There is a source element that has an author def have_author?(check_parent=true) authors.any? {|author| !author.to_s.empty?} or (check_parent and @parent and @parent.have_author?) or (source and source.have_author?) end private def atom_validate(ignore_unknown_element, tags, uri) unless have_author? raise MissingTagError.new("author", tag_name) end validate_duplicate_links(links) end def have_required_elements? super and have_author? end def maker_target(items) if items.respond_to?("items") # For backward compatibility items = items.items end items.new_item end Author = Feed::Author Category = Feed::Category class Content < RSS::Element include CommonModel class << self def xml_setter "xml=" end def xml_getter "xml" end end [ ["type", ""], ["src", "", false, [nil, :uri]], ].each do |name, uri, required, type| install_get_attribute(name, uri, required, type) end content_setup add_need_initialize_variable("xml") attr_writer :xml def have_xml_content? inline_xhtml? or inline_other_xml? end def xml return @xml unless inline_xhtml? return @xml if @xml.nil? if @xml.is_a?(XML::Element) and [@xml.name, @xml.uri] == ["div", XHTML_URI] return @xml end children = @xml children = [children] unless children.is_a?(Array) XML::Element.new("div", nil, XHTML_URI, {"xmlns" => XHTML_URI}, children) end def xhtml if inline_xhtml? xml else nil end end def atom_validate(ignore_unknown_element, tags, uri) if out_of_line? raise MissingAttributeError.new(tag_name, "type") if @type.nil? unless (content.nil? or content.empty?) raise NotAvailableValueError.new(tag_name, content) end elsif inline_xhtml? if @xml.nil? raise MissingTagError.new("div", tag_name) end unless @xml.name == "div" and @xml.uri == XHTML_URI raise NotExpectedTagError.new(@xml.name, @xml.uri, tag_name) end end end def inline_text? !out_of_line? and [nil, "text", "html"].include?(@type) end def inline_html? return false if out_of_line? @type == "html" or mime_split == ["text", "html"] end def inline_xhtml? !out_of_line? and @type == "xhtml" end def inline_other? return false if out_of_line? media_type, subtype = mime_split return false if media_type.nil? or subtype.nil? true end def inline_other_text? return false unless inline_other? return false if inline_other_xml? media_type, = mime_split return true if "text" == media_type.downcase false end def inline_other_xml? return false unless inline_other? media_type, subtype = mime_split normalized_mime_type = "#{media_type}/#{subtype}".downcase if /(?:\+xml|^xml)$/ =~ subtype or %w(text/xml-external-parsed-entity application/xml-external-parsed-entity application/xml-dtd).find {|x| x == normalized_mime_type} return true end false end def inline_other_base64? inline_other? and !inline_other_text? and !inline_other_xml? end def out_of_line? not @src.nil? end def mime_split media_type = subtype = nil if /\A\s*([a-z]+)\/([a-z\+]+)\s*(?:;.*)?\z/i =~ @type.to_s media_type = $1.downcase subtype = $2.downcase end [media_type, subtype] end def need_base64_encode? inline_other_base64? end private def empty_content? out_of_line? or super end end Contributor = Feed::Contributor Id = Feed::Id Link = Feed::Link class Published < RSS::Element include CommonModel include DateConstruct end Rights = Feed::Rights class Source < RSS::Element include CommonModel [ ["author", "*", :children], ["category", "*", :children, "categories"], ["contributor", "*", :children], ["generator", "?"], ["icon", "?"], ["id", "?", nil, :content], ["link", "*", :children], ["logo", "?"], ["rights", "?"], ["subtitle", "?"], ["title", "?"], ["updated", "?", nil, :content], ].each do |tag, occurs, type, *args| type ||= :attribute __send__("install_have_#{type}_element", tag, URI, occurs, tag, *args) end def have_author? !author.to_s.empty? end Author = Feed::Author Category = Feed::Category Contributor = Feed::Contributor Generator = Feed::Generator Icon = Feed::Icon Id = Feed::Id Link = Feed::Link Logo = Feed::Logo Rights = Feed::Rights Subtitle = Feed::Subtitle Title = Feed::Title Updated = Feed::Updated end class Summary < RSS::Element include CommonModel include TextConstruct end Title = Feed::Title Updated = Feed::Updated end end # Defines a top-level Atom Entry element # class Entry < RSS::Element include RootElementMixin include CommonModel include DuplicateLinkChecker [ ["author", "*", :children], ["category", "*", :children, "categories"], ["content", "?"], ["contributor", "*", :children], ["id", nil, nil, :content], ["link", "*", :children], ["published", "?", :child, :content], ["rights", "?"], ["source", "?"], ["summary", "?"], ["title", nil], ["updated", nil, nil, :content], ].each do |tag, occurs, type, *args| type ||= :attribute __send__("install_have_#{type}_element", tag, URI, occurs, tag, *args) end # Creates a new Atom Entry element def initialize(version=nil, encoding=nil, standalone=nil) super("1.0", version, encoding, standalone) @feed_type = "atom" @feed_subtype = "entry" end # Returns the Entry in an array def items [self] end # sets up the +maker+ for constructing Entry elements def setup_maker(maker) maker = maker.maker if maker.respond_to?("maker") super(maker) end # Returns where there are any authors present or there is a source with an author def have_author? authors.any? {|author| !author.to_s.empty?} or (source and source.have_author?) end private def atom_validate(ignore_unknown_element, tags, uri) unless have_author? raise MissingTagError.new("author", tag_name) end validate_duplicate_links(links) end def have_required_elements? super and have_author? end def maker_target(maker) maker.items.new_item end Author = Feed::Entry::Author Category = Feed::Entry::Category Content = Feed::Entry::Content Contributor = Feed::Entry::Contributor Id = Feed::Entry::Id Link = Feed::Entry::Link Published = Feed::Entry::Published Rights = Feed::Entry::Rights Source = Feed::Entry::Source Summary = Feed::Entry::Summary Title = Feed::Entry::Title Updated = Feed::Entry::Updated end end Atom::CommonModel::ELEMENTS.each do |name| BaseListener.install_get_text_element(Atom::URI, name, "#{name}=") end module ListenerMixin private def initial_start_feed(tag_name, prefix, attrs, ns) check_ns(tag_name, prefix, ns, Atom::URI, false) @rss = Atom::Feed.new(@version, @encoding, @standalone) @rss.do_validate = @do_validate @rss.xml_stylesheets = @xml_stylesheets @rss.lang = attrs["xml:lang"] @rss.base = attrs["xml:base"] @last_element = @rss pr = Proc.new do |text, tags| @rss.validate_for_stream(tags) if @do_validate end @proc_stack.push(pr) end def initial_start_entry(tag_name, prefix, attrs, ns) check_ns(tag_name, prefix, ns, Atom::URI, false) @rss = Atom::Entry.new(@version, @encoding, @standalone) @rss.do_validate = @do_validate @rss.xml_stylesheets = @xml_stylesheets @rss.lang = attrs["xml:lang"] @rss.base = attrs["xml:base"] @last_element = @rss pr = Proc.new do |text, tags| @rss.validate_for_stream(tags) if @do_validate end @proc_stack.push(pr) end end end taxonomy.rb000064400000005753147634040010006760 0ustar00require "rss/1.0" require "rss/dublincore" module RSS TAXO_PREFIX = "taxo" TAXO_URI = "http://purl.org/rss/1.0/modules/taxonomy/" RDF.install_ns(TAXO_PREFIX, TAXO_URI) TAXO_ELEMENTS = [] %w(link).each do |name| full_name = "#{TAXO_PREFIX}_#{name}" BaseListener.install_get_text_element(TAXO_URI, name, full_name) TAXO_ELEMENTS << "#{TAXO_PREFIX}_#{name}" end %w(topic topics).each do |name| class_name = Utils.to_class_name(name) BaseListener.install_class_name(TAXO_URI, name, "Taxonomy#{class_name}") TAXO_ELEMENTS << "#{TAXO_PREFIX}_#{name}" end module TaxonomyTopicsModel extend BaseModel def self.append_features(klass) super klass.install_must_call_validator(TAXO_PREFIX, TAXO_URI) %w(topics).each do |name| klass.install_have_child_element(name, TAXO_URI, "?", "#{TAXO_PREFIX}_#{name}") end end class TaxonomyTopics < Element include RSS10 Bag = ::RSS::RDF::Bag class << self def required_prefix TAXO_PREFIX end def required_uri TAXO_URI end end @tag_name = "topics" install_have_child_element("Bag", RDF::URI, nil) install_must_call_validator('rdf', RDF::URI) def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.Bag = args[0] end self.Bag ||= Bag.new end def full_name tag_name_with_prefix(TAXO_PREFIX) end def maker_target(target) target.taxo_topics end def resources if @Bag @Bag.lis.collect do |li| li.resource end else [] end end end end module TaxonomyTopicModel extend BaseModel def self.append_features(klass) super var_name = "#{TAXO_PREFIX}_topic" klass.install_have_children_element("topic", TAXO_URI, "*", var_name) end class TaxonomyTopic < Element include RSS10 include DublinCoreModel include TaxonomyTopicsModel class << self def required_prefix TAXO_PREFIX end def required_uri TAXO_URI end end @tag_name = "topic" install_get_attribute("about", ::RSS::RDF::URI, true, nil, nil, "#{RDF::PREFIX}:about") install_text_element("link", TAXO_URI, "?", "#{TAXO_PREFIX}_link") def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.about = args[0] end end def full_name tag_name_with_prefix(TAXO_PREFIX) end def maker_target(target) target.new_taxo_topic end end end class RDF include TaxonomyTopicModel class Channel include TaxonomyTopicsModel end class Item; include TaxonomyTopicsModel; end end end trackback.rb000064400000015071147634040010007021 0ustar00require 'rss/1.0' require 'rss/2.0' module RSS TRACKBACK_PREFIX = 'trackback' TRACKBACK_URI = 'http://madskills.com/public/xml/rss/module/trackback/' RDF.install_ns(TRACKBACK_PREFIX, TRACKBACK_URI) Rss.install_ns(TRACKBACK_PREFIX, TRACKBACK_URI) module TrackBackUtils private def trackback_validate(ignore_unknown_element, tags, uri) return if tags.nil? if tags.find {|tag| tag == "about"} and !tags.find {|tag| tag == "ping"} raise MissingTagError.new("#{TRACKBACK_PREFIX}:ping", tag_name) end end end module BaseTrackBackModel ELEMENTS = %w(ping about) def append_features(klass) super unless klass.class == Module klass.module_eval {include TrackBackUtils} klass.install_must_call_validator(TRACKBACK_PREFIX, TRACKBACK_URI) %w(ping).each do |name| var_name = "#{TRACKBACK_PREFIX}_#{name}" klass_name = "TrackBack#{Utils.to_class_name(name)}" klass.install_have_child_element(name, TRACKBACK_URI, "?", var_name) klass.module_eval(<<-EOC, __FILE__, __LINE__) remove_method :#{var_name} def #{var_name} @#{var_name} and @#{var_name}.value end remove_method :#{var_name}= def #{var_name}=(value) @#{var_name} = Utils.new_with_value_if_need(#{klass_name}, value) end EOC end [%w(about s)].each do |name, postfix| var_name = "#{TRACKBACK_PREFIX}_#{name}" klass_name = "TrackBack#{Utils.to_class_name(name)}" klass.install_have_children_element(name, TRACKBACK_URI, "*", var_name) klass.module_eval(<<-EOC, __FILE__, __LINE__) remove_method :#{var_name} def #{var_name}(*args) if args.empty? @#{var_name}.first and @#{var_name}.first.value else ret = @#{var_name}.__send__("[]", *args) if ret.is_a?(Array) ret.collect {|x| x.value} else ret.value end end end remove_method :#{var_name}= remove_method :set_#{var_name} def #{var_name}=(*args) if args.size == 1 item = Utils.new_with_value_if_need(#{klass_name}, args[0]) @#{var_name}.push(item) else new_val = args.last if new_val.is_a?(Array) new_val = new_value.collect do |val| Utils.new_with_value_if_need(#{klass_name}, val) end else new_val = Utils.new_with_value_if_need(#{klass_name}, new_val) end @#{var_name}.__send__("[]=", *(args[0..-2] + [new_val])) end end alias set_#{var_name} #{var_name}= EOC end end end end module TrackBackModel10 extend BaseModel extend BaseTrackBackModel class TrackBackPing < Element include RSS10 class << self def required_prefix TRACKBACK_PREFIX end def required_uri TRACKBACK_URI end end @tag_name = "ping" [ ["resource", ::RSS::RDF::URI, true] ].each do |name, uri, required| install_get_attribute(name, uri, required, nil, nil, "#{::RSS::RDF::PREFIX}:#{name}") end alias_method(:value, :resource) alias_method(:value=, :resource=) def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.resource = args[0] end end def full_name tag_name_with_prefix(TRACKBACK_PREFIX) end end class TrackBackAbout < Element include RSS10 class << self def required_prefix TRACKBACK_PREFIX end def required_uri TRACKBACK_URI end end @tag_name = "about" [ ["resource", ::RSS::RDF::URI, true] ].each do |name, uri, required| install_get_attribute(name, uri, required, nil, nil, "#{::RSS::RDF::PREFIX}:#{name}") end alias_method(:value, :resource) alias_method(:value=, :resource=) def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.resource = args[0] end end def full_name tag_name_with_prefix(TRACKBACK_PREFIX) end private def maker_target(abouts) abouts.new_about end def setup_maker_attributes(about) about.resource = self.resource end end end module TrackBackModel20 extend BaseModel extend BaseTrackBackModel class TrackBackPing < Element include RSS09 @tag_name = "ping" content_setup class << self def required_prefix TRACKBACK_PREFIX end def required_uri TRACKBACK_URI end end alias_method(:value, :content) alias_method(:value=, :content=) def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.content = args[0] end end def full_name tag_name_with_prefix(TRACKBACK_PREFIX) end end class TrackBackAbout < Element include RSS09 @tag_name = "about" content_setup class << self def required_prefix TRACKBACK_PREFIX end def required_uri TRACKBACK_URI end end alias_method(:value, :content) alias_method(:value=, :content=) def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.content = args[0] end end def full_name tag_name_with_prefix(TRACKBACK_PREFIX) end end end class RDF class Item; include TrackBackModel10; end end class Rss class Channel class Item; include TrackBackModel20; end end end BaseTrackBackModel::ELEMENTS.each do |name| class_name = Utils.to_class_name(name) BaseListener.install_class_name(TRACKBACK_URI, name, "TrackBack#{class_name}") end BaseTrackBackModel::ELEMENTS.collect! {|name| "#{TRACKBACK_PREFIX}_#{name}"} end xmlparser.rb000064400000003062147634040010007106 0ustar00begin require "xml/parser" rescue LoadError require "xmlparser" end begin require "xml/encoding-ja" rescue LoadError require "xmlencoding-ja" if defined?(Kconv) module XMLEncoding_ja class SJISHandler include Kconv end end end end module XML class Parser unless defined?(Error) Error = ::XMLParserError end end end module RSS class REXMLLikeXMLParser < ::XML::Parser include ::XML::Encoding_ja def listener=(listener) @listener = listener end def startElement(name, attrs) @listener.tag_start(name, attrs) end def endElement(name) @listener.tag_end(name) end def character(data) @listener.text(data) end def xmlDecl(version, encoding, standalone) @listener.xmldecl(version, encoding, standalone == 1) end def processingInstruction(target, content) @listener.instruction(target, content) end end class XMLParserParser < BaseParser class << self def listener XMLParserListener end end private def _parse begin parser = REXMLLikeXMLParser.new parser.listener = @listener parser.parse(@rss) rescue ::XML::Parser::Error => e raise NotWellFormedError.new(parser.line){e.message} end end end class XMLParserListener < BaseListener include ListenerMixin def xmldecl(version, encoding, standalone) super # Encoding is converted to UTF-8 when XMLParser parses XML. @encoding = 'UTF-8' end end end rexmlparser.rb000064400000002141147634040010007432 0ustar00require "rexml/document" require "rexml/streamlistener" /\A(\d+)\.(\d+)(?:\.\d+)+\z/ =~ REXML::Version if ([$1.to_i, $2.to_i] <=> [2, 5]) < 0 raise LoadError, "needs REXML 2.5 or later (#{REXML::Version})" end module RSS class REXMLParser < BaseParser class << self def listener REXMLListener end end private def _parse begin REXML::Document.parse_stream(@rss, @listener) rescue RuntimeError => e raise NotWellFormedError.new{e.message} rescue REXML::ParseException => e context = e.context line = context[0] if context raise NotWellFormedError.new(line){e.message} end end end class REXMLListener < BaseListener include REXML::StreamListener include ListenerMixin class << self def raise_for_undefined_entity? false end end def xmldecl(version, encoding, standalone) super(version, encoding, standalone == "yes") # Encoding is converted to UTF-8 when REXML parse XML. @encoding = 'UTF-8' end alias_method(:cdata, :text) end end slash.rb000064400000002335147634040010006205 0ustar00require 'rss/1.0' module RSS SLASH_PREFIX = 'slash' SLASH_URI = "http://purl.org/rss/1.0/modules/slash/" RDF.install_ns(SLASH_PREFIX, SLASH_URI) module SlashModel extend BaseModel ELEMENT_INFOS = \ [ ["section"], ["department"], ["comments", :positive_integer], ["hit_parade", :csv_integer], ] class << self def append_features(klass) super return if klass.instance_of?(Module) klass.install_must_call_validator(SLASH_PREFIX, SLASH_URI) ELEMENT_INFOS.each do |name, type, *additional_infos| full_name = "#{SLASH_PREFIX}_#{name}" klass.install_text_element(full_name, SLASH_URI, "?", full_name, type, name) end klass.module_eval do alias_method(:slash_hit_parades, :slash_hit_parade) undef_method(:slash_hit_parade) alias_method(:slash_hit_parade, :slash_hit_parade_content) end end end end class RDF class Item; include SlashModel; end end SlashModel::ELEMENT_INFOS.each do |name, type| accessor_base = "#{SLASH_PREFIX}_#{name}" BaseListener.install_get_text_element(SLASH_URI, name, accessor_base) end end content.rb000064400000001403147634040010006540 0ustar00require "rss/rss" module RSS CONTENT_PREFIX = 'content' CONTENT_URI = "http://purl.org/rss/1.0/modules/content/" module ContentModel extend BaseModel ELEMENTS = ["#{CONTENT_PREFIX}_encoded"] def self.append_features(klass) super klass.install_must_call_validator(CONTENT_PREFIX, CONTENT_URI) ELEMENTS.each do |full_name| name = full_name[(CONTENT_PREFIX.size + 1)..-1] klass.install_text_element(name, CONTENT_URI, "?", full_name) end end end prefix_size = CONTENT_PREFIX.size + 1 ContentModel::ELEMENTS.each do |full_name| name = full_name[prefix_size..-1] BaseListener.install_get_text_element(CONTENT_URI, name, full_name) end end require 'rss/content/1.0' require 'rss/content/2.0' rss.rb000064400000103041147634040010005676 0ustar00require "time" class Time class << self unless respond_to?(:w3cdtf) def w3cdtf(date) if /\A\s* (-?\d+)-(\d\d)-(\d\d) (?:T (\d\d):(\d\d)(?::(\d\d))? (\.\d+)? (Z|[+-]\d\d:\d\d)?)? \s*\z/ix =~ date and (($5 and $8) or (!$5 and !$8)) datetime = [$1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i] usec = 0 usec = $7.to_f * 1000000 if $7 zone = $8 if zone off = zone_offset(zone, datetime[0]) datetime = apply_offset(*(datetime + [off])) datetime << usec time = Time.utc(*datetime) time.localtime unless zone_utc?(zone) time else datetime << usec Time.local(*datetime) end else raise ArgumentError.new("invalid date: #{date.inspect}") end end end end unless method_defined?(:w3cdtf) def w3cdtf if usec.zero? fraction_digits = 0 else fraction_digits = Math.log10(usec.to_s.sub(/0*$/, '').to_i).floor + 1 end xmlschema(fraction_digits) end end end require "English" require "rss/utils" require "rss/converter" require "rss/xml-stylesheet" module RSS VERSION = "0.2.7" URI = "http://purl.org/rss/1.0/" DEBUG = false class Error < StandardError; end class OverlappedPrefixError < Error attr_reader :prefix def initialize(prefix) @prefix = prefix end end class InvalidRSSError < Error; end ## # Raised if no matching tag is found. class MissingTagError < InvalidRSSError attr_reader :tag, :parent def initialize(tag, parent) @tag, @parent = tag, parent super("tag <#{tag}> is missing in tag <#{parent}>") end end ## # Raised if there are more occurrences of the tag than expected. class TooMuchTagError < InvalidRSSError attr_reader :tag, :parent def initialize(tag, parent) @tag, @parent = tag, parent super("tag <#{tag}> is too much in tag <#{parent}>") end end ## # Raised if a required attribute is missing. class MissingAttributeError < InvalidRSSError attr_reader :tag, :attribute def initialize(tag, attribute) @tag, @attribute = tag, attribute super("attribute <#{attribute}> is missing in tag <#{tag}>") end end ## # Raised when an unknown tag is found. class UnknownTagError < InvalidRSSError attr_reader :tag, :uri def initialize(tag, uri) @tag, @uri = tag, uri super("tag <#{tag}> is unknown in namespace specified by uri <#{uri}>") end end ## # Raised when an unexpected tag is encountered. class NotExpectedTagError < InvalidRSSError attr_reader :tag, :uri, :parent def initialize(tag, uri, parent) @tag, @uri, @parent = tag, uri, parent super("tag <{#{uri}}#{tag}> is not expected in tag <#{parent}>") end end # For backward compatibility :X NotExceptedTagError = NotExpectedTagError ## # Raised when an incorrect value is used. class NotAvailableValueError < InvalidRSSError attr_reader :tag, :value, :attribute def initialize(tag, value, attribute=nil) @tag, @value, @attribute = tag, value, attribute message = "value <#{value}> of " message << "attribute <#{attribute}> of " if attribute message << "tag <#{tag}> is not available." super(message) end end ## # Raised when an unknown conversion error occurs. class UnknownConversionMethodError < Error attr_reader :to, :from def initialize(to, from) @to = to @from = from super("can't convert to #{to} from #{from}.") end end # for backward compatibility UnknownConvertMethod = UnknownConversionMethodError ## # Raised when a conversion failure occurs. class ConversionError < Error attr_reader :string, :to, :from def initialize(string, to, from) @string = string @to = to @from = from super("can't convert #{@string} to #{to} from #{from}.") end end ## # Raised when a required variable is not set. class NotSetError < Error attr_reader :name, :variables def initialize(name, variables) @name = name @variables = variables super("required variables of #{@name} are not set: #{@variables.join(', ')}") end end ## # Raised when a RSS::Maker attempts to use an unknown maker. class UnsupportedMakerVersionError < Error attr_reader :version def initialize(version) @version = version super("Maker doesn't support version: #{@version}") end end module BaseModel include Utils def install_have_child_element(tag_name, uri, occurs, name=nil, type=nil) name ||= tag_name add_need_initialize_variable(name) install_model(tag_name, uri, occurs, name) writer_type, reader_type = type def_corresponded_attr_writer name, writer_type def_corresponded_attr_reader name, reader_type install_element(name) do |n, elem_name| <<-EOC if @#{n} "\#{@#{n}.to_s(need_convert, indent)}" else '' end EOC end end alias_method(:install_have_attribute_element, :install_have_child_element) def install_have_children_element(tag_name, uri, occurs, name=nil, plural_name=nil) name ||= tag_name plural_name ||= "#{name}s" add_have_children_element(name, plural_name) add_plural_form(name, plural_name) install_model(tag_name, uri, occurs, plural_name, true) def_children_accessor(name, plural_name) install_element(name, "s") do |n, elem_name| <<-EOC rv = [] @#{n}.each do |x| value = "\#{x.to_s(need_convert, indent)}" rv << value if /\\A\\s*\\z/ !~ value end rv.join("\n") EOC end end def install_text_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil) name ||= tag_name disp_name ||= name self::ELEMENTS << name unless self::ELEMENTS.include?(name) add_need_initialize_variable(name) install_model(tag_name, uri, occurs, name) def_corresponded_attr_writer(name, type, disp_name) def_corresponded_attr_reader(name, type || :convert) install_element(name) do |n, elem_name| <<-EOC if respond_to?(:#{n}_content) content = #{n}_content else content = @#{n} end if content rv = "\#{indent}<#{elem_name}>" value = html_escape(content) if need_convert rv << convert(value) else rv << value end rv << "" rv else '' end EOC end end def install_date_element(tag_name, uri, occurs, name=nil, type=nil, disp_name=nil) name ||= tag_name type ||= :w3cdtf disp_name ||= name self::ELEMENTS << name add_need_initialize_variable(name) install_model(tag_name, uri, occurs, name) # accessor convert_attr_reader name date_writer(name, type, disp_name) install_element(name) do |n, elem_name| <<-EOC if @#{n} rv = "\#{indent}<#{elem_name}>" value = html_escape(@#{n}.#{type}) if need_convert rv << convert(value) else rv << value end rv << "" rv else '' end EOC end end private def install_element(name, postfix="") elem_name = name.sub('_', ':') method_name = "#{name}_element#{postfix}" add_to_element_method(method_name) module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{method_name}(need_convert=true, indent='') #{yield(name, elem_name)} end private :#{method_name} EOC end def inherit_convert_attr_reader(*attrs) attrs.each do |attr| attr = attr.id2name if attr.kind_of?(Integer) module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{attr}_without_inherit convert(@#{attr}) end def #{attr} if @#{attr} #{attr}_without_inherit elsif @parent @parent.#{attr} else nil end end EOC end end def uri_convert_attr_reader(*attrs) attrs.each do |attr| attr = attr.id2name if attr.kind_of?(Integer) module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{attr}_without_base convert(@#{attr}) end def #{attr} value = #{attr}_without_base return nil if value.nil? if /\\A[a-z][a-z0-9+.\\-]*:/i =~ value value else "\#{base}\#{value}" end end EOC end end def convert_attr_reader(*attrs) attrs.each do |attr| attr = attr.id2name if attr.kind_of?(Integer) module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{attr} convert(@#{attr}) end EOC end end def yes_clean_other_attr_reader(*attrs) attrs.each do |attr| attr = attr.id2name if attr.kind_of?(Integer) module_eval(<<-EOC, __FILE__, __LINE__ + 1) attr_reader(:#{attr}) def #{attr}? YesCleanOther.parse(@#{attr}) end EOC end end def yes_other_attr_reader(*attrs) attrs.each do |attr| attr = attr.id2name if attr.kind_of?(Integer) module_eval(<<-EOC, __FILE__, __LINE__ + 1) attr_reader(:#{attr}) def #{attr}? Utils::YesOther.parse(@#{attr}) end EOC end end def csv_attr_reader(*attrs) separator = nil if attrs.last.is_a?(Hash) options = attrs.pop separator = options[:separator] end separator ||= ", " attrs.each do |attr| attr = attr.id2name if attr.kind_of?(Integer) module_eval(<<-EOC, __FILE__, __LINE__ + 1) attr_reader(:#{attr}) def #{attr}_content if @#{attr}.nil? @#{attr} else @#{attr}.join(#{separator.dump}) end end EOC end end def date_writer(name, type, disp_name=name) module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{name}=(new_value) if new_value.nil? @#{name} = new_value elsif new_value.kind_of?(Time) @#{name} = new_value.dup else if @do_validate begin @#{name} = Time.__send__('#{type}', new_value) rescue ArgumentError raise NotAvailableValueError.new('#{disp_name}', new_value) end else @#{name} = nil if /\\A\\s*\\z/ !~ new_value.to_s begin unless Date._parse(new_value, false).empty? @#{name} = Time.parse(new_value) end rescue ArgumentError end end end end # Is it need? if @#{name} class << @#{name} undef_method(:to_s) alias_method(:to_s, :#{type}) end end end EOC end def integer_writer(name, disp_name=name) module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{name}=(new_value) if new_value.nil? @#{name} = new_value else if @do_validate begin @#{name} = Integer(new_value) rescue ArgumentError raise NotAvailableValueError.new('#{disp_name}', new_value) end else @#{name} = new_value.to_i end end end EOC end def positive_integer_writer(name, disp_name=name) module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{name}=(new_value) if new_value.nil? @#{name} = new_value else if @do_validate begin tmp = Integer(new_value) raise ArgumentError if tmp <= 0 @#{name} = tmp rescue ArgumentError raise NotAvailableValueError.new('#{disp_name}', new_value) end else @#{name} = new_value.to_i end end end EOC end def boolean_writer(name, disp_name=name) module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{name}=(new_value) if new_value.nil? @#{name} = new_value else if @do_validate and ![true, false, "true", "false"].include?(new_value) raise NotAvailableValueError.new('#{disp_name}', new_value) end if [true, false].include?(new_value) @#{name} = new_value else @#{name} = new_value == "true" end end end EOC end def text_type_writer(name, disp_name=name) module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{name}=(new_value) if @do_validate and !["text", "html", "xhtml", nil].include?(new_value) raise NotAvailableValueError.new('#{disp_name}', new_value) end @#{name} = new_value end EOC end def content_writer(name, disp_name=name) klass_name = "self.class::#{Utils.to_class_name(name)}" module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{name}=(new_value) if new_value.is_a?(#{klass_name}) @#{name} = new_value else @#{name} = #{klass_name}.new @#{name}.content = new_value end end EOC end def yes_clean_other_writer(name, disp_name=name) module_eval(<<-EOC, __FILE__, __LINE__ + 1) def #{name}=(value) value = (value ? "yes" : "no") if [true, false].include?(value) @#{name} = value end EOC end def yes_other_writer(name, disp_name=name) module_eval(<<-EOC, __FILE__, __LINE__ + 1) def #{name}=(new_value) if [true, false].include?(new_value) new_value = new_value ? "yes" : "no" end @#{name} = new_value end EOC end def csv_writer(name, disp_name=name) module_eval(<<-EOC, __FILE__, __LINE__ + 1) def #{name}=(new_value) @#{name} = Utils::CSV.parse(new_value) end EOC end def csv_integer_writer(name, disp_name=name) module_eval(<<-EOC, __FILE__, __LINE__ + 1) def #{name}=(new_value) @#{name} = Utils::CSV.parse(new_value) {|v| Integer(v)} end EOC end def def_children_accessor(accessor_name, plural_name) module_eval(<<-EOC, *get_file_and_line_from_caller(2)) def #{plural_name} @#{accessor_name} end def #{accessor_name}(*args) if args.empty? @#{accessor_name}.first else @#{accessor_name}[*args] end end def #{accessor_name}=(*args) receiver = self.class.name warn("Warning:\#{caller.first.sub(/:in `.*'\z/, '')}: " \ "Don't use `\#{receiver}\##{accessor_name} = XXX'/" \ "`\#{receiver}\#set_#{accessor_name}(XXX)'. " \ "Those APIs are not sense of Ruby. " \ "Use `\#{receiver}\##{plural_name} << XXX' instead of them.") if args.size == 1 @#{accessor_name}.push(args[0]) else @#{accessor_name}.__send__("[]=", *args) end end alias_method(:set_#{accessor_name}, :#{accessor_name}=) EOC end end module SetupMaker def setup_maker(maker) target = maker_target(maker) unless target.nil? setup_maker_attributes(target) setup_maker_element(target) setup_maker_elements(target) end end private def maker_target(maker) nil end def setup_maker_attributes(target) end def setup_maker_element(target) self.class.need_initialize_variables.each do |var| value = __send__(var) next if value.nil? if value.respond_to?("setup_maker") and !not_need_to_call_setup_maker_variables.include?(var) value.setup_maker(target) else setter = "#{var}=" if target.respond_to?(setter) target.__send__(setter, value) end end end end def not_need_to_call_setup_maker_variables [] end def setup_maker_elements(parent) self.class.have_children_elements.each do |name, plural_name| if parent.respond_to?(plural_name) target = parent.__send__(plural_name) __send__(plural_name).each do |elem| elem.setup_maker(target) end end end end end class Element extend BaseModel include Utils extend Utils::InheritedReader include SetupMaker INDENT = " " MUST_CALL_VALIDATORS = {} MODELS = [] GET_ATTRIBUTES = [] HAVE_CHILDREN_ELEMENTS = [] TO_ELEMENT_METHODS = [] NEED_INITIALIZE_VARIABLES = [] PLURAL_FORMS = {} class << self def must_call_validators inherited_hash_reader("MUST_CALL_VALIDATORS") end def models inherited_array_reader("MODELS") end def get_attributes inherited_array_reader("GET_ATTRIBUTES") end def have_children_elements inherited_array_reader("HAVE_CHILDREN_ELEMENTS") end def to_element_methods inherited_array_reader("TO_ELEMENT_METHODS") end def need_initialize_variables inherited_array_reader("NEED_INITIALIZE_VARIABLES") end def plural_forms inherited_hash_reader("PLURAL_FORMS") end def inherited_base ::RSS::Element end def inherited(klass) klass.const_set(:MUST_CALL_VALIDATORS, {}) klass.const_set(:MODELS, []) klass.const_set(:GET_ATTRIBUTES, []) klass.const_set(:HAVE_CHILDREN_ELEMENTS, []) klass.const_set(:TO_ELEMENT_METHODS, []) klass.const_set(:NEED_INITIALIZE_VARIABLES, []) klass.const_set(:PLURAL_FORMS, {}) tag_name = klass.name.split(/::/).last tag_name[0, 1] = tag_name[0, 1].downcase klass.instance_variable_set(:@tag_name, tag_name) klass.instance_variable_set(:@have_content, false) end def install_must_call_validator(prefix, uri) self::MUST_CALL_VALIDATORS[uri] = prefix end def install_model(tag, uri, occurs=nil, getter=nil, plural=false) getter ||= tag if m = self::MODELS.find {|t, u, o, g, p| t == tag and u == uri} m[2] = occurs else self::MODELS << [tag, uri, occurs, getter, plural] end end def install_get_attribute(name, uri, required=true, type=nil, disp_name=nil, element_name=nil) disp_name ||= name element_name ||= name writer_type, reader_type = type def_corresponded_attr_writer name, writer_type, disp_name def_corresponded_attr_reader name, reader_type if type == :boolean and /^is/ =~ name alias_method "#{$POSTMATCH}?", name end self::GET_ATTRIBUTES << [name, uri, required, element_name] add_need_initialize_variable(disp_name) end def def_corresponded_attr_writer(name, type=nil, disp_name=nil) disp_name ||= name case type when :integer integer_writer name, disp_name when :positive_integer positive_integer_writer name, disp_name when :boolean boolean_writer name, disp_name when :w3cdtf, :rfc822, :rfc2822 date_writer name, type, disp_name when :text_type text_type_writer name, disp_name when :content content_writer name, disp_name when :yes_clean_other yes_clean_other_writer name, disp_name when :yes_other yes_other_writer name, disp_name when :csv csv_writer name when :csv_integer csv_integer_writer name else attr_writer name end end def def_corresponded_attr_reader(name, type=nil) case type when :inherit inherit_convert_attr_reader name when :uri uri_convert_attr_reader name when :yes_clean_other yes_clean_other_attr_reader name when :yes_other yes_other_attr_reader name when :csv csv_attr_reader name when :csv_integer csv_attr_reader name, :separator => "," else convert_attr_reader name end end def content_setup(type=nil, disp_name=nil) writer_type, reader_type = type def_corresponded_attr_writer :content, writer_type, disp_name def_corresponded_attr_reader :content, reader_type @have_content = true end def have_content? @have_content end def add_have_children_element(variable_name, plural_name) self::HAVE_CHILDREN_ELEMENTS << [variable_name, plural_name] end def add_to_element_method(method_name) self::TO_ELEMENT_METHODS << method_name end def add_need_initialize_variable(variable_name) self::NEED_INITIALIZE_VARIABLES << variable_name end def add_plural_form(singular, plural) self::PLURAL_FORMS[singular] = plural end def required_prefix nil end def required_uri "" end def need_parent? false end def install_ns(prefix, uri) if self::NSPOOL.has_key?(prefix) raise OverlappedPrefixError.new(prefix) end self::NSPOOL[prefix] = uri end def tag_name @tag_name end end attr_accessor :parent, :do_validate def initialize(do_validate=true, attrs=nil) @parent = nil @converter = nil if attrs.nil? and (do_validate.is_a?(Hash) or do_validate.is_a?(Array)) do_validate, attrs = true, do_validate end @do_validate = do_validate initialize_variables(attrs || {}) end def tag_name self.class.tag_name end def full_name tag_name end def converter=(converter) @converter = converter targets = children.dup self.class.have_children_elements.each do |variable_name, plural_name| targets.concat(__send__(plural_name)) end targets.each do |target| target.converter = converter unless target.nil? end end def convert(value) if @converter @converter.convert(value) else value end end def valid?(ignore_unknown_element=true) validate(ignore_unknown_element) true rescue RSS::Error false end def validate(ignore_unknown_element=true) do_validate = @do_validate @do_validate = true validate_attribute __validate(ignore_unknown_element) ensure @do_validate = do_validate end def validate_for_stream(tags, ignore_unknown_element=true) validate_attribute __validate(ignore_unknown_element, tags, false) end def to_s(need_convert=true, indent='') if self.class.have_content? return "" if !empty_content? and !content_is_set? rv = tag(indent) do |next_indent| if empty_content? "" else xmled_content end end else rv = tag(indent) do |next_indent| self.class.to_element_methods.collect do |method_name| __send__(method_name, false, next_indent) end end end rv = convert(rv) if need_convert rv end def have_xml_content? false end def need_base64_encode? false end def set_next_element(tag_name, next_element) klass = next_element.class prefix = "" prefix << "#{klass.required_prefix}_" if klass.required_prefix key = "#{prefix}#{tag_name.gsub(/-/, '_')}" if self.class.plural_forms.has_key?(key) ary = __send__("#{self.class.plural_forms[key]}") ary << next_element else __send__("#{key}=", next_element) end end protected def have_required_elements? self.class::MODELS.all? do |tag, uri, occurs, getter| if occurs.nil? or occurs == "+" child = __send__(getter) if child.is_a?(Array) children = child children.any? {|c| c.have_required_elements?} else !child.to_s.empty? end else true end end end private def initialize_variables(attrs) normalized_attrs = {} attrs.each do |key, value| normalized_attrs[key.to_s] = value end self.class.need_initialize_variables.each do |variable_name| value = normalized_attrs[variable_name.to_s] if value __send__("#{variable_name}=", value) else instance_variable_set("@#{variable_name}", nil) end end initialize_have_children_elements @content = normalized_attrs["content"] if self.class.have_content? end def initialize_have_children_elements self.class.have_children_elements.each do |variable_name, plural_name| instance_variable_set("@#{variable_name}", []) end end def tag(indent, additional_attrs={}, &block) next_indent = indent + INDENT attrs = collect_attrs return "" if attrs.nil? return "" unless have_required_elements? attrs.update(additional_attrs) start_tag = make_start_tag(indent, next_indent, attrs.dup) if block content = block.call(next_indent) else content = [] end if content.is_a?(String) content = [content] start_tag << ">" end_tag = "" else content = content.reject{|x| x.empty?} if content.empty? return "" if attrs.empty? end_tag = "/>" else start_tag << ">\n" end_tag = "\n#{indent}" end end start_tag + content.join("\n") + end_tag end def make_start_tag(indent, next_indent, attrs) start_tag = ["#{indent}<#{full_name}"] unless attrs.empty? start_tag << attrs.collect do |key, value| %Q[#{h key}="#{h value}"] end.join("\n#{next_indent}") end start_tag.join(" ") end def collect_attrs attrs = {} _attrs.each do |name, required, alias_name| value = __send__(alias_name || name) return nil if required and value.nil? next if value.nil? return nil if attrs.has_key?(name) attrs[name] = value end attrs end def tag_name_with_prefix(prefix) "#{prefix}:#{tag_name}" end # For backward compatibility def calc_indent '' end def children rv = [] self.class.models.each do |name, uri, occurs, getter| value = __send__(getter) next if value.nil? value = [value] unless value.is_a?(Array) value.each do |v| rv << v if v.is_a?(Element) end end rv end def _tags rv = [] self.class.models.each do |name, uri, occurs, getter, plural| value = __send__(getter) next if value.nil? if plural and value.is_a?(Array) rv.concat([[uri, name]] * value.size) else rv << [uri, name] end end rv end def _attrs self.class.get_attributes.collect do |name, uri, required, element_name| [element_name, required, name] end end def __validate(ignore_unknown_element, tags=_tags, recursive=true) if recursive children.compact.each do |child| child.validate end end must_call_validators = self.class.must_call_validators tags = tag_filter(tags.dup) p tags if DEBUG must_call_validators.each do |uri, prefix| _validate(ignore_unknown_element, tags[uri], uri) meth = "#{prefix}_validate" if !prefix.empty? and respond_to?(meth, true) __send__(meth, ignore_unknown_element, tags[uri], uri) end end end def validate_attribute _attrs.each do |a_name, required, alias_name| value = instance_variable_get("@#{alias_name || a_name}") if required and value.nil? raise MissingAttributeError.new(tag_name, a_name) end __send__("#{alias_name || a_name}=", value) end end def _validate(ignore_unknown_element, tags, uri, models=self.class.models) count = 1 do_redo = false not_shift = false tag = nil models = models.find_all {|model| model[1] == uri} element_names = models.collect {|model| model[0]} if tags tags_size = tags.size tags = tags.sort_by {|x| element_names.index(x) || tags_size} end models.each_with_index do |model, i| name, _, occurs, = model if DEBUG p "before" p tags p model end if not_shift not_shift = false elsif tags tag = tags.shift end if DEBUG p "mid" p count end case occurs when '?' if count > 2 raise TooMuchTagError.new(name, tag_name) else if name == tag do_redo = true else not_shift = true end end when '*' if name == tag do_redo = true else not_shift = true end when '+' if name == tag do_redo = true else if count > 1 not_shift = true else raise MissingTagError.new(name, tag_name) end end else if name == tag if models[i+1] and models[i+1][0] != name and tags and tags.first == name raise TooMuchTagError.new(name, tag_name) end else raise MissingTagError.new(name, tag_name) end end if DEBUG p "after" p not_shift p do_redo p tag end if do_redo do_redo = false count += 1 redo else count = 1 end end if !ignore_unknown_element and !tags.nil? and !tags.empty? raise NotExpectedTagError.new(tags.first, uri, tag_name) end end def tag_filter(tags) rv = {} tags.each do |tag| rv[tag[0]] = [] unless rv.has_key?(tag[0]) rv[tag[0]].push(tag[1]) end rv end def empty_content? false end def content_is_set? if have_xml_content? __send__(self.class.xml_getter) else content end end def xmled_content if have_xml_content? __send__(self.class.xml_getter).to_s else _content = content _content = [_content].pack("m").delete("\n") if need_base64_encode? h(_content) end end end module RootElementMixin include XMLStyleSheetMixin attr_reader :output_encoding attr_reader :feed_type, :feed_subtype, :feed_version attr_accessor :version, :encoding, :standalone def initialize(feed_version, version=nil, encoding=nil, standalone=nil) super() @feed_type = nil @feed_subtype = nil @feed_version = feed_version @version = version || '1.0' @encoding = encoding @standalone = standalone @output_encoding = nil end def feed_info [@feed_type, @feed_version, @feed_subtype] end def output_encoding=(enc) @output_encoding = enc self.converter = Converter.new(@output_encoding, @encoding) end def setup_maker(maker) maker.version = version maker.encoding = encoding maker.standalone = standalone xml_stylesheets.each do |xss| xss.setup_maker(maker) end super end def to_feed(type, &block) Maker.make(type) do |maker| setup_maker(maker) block.call(maker) if block end end def to_rss(type, &block) to_feed("rss#{type}", &block) end def to_atom(type, &block) to_feed("atom:#{type}", &block) end def to_xml(type=nil, &block) if type.nil? or same_feed_type?(type) to_s else to_feed(type, &block).to_s end end private def same_feed_type?(type) if /^(atom|rss)?(\d+\.\d+)?(?::(.+))?$/i =~ type feed_type = ($1 || @feed_type).downcase feed_version = $2 || @feed_version feed_subtype = $3 || @feed_subtype [feed_type, feed_version, feed_subtype] == feed_info else false end end def tag(indent, attrs={}, &block) rv = super(indent, ns_declarations.merge(attrs), &block) return rv if rv.empty? "#{xmldecl}#{xml_stylesheet_pi}#{rv}" end def xmldecl rv = %Q[\n" rv end def ns_declarations decls = {} self.class::NSPOOL.collect do |prefix, uri| prefix = ":#{prefix}" unless prefix.empty? decls["xmlns#{prefix}"] = uri end decls end def maker_target(target) target end end end image.rb000064400000011114147634040010006150 0ustar00require 'rss/1.0' require 'rss/dublincore' module RSS IMAGE_PREFIX = 'image' IMAGE_URI = 'http://purl.org/rss/1.0/modules/image/' RDF.install_ns(IMAGE_PREFIX, IMAGE_URI) IMAGE_ELEMENTS = [] %w(item favicon).each do |name| class_name = Utils.to_class_name(name) BaseListener.install_class_name(IMAGE_URI, name, "Image#{class_name}") IMAGE_ELEMENTS << "#{IMAGE_PREFIX}_#{name}" end module ImageModelUtils def validate_one_tag_name(ignore_unknown_element, name, tags) if !ignore_unknown_element invalid = tags.find {|tag| tag != name} raise UnknownTagError.new(invalid, IMAGE_URI) if invalid end raise TooMuchTagError.new(name, tag_name) if tags.size > 1 end end module ImageItemModel include ImageModelUtils extend BaseModel def self.append_features(klass) super klass.install_have_child_element("item", IMAGE_URI, "?", "#{IMAGE_PREFIX}_item") klass.install_must_call_validator(IMAGE_PREFIX, IMAGE_URI) end class ImageItem < Element include RSS10 include DublinCoreModel @tag_name = "item" class << self def required_prefix IMAGE_PREFIX end def required_uri IMAGE_URI end end install_must_call_validator(IMAGE_PREFIX, IMAGE_URI) [ ["about", ::RSS::RDF::URI, true], ["resource", ::RSS::RDF::URI, false], ].each do |name, uri, required| install_get_attribute(name, uri, required, nil, nil, "#{::RSS::RDF::PREFIX}:#{name}") end %w(width height).each do |tag| full_name = "#{IMAGE_PREFIX}_#{tag}" disp_name = "#{IMAGE_PREFIX}:#{tag}" install_text_element(tag, IMAGE_URI, "?", full_name, :integer, disp_name) BaseListener.install_get_text_element(IMAGE_URI, tag, full_name) end alias width= image_width= alias width image_width alias height= image_height= alias height image_height def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.about = args[0] self.resource = args[1] end end def full_name tag_name_with_prefix(IMAGE_PREFIX) end private def maker_target(target) target.image_item end def setup_maker_attributes(item) item.about = self.about item.resource = self.resource end end end module ImageFaviconModel include ImageModelUtils extend BaseModel def self.append_features(klass) super unless klass.class == Module klass.install_have_child_element("favicon", IMAGE_URI, "?", "#{IMAGE_PREFIX}_favicon") klass.install_must_call_validator(IMAGE_PREFIX, IMAGE_URI) end end class ImageFavicon < Element include RSS10 include DublinCoreModel @tag_name = "favicon" class << self def required_prefix IMAGE_PREFIX end def required_uri IMAGE_URI end end [ ["about", ::RSS::RDF::URI, true, ::RSS::RDF::PREFIX], ["size", IMAGE_URI, true, IMAGE_PREFIX], ].each do |name, uri, required, prefix| install_get_attribute(name, uri, required, nil, nil, "#{prefix}:#{name}") end AVAILABLE_SIZES = %w(small medium large) alias_method :set_size, :size= private :set_size def size=(new_value) if @do_validate and !new_value.nil? new_value = new_value.strip unless AVAILABLE_SIZES.include?(new_value) attr_name = "#{IMAGE_PREFIX}:size" raise NotAvailableValueError.new(full_name, new_value, attr_name) end end set_size(new_value) end alias image_size= size= alias image_size size def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.about = args[0] self.size = args[1] end end def full_name tag_name_with_prefix(IMAGE_PREFIX) end private def maker_target(target) target.image_favicon end def setup_maker_attributes(favicon) favicon.about = self.about favicon.size = self.size end end end class RDF class Channel; include ImageFaviconModel; end class Item; include ImageItemModel; end end end itunes.rb000064400000023722147634040010006405 0ustar00require 'rss/2.0' module RSS ITUNES_PREFIX = 'itunes' ITUNES_URI = 'http://www.itunes.com/dtds/podcast-1.0.dtd' Rss.install_ns(ITUNES_PREFIX, ITUNES_URI) module ITunesModelUtils include Utils def def_class_accessor(klass, name, type, *args) normalized_name = name.gsub(/-/, "_") full_name = "#{ITUNES_PREFIX}_#{normalized_name}" klass_name = "ITunes#{Utils.to_class_name(normalized_name)}" case type when :element, :attribute klass::ELEMENTS << full_name def_element_class_accessor(klass, name, full_name, klass_name, *args) when :elements klass::ELEMENTS << full_name def_elements_class_accessor(klass, name, full_name, klass_name, *args) else klass.install_must_call_validator(ITUNES_PREFIX, ITUNES_URI) klass.install_text_element(normalized_name, ITUNES_URI, "?", full_name, type, name) end end def def_element_class_accessor(klass, name, full_name, klass_name, recommended_attribute_name=nil) klass.install_have_child_element(name, ITUNES_PREFIX, "?", full_name) end def def_elements_class_accessor(klass, name, full_name, klass_name, plural_name, recommended_attribute_name=nil) full_plural_name = "#{ITUNES_PREFIX}_#{plural_name}" klass.install_have_children_element(name, ITUNES_PREFIX, "*", full_name, full_plural_name) end end module ITunesBaseModel extend ITunesModelUtils ELEMENTS = [] ELEMENT_INFOS = [["author"], ["block", :yes_other], ["explicit", :yes_clean_other], ["keywords", :csv], ["subtitle"], ["summary"]] end module ITunesChannelModel extend BaseModel extend ITunesModelUtils include ITunesBaseModel ELEMENTS = [] class << self def append_features(klass) super return if klass.instance_of?(Module) ELEMENT_INFOS.each do |name, type, *additional_infos| def_class_accessor(klass, name, type, *additional_infos) end end end ELEMENT_INFOS = [ ["category", :elements, "categories", "text"], ["image", :attribute, "href"], ["owner", :element], ["new-feed-url"], ] + ITunesBaseModel::ELEMENT_INFOS class ITunesCategory < Element include RSS09 @tag_name = "category" class << self def required_prefix ITUNES_PREFIX end def required_uri ITUNES_URI end end [ ["text", "", true] ].each do |name, uri, required| install_get_attribute(name, uri, required) end ITunesCategory = self install_have_children_element("category", ITUNES_URI, "*", "#{ITUNES_PREFIX}_category", "#{ITUNES_PREFIX}_categories") def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.text = args[0] end end def full_name tag_name_with_prefix(ITUNES_PREFIX) end private def maker_target(categories) if text or !itunes_categories.empty? categories.new_category else nil end end def setup_maker_attributes(category) category.text = text if text end def setup_maker_elements(category) super(category) itunes_categories.each do |sub_category| sub_category.setup_maker(category) end end end class ITunesImage < Element include RSS09 @tag_name = "image" class << self def required_prefix ITUNES_PREFIX end def required_uri ITUNES_URI end end [ ["href", "", true] ].each do |name, uri, required| install_get_attribute(name, uri, required) end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.href = args[0] end end def full_name tag_name_with_prefix(ITUNES_PREFIX) end private def maker_target(target) if href target.itunes_image {|image| image} else nil end end def setup_maker_attributes(image) image.href = href end end class ITunesOwner < Element include RSS09 @tag_name = "owner" class << self def required_prefix ITUNES_PREFIX end def required_uri ITUNES_URI end end install_must_call_validator(ITUNES_PREFIX, ITUNES_URI) [ ["name"], ["email"], ].each do |name,| ITunesBaseModel::ELEMENT_INFOS << name install_text_element(name, ITUNES_URI, nil, "#{ITUNES_PREFIX}_#{name}") end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.itunes_name = args[0] self.itunes_email = args[1] end end def full_name tag_name_with_prefix(ITUNES_PREFIX) end private def maker_target(target) target.itunes_owner end def setup_maker_element(owner) super(owner) owner.itunes_name = itunes_name owner.itunes_email = itunes_email end end end module ITunesItemModel extend BaseModel extend ITunesModelUtils include ITunesBaseModel class << self def append_features(klass) super return if klass.instance_of?(Module) ELEMENT_INFOS.each do |name, type| def_class_accessor(klass, name, type) end end end ELEMENT_INFOS = ITunesBaseModel::ELEMENT_INFOS + [["duration", :element, "content"]] class ITunesDuration < Element include RSS09 @tag_name = "duration" class << self def required_prefix ITUNES_PREFIX end def required_uri ITUNES_URI end def parse(duration, do_validate=true) if do_validate and /\A(?: \d?\d:[0-5]\d:[0-5]\d| [0-5]?\d:[0-5]\d )\z/x !~ duration raise ArgumentError, "must be one of HH:MM:SS, H:MM:SS, MM::SS, M:SS: " + duration.inspect end components = duration.split(':') components[3..-1] = nil if components.size > 3 components.unshift("00") until components.size == 3 components.collect do |component| component.to_i end end def construct(hour, minute, second) components = [minute, second] if components.include?(nil) nil else components.unshift(hour) if hour and hour > 0 components.collect do |component| "%02d" % component end.join(":") end end end content_setup alias_method(:value, :content) remove_method(:content=) attr_reader :hour, :minute, :second def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() args = args[0] if args.size == 1 and args[0].is_a?(Array) if args.size == 1 self.content = args[0] elsif args.size > 3 raise ArgumentError, "must be (do_validate, params), (content), " + "(minute, second), ([minute, second]), " + "(hour, minute, second) or ([hour, minute, second]): " + args.inspect else @second, @minute, @hour = args.reverse update_content end end end def content=(value) if value.nil? @content = nil elsif value.is_a?(self.class) self.content = value.content else begin @hour, @minute, @second = self.class.parse(value, @do_validate) rescue ArgumentError raise NotAvailableValueError.new(tag_name, value) end @content = value end end alias_method(:value=, :content=) def hour=(hour) @hour = @do_validate ? Integer(hour) : hour.to_i update_content hour end def minute=(minute) @minute = @do_validate ? Integer(minute) : minute.to_i update_content minute end def second=(second) @second = @do_validate ? Integer(second) : second.to_i update_content second end def full_name tag_name_with_prefix(ITUNES_PREFIX) end private def update_content @content = self.class.construct(hour, minute, second) end def maker_target(target) if @content target.itunes_duration {|duration| duration} else nil end end def setup_maker_element(duration) super(duration) duration.content = @content end end end class Rss class Channel include ITunesChannelModel class Item; include ITunesItemModel; end end end element_infos = ITunesChannelModel::ELEMENT_INFOS + ITunesItemModel::ELEMENT_INFOS element_infos.each do |name, type| case type when :element, :elements, :attribute class_name = Utils.to_class_name(name) BaseListener.install_class_name(ITUNES_URI, name, "ITunes#{class_name}") else accessor_base = "#{ITUNES_PREFIX}_#{name.gsub(/-/, '_')}" BaseListener.install_get_text_element(ITUNES_URI, name, accessor_base) end end end 2.0.rb000064400000004450147634040010005372 0ustar00require "rss/0.9" module RSS class Rss class Channel [ ["generator"], ["ttl", :integer], ].each do |name, type| install_text_element(name, "", "?", name, type) end [ %w(category categories), ].each do |name, plural_name| install_have_children_element(name, "", "*", name, plural_name) end [ ["image", "?"], ["language", "?"], ].each do |name, occurs| install_model(name, "", occurs) end Category = Item::Category class Item [ ["comments", "?"], ["author", "?"], ].each do |name, occurs| install_text_element(name, "", occurs) end [ ["pubDate", '?'], ].each do |name, occurs| install_date_element(name, "", occurs, name, 'rfc822') end alias date pubDate alias date= pubDate= [ ["guid", '?'], ].each do |name, occurs| install_have_child_element(name, "", occurs) end private alias _setup_maker_element setup_maker_element def setup_maker_element(item) _setup_maker_element(item) @guid.setup_maker(item) if @guid end class Guid < Element include RSS09 [ ["isPermaLink", "", false, :boolean] ].each do |name, uri, required, type| install_get_attribute(name, uri, required, type) end content_setup def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.isPermaLink = args[0] self.content = args[1] end end alias_method :_PermaLink?, :PermaLink? private :_PermaLink? def PermaLink? perma = _PermaLink? perma or perma.nil? end private def maker_target(item) item.guid end def setup_maker_attributes(guid) guid.isPermaLink = isPermaLink guid.content = content end end end end end RSS09::ELEMENTS.each do |name| BaseListener.install_get_text_element("", name, name) end end parser.rb000064400000036744147634040010006402 0ustar00require "forwardable" require "open-uri" require "rss/rss" require "rss/xml" module RSS class NotWellFormedError < Error attr_reader :line, :element # Create a new NotWellFormedError for an error at +line+ # in +element+. If a block is given the return value of # the block ends up in the error message. def initialize(line=nil, element=nil) message = "This is not well formed XML" if element or line message << "\nerror occurred" message << " in #{element}" if element message << " at about #{line} line" if line end message << "\n#{yield}" if block_given? super(message) end end class XMLParserNotFound < Error def initialize super("available XML parser was not found in " << "#{AVAILABLE_PARSER_LIBRARIES.inspect}.") end end class NotValidXMLParser < Error def initialize(parser) super("#{parser} is not an available XML parser. " << "Available XML parser" << (AVAILABLE_PARSERS.size > 1 ? "s are " : " is ") << "#{AVAILABLE_PARSERS.inspect}.") end end class NSError < InvalidRSSError attr_reader :tag, :prefix, :uri def initialize(tag, prefix, require_uri) @tag, @prefix, @uri = tag, prefix, require_uri super("prefix <#{prefix}> doesn't associate uri " << "<#{require_uri}> in tag <#{tag}>") end end class Parser extend Forwardable class << self @@default_parser = nil def default_parser @@default_parser || AVAILABLE_PARSERS.first end # Set @@default_parser to new_value if it is one of the # available parsers. Else raise NotValidXMLParser error. def default_parser=(new_value) if AVAILABLE_PARSERS.include?(new_value) @@default_parser = new_value else raise NotValidXMLParser.new(new_value) end end def parse(rss, do_validate=true, ignore_unknown_element=true, parser_class=default_parser) parser = new(rss, parser_class) parser.do_validate = do_validate parser.ignore_unknown_element = ignore_unknown_element parser.parse end end def_delegators(:@parser, :parse, :rss, :ignore_unknown_element, :ignore_unknown_element=, :do_validate, :do_validate=) def initialize(rss, parser_class=self.class.default_parser) @parser = parser_class.new(normalize_rss(rss)) end private # Try to get the XML associated with +rss+. # Return +rss+ if it already looks like XML, or treat it as a URI, # or a file to get the XML, def normalize_rss(rss) return rss if maybe_xml?(rss) uri = to_uri(rss) if uri.respond_to?(:read) uri.read elsif !rss.tainted? and File.readable?(rss) File.open(rss) {|f| f.read} else rss end end # maybe_xml? tests if source is a string that looks like XML. def maybe_xml?(source) source.is_a?(String) and / :xml}] @tag_stack = [[]] @text_stack = [''] @proc_stack = [] @last_element = nil @version = @encoding = @standalone = nil @xml_stylesheets = [] @xml_child_mode = false @xml_element = nil @last_xml_element = nil end # set instance vars for version, encoding, standalone def xmldecl(version, encoding, standalone) @version, @encoding, @standalone = version, encoding, standalone end def instruction(name, content) if name == "xml-stylesheet" params = parse_pi_content(content) if params.has_key?("href") @xml_stylesheets << XMLStyleSheet.new(params) end end end def tag_start(name, attributes) @text_stack.push('') ns = @ns_stack.last.dup attrs = {} attributes.each do |n, v| if /\Axmlns(?:\z|:)/ =~ n ns[$POSTMATCH] = v else attrs[n] = v end end @ns_stack.push(ns) prefix, local = split_name(name) @tag_stack.last.push([_ns(ns, prefix), local]) @tag_stack.push([]) if @xml_child_mode previous = @last_xml_element element_attrs = attributes.dup unless previous ns.each do |ns_prefix, value| next if ns_prefix == "xml" key = ns_prefix.empty? ? "xmlns" : "xmlns:#{ns_prefix}" element_attrs[key] ||= value end end next_element = XML::Element.new(local, prefix.empty? ? nil : prefix, _ns(ns, prefix), element_attrs) previous << next_element if previous @last_xml_element = next_element pr = Proc.new do |text, tags| if previous @last_xml_element = previous else @xml_element = @last_xml_element @last_xml_element = nil end end @proc_stack.push(pr) else if @rss.nil? and respond_to?("initial_start_#{local}", true) __send__("initial_start_#{local}", local, prefix, attrs, ns.dup) elsif respond_to?("start_#{local}", true) __send__("start_#{local}", local, prefix, attrs, ns.dup) else start_else_element(local, prefix, attrs, ns.dup) end end end def tag_end(name) if DEBUG p "end tag #{name}" p @tag_stack end text = @text_stack.pop tags = @tag_stack.pop pr = @proc_stack.pop pr.call(text, tags) unless pr.nil? @ns_stack.pop end def text(data) if @xml_child_mode @last_xml_element << data if @last_xml_element else @text_stack.last << data end end private def _ns(ns, prefix) ns.fetch(prefix, "") end CONTENT_PATTERN = /\s*([^=]+)=(["'])([^\2]+?)\2/ # Extract the first name="value" pair from content. # Works with single quotes according to the constant # CONTENT_PATTERN. Return a Hash. def parse_pi_content(content) params = {} content.scan(CONTENT_PATTERN) do |name, quote, value| params[name] = value end params end def start_else_element(local, prefix, attrs, ns) class_name = self.class.class_name(_ns(ns, prefix), local) current_class = @last_element.class if known_class?(current_class, class_name) next_class = current_class.const_get(class_name) start_have_something_element(local, prefix, attrs, ns, next_class) else if !@do_validate or @ignore_unknown_element @proc_stack.push(setup_next_element_in_unknown_element) else parent = "ROOT ELEMENT???" if current_class.tag_name parent = current_class.tag_name end raise NotExpectedTagError.new(local, _ns(ns, prefix), parent) end end end if Module.method(:const_defined?).arity == -1 def known_class?(target_class, class_name) class_name and (target_class.const_defined?(class_name, false) or target_class.constants.include?(class_name.to_sym)) end else def known_class?(target_class, class_name) class_name and (target_class.const_defined?(class_name) or target_class.constants.include?(class_name)) end end NAMESPLIT = /^(?:([\w:][-\w.]*):)?([\w:][-\w.]*)/ def split_name(name) name =~ NAMESPLIT [$1 || '', $2] end def check_ns(tag_name, prefix, ns, require_uri, ignore_unknown_element=nil) if _ns(ns, prefix) == require_uri true else if ignore_unknown_element.nil? ignore_unknown_element = @ignore_unknown_element end if ignore_unknown_element false elsif @do_validate raise NSError.new(tag_name, prefix, require_uri) else # Force bind required URI with prefix @ns_stack.last[prefix] = require_uri true end end end def start_get_text_element(tag_name, prefix, ns, required_uri) pr = Proc.new do |text, tags| setter = self.class.setter(required_uri, tag_name) if setter and @last_element.respond_to?(setter) if @do_validate getter = self.class.getter(required_uri, tag_name) if @last_element.__send__(getter) raise TooMuchTagError.new(tag_name, @last_element.tag_name) end end @last_element.__send__(setter, text.to_s) else if @do_validate and !@ignore_unknown_element raise NotExpectedTagError.new(tag_name, _ns(ns, prefix), @last_element.tag_name) end end end @proc_stack.push(pr) end def start_have_something_element(tag_name, prefix, attrs, ns, klass) if check_ns(tag_name, prefix, ns, klass.required_uri) attributes = collect_attributes(tag_name, prefix, attrs, ns, klass) @proc_stack.push(setup_next_element(tag_name, klass, attributes)) else @proc_stack.push(setup_next_element_in_unknown_element) end end def collect_attributes(tag_name, prefix, attrs, ns, klass) attributes = {} klass.get_attributes.each do |a_name, a_uri, required, element_name| if a_uri.is_a?(String) or !a_uri.respond_to?(:include?) a_uri = [a_uri] end unless a_uri == [""] for prefix, uri in ns if a_uri.include?(uri) val = attrs["#{prefix}:#{a_name}"] break if val end end end if val.nil? and a_uri.include?("") val = attrs[a_name] end if @do_validate and required and val.nil? unless a_uri.include?("") for prefix, uri in ns if a_uri.include?(uri) a_name = "#{prefix}:#{a_name}" end end end raise MissingAttributeError.new(tag_name, a_name) end attributes[a_name] = val end attributes end def setup_next_element(tag_name, klass, attributes) previous = @last_element next_element = klass.new(@do_validate, attributes) previous.set_next_element(tag_name, next_element) @last_element = next_element @last_element.parent = previous if klass.need_parent? @xml_child_mode = @last_element.have_xml_content? Proc.new do |text, tags| p(@last_element.class) if DEBUG if @xml_child_mode @last_element.content = @xml_element.to_s xml_setter = @last_element.class.xml_setter @last_element.__send__(xml_setter, @xml_element) @xml_element = nil @xml_child_mode = false else if klass.have_content? if @last_element.need_base64_encode? text = text.lstrip.unpack("m").first end @last_element.content = text end end if @do_validate @last_element.validate_for_stream(tags, @ignore_unknown_element) end @last_element = previous end end def setup_next_element_in_unknown_element current_element, @last_element = @last_element, nil Proc.new {@last_element = current_element} end end unless const_defined? :AVAILABLE_PARSER_LIBRARIES AVAILABLE_PARSER_LIBRARIES = [ ["rss/xmlparser", :XMLParserParser], ["rss/xmlscanner", :XMLScanParser], ["rss/rexmlparser", :REXMLParser], ] end AVAILABLE_PARSERS = [] AVAILABLE_PARSER_LIBRARIES.each do |lib, parser| begin require lib AVAILABLE_PARSERS.push(const_get(parser)) rescue LoadError end end if AVAILABLE_PARSERS.empty? raise XMLParserNotFound end end syndication.rb000064400000003400147634040010007411 0ustar00require "rss/1.0" module RSS SY_PREFIX = 'sy' SY_URI = "http://purl.org/rss/1.0/modules/syndication/" RDF.install_ns(SY_PREFIX, SY_URI) module SyndicationModel extend BaseModel ELEMENTS = [] def self.append_features(klass) super klass.install_must_call_validator(SY_PREFIX, SY_URI) klass.module_eval do [ ["updatePeriod"], ["updateFrequency", :positive_integer] ].each do |name, type| install_text_element(name, SY_URI, "?", "#{SY_PREFIX}_#{name}", type, "#{SY_PREFIX}:#{name}") end %w(updateBase).each do |name| install_date_element(name, SY_URI, "?", "#{SY_PREFIX}_#{name}", 'w3cdtf', "#{SY_PREFIX}:#{name}") end end klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) alias_method(:_sy_updatePeriod=, :sy_updatePeriod=) def sy_updatePeriod=(new_value) new_value = new_value.strip validate_sy_updatePeriod(new_value) if @do_validate self._sy_updatePeriod = new_value end EOC end private SY_UPDATEPERIOD_AVAILABLE_VALUES = %w(hourly daily weekly monthly yearly) def validate_sy_updatePeriod(value) unless SY_UPDATEPERIOD_AVAILABLE_VALUES.include?(value) raise NotAvailableValueError.new("updatePeriod", value) end end end class RDF class Channel; include SyndicationModel; end end prefix_size = SY_PREFIX.size + 1 SyndicationModel::ELEMENTS.uniq! SyndicationModel::ELEMENTS.each do |full_name| name = full_name[prefix_size..-1] BaseListener.install_get_text_element(SY_URI, name, full_name) end end dublincore/atom.rb000064400000000373147634040010010161 0ustar00require "rss/atom" module RSS module Atom Feed.install_ns(DC_PREFIX, DC_URI) class Feed include DublinCoreModel class Entry; include DublinCoreModel; end end class Entry include DublinCoreModel end end end dublincore/2.0.rb000064400000000300147634040010007506 0ustar00require "rss/2.0" module RSS Rss.install_ns(DC_PREFIX, DC_URI) class Rss class Channel include DublinCoreModel class Item; include DublinCoreModel; end end end end dublincore/1.0.rb000064400000000426147634040010007516 0ustar00require "rss/1.0" module RSS RDF.install_ns(DC_PREFIX, DC_URI) class RDF class Channel; include DublinCoreModel; end class Image; include DublinCoreModel; end class Item; include DublinCoreModel; end class Textinput; include DublinCoreModel; end end end dublincore.rb000064400000010270147634040010007216 0ustar00require "rss/rss" module RSS DC_PREFIX = 'dc' DC_URI = "http://purl.org/dc/elements/1.1/" module BaseDublinCoreModel def append_features(klass) super return if klass.instance_of?(Module) DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name| plural = plural_name || "#{name}s" full_name = "#{DC_PREFIX}_#{name}" full_plural_name = "#{DC_PREFIX}_#{plural}" klass_name = "DublinCore#{Utils.to_class_name(name)}" klass.install_must_call_validator(DC_PREFIX, DC_URI) klass.install_have_children_element(name, DC_URI, "*", full_name, full_plural_name) klass.module_eval(<<-EOC, *get_file_and_line_from_caller(0)) remove_method :#{full_name} remove_method :#{full_name}= remove_method :set_#{full_name} def #{full_name} @#{full_name}.first and @#{full_name}.first.value end def #{full_name}=(new_value) @#{full_name}[0] = Utils.new_with_value_if_need(#{klass_name}, new_value) end alias set_#{full_name} #{full_name}= EOC end klass.module_eval(<<-EOC, *get_file_and_line_from_caller(0)) if method_defined?(:date) alias date_without_#{DC_PREFIX}_date= date= def date=(value) self.date_without_#{DC_PREFIX}_date = value self.#{DC_PREFIX}_date = value end else alias date #{DC_PREFIX}_date alias date= #{DC_PREFIX}_date= end # For backward compatibility alias #{DC_PREFIX}_rightses #{DC_PREFIX}_rights_list EOC end end module DublinCoreModel extend BaseModel extend BaseDublinCoreModel TEXT_ELEMENTS = { "title" => nil, "description" => nil, "creator" => nil, "subject" => nil, "publisher" => nil, "contributor" => nil, "type" => nil, "format" => nil, "identifier" => nil, "source" => nil, "language" => nil, "relation" => nil, "coverage" => nil, "rights" => "rights_list" } DATE_ELEMENTS = { "date" => "w3cdtf", } ELEMENT_NAME_INFOS = DublinCoreModel::TEXT_ELEMENTS.to_a DublinCoreModel::DATE_ELEMENTS.each do |name, | ELEMENT_NAME_INFOS << [name, nil] end ELEMENTS = TEXT_ELEMENTS.keys + DATE_ELEMENTS.keys ELEMENTS.each do |name, plural_name| module_eval(<<-EOC, *get_file_and_line_from_caller(0)) class DublinCore#{Utils.to_class_name(name)} < Element include RSS10 content_setup class << self def required_prefix DC_PREFIX end def required_uri DC_URI end end @tag_name = #{name.dump} alias_method(:value, :content) alias_method(:value=, :content=) def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.content = args[0] end end def full_name tag_name_with_prefix(DC_PREFIX) end def maker_target(target) target.new_#{name} end def setup_maker_attributes(#{name}) #{name}.content = content end end EOC end DATE_ELEMENTS.each do |name, type| tag_name = "#{DC_PREFIX}:#{name}" module_eval(<<-EOC, *get_file_and_line_from_caller(0)) class DublinCore#{Utils.to_class_name(name)} < Element remove_method(:content=) remove_method(:value=) date_writer("content", #{type.dump}, #{tag_name.dump}) alias_method(:value=, :content=) end EOC end end # For backward compatibility DublincoreModel = DublinCoreModel DublinCoreModel::ELEMENTS.each do |name| class_name = Utils.to_class_name(name) BaseListener.install_class_name(DC_URI, name, "DublinCore#{class_name}") end DublinCoreModel::ELEMENTS.collect! {|name| "#{DC_PREFIX}_#{name}"} end require 'rss/dublincore/1.0' require 'rss/dublincore/2.0' require 'rss/dublincore/atom' content/2.0.rb000064400000000251147634040010007037 0ustar00require "rss/2.0" module RSS Rss.install_ns(CONTENT_PREFIX, CONTENT_URI) class Rss class Channel class Item; include ContentModel; end end end end content/1.0.rb000064400000000215147634040010007036 0ustar00require 'rss/1.0' module RSS RDF.install_ns(CONTENT_PREFIX, CONTENT_URI) class RDF class Item; include ContentModel; end end end maker/atom.rb000064400000010777147634040010007143 0ustar00require "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_method(:uri=, :content=) end set = setup_values(logo) class << logo remove_method(: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 maker/taxonomy.rb000064400000006154147634040010010053 0ustar00require 'rss/taxonomy' require 'rss/maker/1.0' require 'rss/maker/dublincore' module RSS module Maker module TaxonomyTopicsModel def self.append_features(klass) super klass.def_classed_element("#{RSS::TAXO_PREFIX}_topics", "TaxonomyTopics") end def self.install_taxo_topics(klass) klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) class TaxonomyTopics < TaxonomyTopicsBase def to_feed(feed, current) if current.respond_to?(:taxo_topics) topics = current.class::TaxonomyTopics.new bag = topics.Bag @resources.each do |resource| bag.lis << RDF::Bag::Li.new(resource) end current.taxo_topics = topics end end end EOC end class TaxonomyTopicsBase < Base attr_reader :resources def_array_element("resource") remove_method :new_resource end end module TaxonomyTopicModel def self.append_features(klass) super class_name = "TaxonomyTopics" klass.def_classed_elements("#{TAXO_PREFIX}_topic", "value", class_name) end def self.install_taxo_topic(klass) klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) class TaxonomyTopics < TaxonomyTopicsBase class TaxonomyTopic < TaxonomyTopicBase DublinCoreModel.install_dublin_core(self) TaxonomyTopicsModel.install_taxo_topics(self) def to_feed(feed, current) if current.respond_to?(:taxo_topics) topic = current.class::TaxonomyTopic.new(value) topic.taxo_link = value taxo_topics.to_feed(feed, topic) if taxo_topics current.taxo_topics << topic setup_other_elements(feed, topic) end end end end EOC end class TaxonomyTopicsBase < Base def_array_element("topic", nil, "TaxonomyTopic") alias_method(:new_taxo_topic, :new_topic) # For backward compatibility class TaxonomyTopicBase < Base include DublinCoreModel include TaxonomyTopicsModel attr_accessor :value add_need_initialize_variable("value") alias_method(:taxo_link, :value) alias_method(:taxo_link=, :value=) def have_required_values? @value end end end end class RSSBase include TaxonomyTopicModel end class ChannelBase include TaxonomyTopicsModel end class ItemsBase class ItemBase include TaxonomyTopicsModel end end makers.each do |maker| maker.module_eval(<<-EOC, __FILE__, __LINE__ + 1) TaxonomyTopicModel.install_taxo_topic(self) class Channel TaxonomyTopicsModel.install_taxo_topics(self) end class Items class Item TaxonomyTopicsModel.install_taxo_topics(self) end end EOC end end end maker/trackback.rb000064400000003106147634040010010114 0ustar00require 'rss/trackback' require 'rss/maker/1.0' require 'rss/maker/2.0' module RSS module Maker module TrackBackModel def self.append_features(klass) super klass.def_other_element("#{RSS::TRACKBACK_PREFIX}_ping") klass.def_classed_elements("#{RSS::TRACKBACK_PREFIX}_about", "value", "TrackBackAbouts") end class TrackBackAboutsBase < Base def_array_element("about", nil, "TrackBackAbout") class TrackBackAboutBase < Base attr_accessor :value add_need_initialize_variable("value") alias_method(:resource, :value) alias_method(:resource=, :value=) alias_method(:content, :value) alias_method(:content=, :value=) def have_required_values? @value 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 ItemsBase class ItemBase; include TrackBackModel; end 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 EOC end end end maker/slash.rb000064400000001337147634040010007305 0ustar00require 'rss/slash' require 'rss/maker/1.0' module RSS module Maker module SlashModel def self.append_features(klass) super ::RSS::SlashModel::ELEMENT_INFOS.each do |name, type| full_name = "#{RSS::SLASH_PREFIX}_#{name}" case type when :csv_integer klass.def_csv_element(full_name, :integer) else klass.def_other_element(full_name) end end klass.module_eval do alias_method(:slash_hit_parades, :slash_hit_parade) alias_method(:slash_hit_parades=, :slash_hit_parade=) end end end class ItemsBase class ItemBase include SlashModel end end end end maker/content.rb000064400000000600147634040010007635 0ustar00require 'rss/content' require 'rss/maker/1.0' require 'rss/maker/2.0' module RSS module Maker module ContentModel def self.append_features(klass) super ::RSS::ContentModel::ELEMENTS.each do |name| klass.def_other_element(name) end end end class ItemsBase class ItemBase; include ContentModel; end end end end maker/image.rb000064400000005560147634040010007257 0ustar00require 'rss/image' require 'rss/maker/1.0' require 'rss/maker/dublincore' module RSS module Maker module ImageItemModel def self.append_features(klass) super name = "#{RSS::IMAGE_PREFIX}_item" klass.def_classed_element(name) 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 < Base include Maker::DublinCoreModel attr_accessor :about, :resource, :image_width, :image_height add_need_initialize_variable("about") add_need_initialize_variable("resource") add_need_initialize_variable("image_width") add_need_initialize_variable("image_height") alias width= image_width= alias width image_width alias height= image_height= alias height image_height 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 module ImageFaviconModel def self.append_features(klass) super name = "#{RSS::IMAGE_PREFIX}_favicon" klass.def_classed_element(name) 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 < Base include Maker::DublinCoreModel attr_accessor :about, :image_size add_need_initialize_variable("about") add_need_initialize_variable("image_size") alias size image_size alias size= image_size= 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 class ChannelBase; include Maker::ImageFaviconModel; end class ItemsBase class ItemBase; include Maker::ImageItemModel; end end makers.each do |maker| maker.module_eval(<<-EOC, __FILE__, __LINE__ + 1) class Channel ImageFaviconModel.install_image_favicon(self) end class Items class Item ImageItemModel.install_image_item(self) end end EOC end end end maker/itunes.rb000064400000016411147634040010007501 0ustar00require 'rss/itunes' require 'rss/maker/2.0' module RSS module Maker module ITunesBaseModel def def_class_accessor(klass, name, type, *args) name = name.gsub(/-/, "_").gsub(/^itunes_/, '') full_name = "#{RSS::ITUNES_PREFIX}_#{name}" case type when nil klass.def_other_element(full_name) when :yes_other def_yes_other_accessor(klass, full_name) when :yes_clean_other def_yes_clean_other_accessor(klass, full_name) when :csv def_csv_accessor(klass, full_name) when :element, :attribute recommended_attribute_name, = *args klass_name = "ITunes#{Utils.to_class_name(name)}" klass.def_classed_element(full_name, klass_name, recommended_attribute_name) when :elements plural_name, recommended_attribute_name = args plural_name ||= "#{name}s" full_plural_name = "#{RSS::ITUNES_PREFIX}_#{plural_name}" klass_name = "ITunes#{Utils.to_class_name(name)}" plural_klass_name = "ITunes#{Utils.to_class_name(plural_name)}" def_elements_class_accessor(klass, name, full_name, full_plural_name, klass_name, plural_klass_name, recommended_attribute_name) end end def def_yes_other_accessor(klass, full_name) klass.def_other_element(full_name) klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) def #{full_name}? Utils::YesOther.parse(@#{full_name}) end EOC end def def_yes_clean_other_accessor(klass, full_name) klass.def_other_element(full_name) klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) def #{full_name}? Utils::YesCleanOther.parse(#{full_name}) end EOC end def def_csv_accessor(klass, full_name) klass.def_csv_element(full_name) end def def_elements_class_accessor(klass, name, full_name, full_plural_name, klass_name, plural_klass_name, recommended_attribute_name=nil) if recommended_attribute_name klass.def_classed_elements(full_name, recommended_attribute_name, plural_klass_name, full_plural_name) else klass.def_classed_element(full_plural_name, plural_klass_name) end klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) def new_#{full_name}(text=nil) #{full_name} = @#{full_plural_name}.new_#{name} #{full_name}.text = text if block_given? yield #{full_name} else #{full_name} end end EOC end end module ITunesChannelModel extend ITunesBaseModel class << self def append_features(klass) super ::RSS::ITunesChannelModel::ELEMENT_INFOS.each do |name, type, *args| def_class_accessor(klass, name, type, *args) end end end class ITunesCategoriesBase < Base def_array_element("category", "itunes_categories", "ITunesCategory") class ITunesCategoryBase < Base attr_accessor :text add_need_initialize_variable("text") def_array_element("category", "itunes_categories", "ITunesCategory") def have_required_values? text end alias_method :to_feed_for_categories, :to_feed def to_feed(feed, current) if text and current.respond_to?(:itunes_category) new_item = current.class::ITunesCategory.new(text) to_feed_for_categories(feed, new_item) current.itunes_categories << new_item end end end end class ITunesImageBase < Base add_need_initialize_variable("href") attr_accessor("href") def to_feed(feed, current) if @href and current.respond_to?(:itunes_image) current.itunes_image ||= current.class::ITunesImage.new current.itunes_image.href = @href end end end class ITunesOwnerBase < Base %w(itunes_name itunes_email).each do |name| add_need_initialize_variable(name) attr_accessor(name) end def to_feed(feed, current) if current.respond_to?(:itunes_owner=) _not_set_required_variables = not_set_required_variables if (required_variable_names - _not_set_required_variables).empty? return end unless have_required_values? raise NotSetError.new("maker.channel.itunes_owner", _not_set_required_variables) end current.itunes_owner ||= current.class::ITunesOwner.new current.itunes_owner.itunes_name = @itunes_name current.itunes_owner.itunes_email = @itunes_email end end private def required_variable_names %w(itunes_name itunes_email) end end end module ITunesItemModel extend ITunesBaseModel class << self def append_features(klass) super ::RSS::ITunesItemModel::ELEMENT_INFOS.each do |name, type, *args| def_class_accessor(klass, name, type, *args) end end end class ITunesDurationBase < Base attr_reader :content add_need_initialize_variable("content") %w(hour minute second).each do |name| attr_reader(name) add_need_initialize_variable(name, 0) end def content=(content) if content.nil? @hour, @minute, @second, @content = nil else @hour, @minute, @second = ::RSS::ITunesItemModel::ITunesDuration.parse(content) @content = content end end def hour=(hour) @hour = Integer(hour) update_content end def minute=(minute) @minute = Integer(minute) update_content end def second=(second) @second = Integer(second) update_content end def to_feed(feed, current) if @content and current.respond_to?(:itunes_duration=) current.itunes_duration ||= current.class::ITunesDuration.new current.itunes_duration.content = @content end end private def update_content components = [@hour, @minute, @second] @content = ::RSS::ITunesItemModel::ITunesDuration.construct(*components) end end end class ChannelBase include Maker::ITunesChannelModel class ITunesCategories < ITunesCategoriesBase class ITunesCategory < ITunesCategoryBase ITunesCategory = self end end class ITunesImage < ITunesImageBase; end class ITunesOwner < ITunesOwnerBase; end end class ItemsBase class ItemBase include Maker::ITunesItemModel class ITunesDuration < ITunesDurationBase; end end end end end maker/2.0.rb000064400000013275147634040010006476 0ustar00require "rss/2.0" require "rss/maker/0.9" module RSS module Maker class RSS20 < RSS09 def initialize(feed_version="2.0") super end class Channel < RSS09::Channel private def required_variable_names %w(link) end class SkipDays < RSS09::Channel::SkipDays class Day < RSS09::Channel::SkipDays::Day end end class SkipHours < RSS09::Channel::SkipHours class Hour < RSS09::Channel::SkipHours::Hour end end class Cloud < RSS09::Channel::Cloud def to_feed(rss, channel) cloud = Rss::Channel::Cloud.new set = setup_values(cloud) if set channel.cloud = cloud set_parent(cloud, channel) setup_other_elements(rss, cloud) end end private def required_variable_names %w(domain port path registerProcedure protocol) end end class Categories < RSS09::Channel::Categories def to_feed(rss, channel) @categories.each do |category| category.to_feed(rss, channel) end end class Category < RSS09::Channel::Categories::Category def to_feed(rss, channel) category = Rss::Channel::Category.new set = setup_values(category) if set channel.categories << category set_parent(category, channel) setup_other_elements(rss, category) end end 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 class Item < RSS09::Items::Item private def required_variable_names [] end def not_set_required_variables vars = super if !title {|t| t.have_required_values?} and !description {|d| d.have_required_values?} vars << "title or description" end vars end def variables super + ["pubDate"] end class Guid < RSS09::Items::Item::Guid def to_feed(rss, item) guid = Rss::Channel::Item::Guid.new set = setup_values(guid) if set item.guid = guid set_parent(guid, item) setup_other_elements(rss, guid) end end private def required_variable_names %w(content) end end class Enclosure < RSS09::Items::Item::Enclosure def to_feed(rss, item) enclosure = Rss::Channel::Item::Enclosure.new set = setup_values(enclosure) if set item.enclosure = enclosure set_parent(enclosure, item) setup_other_elements(rss, enclosure) end end private def required_variable_names %w(url length type) end end class Source < RSS09::Items::Item::Source def to_feed(rss, item) source = Rss::Channel::Item::Source.new set = setup_values(source) if set item.source = source set_parent(source, item) setup_other_elements(rss, source) end end 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_feed(rss, item) @categories.each do |category| category.to_feed(rss, item) end end class Category < RSS09::Items::Item::Categories::Category 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 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("2.0", "2.0", RSS20) add_maker("rss2.0", "2.0", RSS20) end end maker/syndication.rb000064400000000535147634040010010516 0ustar00require 'rss/syndication' require 'rss/maker/1.0' module RSS module Maker module SyndicationModel def self.append_features(klass) super ::RSS::SyndicationModel::ELEMENTS.each do |name| klass.def_other_element(name) end end end class ChannelBase; include SyndicationModel; end end end maker/base.rb000064400000060067147634040010007112 0ustar00require '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) unless value.nil? 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 keep = { :date => date, :dc_dates => dc_dates.to_a.dup, } _date = _parse_date_if_needed(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 ensure date = keep[:date] dc_dates.replace(keep[:dc_dates]) end def _parse_date_if_needed(date_value) date_value = Time.parse(date_value) if date_value.is_a?(String) date_value end end module SetupDefaultLanguage private def _set_default_values keep = { :dc_languages => dc_languages.to_a.dup, } _language = language if _language and !dc_languages.any? {|dc_language| dc_language.value == _language} dc_language = self.class::DublinCoreLanguages::DublinCoreLanguage.new(self) dc_language.value = _language.dup dc_languages.unshift(dc_language) end super ensure dc_languages.replace(keep[:dc_languages]) end end class RSSBase < Base class << self def make(*args, &block) new(*args).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__ + 1) 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 ttl).each do |element| attr_accessor element add_need_initialize_variable(element) end %w(date lastBuildDate).each do |date_element| attr_reader date_element add_need_initialize_variable(date_element) end def date=(_date) @date = _parse_date_if_needed(_date) end def lastBuildDate=(_date) @lastBuildDate = _parse_date_if_needed(_date) 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(comments id published).each do |element| attr_accessor element add_need_initialize_variable(element) end %w(date).each do |date_element| attr_reader date_element add_need_initialize_variable(date_element) end def date=(_date) @date = _parse_date_if_needed(_date) 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 def permanent_link? isPermaLink end def permanent_link=(bool) self.isPermaLink = bool 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 include SetupDefaultDate %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).each do |element| attr_accessor element add_need_initialize_variable(element) end alias_method(:url, :link) alias_method(:url=, :link=) %w(date).each do |date_element| attr_reader date_element add_need_initialize_variable(date_element) end def date=(_date) @date = _parse_date_if_needed(_date) 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 < 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 maker/entry.rb000064400000011323147634040010007330 0ustar00require "rss/maker/atom" require "rss/maker/feed" module RSS module Maker module Atom class Entry < RSSBase def initialize(feed_version="1.0") super @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 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 {|t| t.have_required_values?} vars end end def _set_default_values 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? @maker.channel.rights {|r| @rights = r} end unless keep[:title].variable_is_set? @maker.channel.title {|t| @title = t} end self.updated ||= @maker.channel.updated super 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[: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", "1.0", Atom::Entry) add_maker("atom1.0:entry", "1.0", Atom::Entry) end end maker/dublincore.rb000064400000007361147634040010010324 0ustar00require 'rss/dublincore' require 'rss/maker/1.0' module RSS module Maker module DublinCoreModel def self.append_features(klass) super ::RSS::DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name| plural_name ||= "#{name}s" full_name = "#{RSS::DC_PREFIX}_#{name}" full_plural_name = "#{RSS::DC_PREFIX}_#{plural_name}" plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}" klass.def_classed_elements(full_name, "value", plural_klass_name, full_plural_name, name) klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) def new_#{full_name}(value=nil) _#{full_name} = #{full_plural_name}.new_#{name} _#{full_name}.value = value if block_given? yield _#{full_name} else _#{full_name} end end EOC end klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) # For backward compatibility alias #{DC_PREFIX}_rightses #{DC_PREFIX}_rights_list EOC end ::RSS::DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name| plural_name ||= "#{name}s" full_name ||= "#{DC_PREFIX}_#{name}" full_plural_name ||= "#{DC_PREFIX}_#{plural_name}" klass_name = Utils.to_class_name(name) full_klass_name = "DublinCore#{klass_name}" plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}" module_eval(<<-EOC, __FILE__, __LINE__ + 1) class #{plural_klass_name}Base < Base def_array_element(#{name.dump}, #{full_plural_name.dump}, #{full_klass_name.dump}) class #{full_klass_name}Base < Base attr_accessor :value add_need_initialize_variable("value") alias_method(:content, :value) alias_method(:content=, :value=) def have_required_values? @value end def to_feed(feed, current) if value and current.respond_to?(:#{full_name}) new_item = current.class::#{full_klass_name}.new(value) current.#{full_plural_name} << new_item end end end #{klass_name}Base = #{full_klass_name}Base end EOC end def self.install_dublin_core(klass) ::RSS::DublinCoreModel::ELEMENT_NAME_INFOS.each do |name, plural_name| plural_name ||= "#{name}s" klass_name = Utils.to_class_name(name) full_klass_name = "DublinCore#{klass_name}" plural_klass_name = "DublinCore#{Utils.to_class_name(plural_name)}" klass.module_eval(<<-EOC, __FILE__, __LINE__ + 1) class #{plural_klass_name} < #{plural_klass_name}Base class #{full_klass_name} < #{full_klass_name}Base end #{klass_name} = #{full_klass_name} end EOC end end end class ChannelBase include DublinCoreModel end class ImageBase; include DublinCoreModel; end class ItemsBase class ItemBase include DublinCoreModel end end class TextinputBase; include DublinCoreModel; end makers.each do |maker| maker.module_eval(<<-EOC, __FILE__, __LINE__ + 1) class Channel 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 Textinput DublinCoreModel.install_dublin_core(self) end EOC end end end maker/feed.rb000064400000030751147634040010007100 0ustar00require "rss/maker/atom" module RSS module Maker module Atom class Feed < RSSBase def initialize(feed_version="1.0") super @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 include SetupDefaultLanguage 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 {|t| t.have_required_values?} vars end def _set_default_values(&block) keep = { :id => id, } self.id ||= about super(&block) ensure self.id = keep[:id] 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_method(:url=, :content=) end set = setup_values(logo) class << logo remove_method(: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) entry.dc_dates.clear 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 {|t| t.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 {|t| t.have_required_values?} vars end def _set_default_values(&block) keep = { :id => id, } self.id ||= link super(&block) ensure self.id = keep[:id] 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_method(:url=, :content=) end set = setup_values(icon) class << icon remove_method(: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", "1.0", Atom::Feed) add_maker("atom:feed", "1.0", Atom::Feed) add_maker("atom1.0", "1.0", Atom::Feed) add_maker("atom1.0:feed", "1.0", Atom::Feed) end end maker/1.0.rb000064400000024016147634040010006470 0ustar00require "rss/1.0" require "rss/maker/base" module RSS module Maker class RSS10 < RSSBase def initialize(feed_version="1.0") super @feed_type = "rss" end private def make_feed RDF.new(@version, @encoding, @standalone) end def setup_elements(rss) setup_channel(rss) setup_image(rss) setup_items(rss) setup_textinput(rss) end class Channel < ChannelBase include SetupDefaultLanguage 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) 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, channel) else raise NotSetError.new("maker.channel", _not_set_required_variables) end end end private def setup_items(rss) items = RDF::Channel::Items.new seq = items.Seq 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? image = RDF::Channel::Image.new(@maker.image.url) rss.channel.image = image set_parent(image, rss.channel) end end def setup_textinput(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 link) end def not_set_required_variables vars = super vars << "description" unless description {|d| d.have_required_values?} vars << "title" unless title {|t| t.have_required_values?} vars 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 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_feed(rss) if @url image = RDF::Image.new(@url) set = setup_values(image) if set rss.image = image set_parent(image, rss) setup_other_elements(rss, image) end end end def 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_feed(rss) if rss.channel normalize.each do |item| item.to_feed(rss) end setup_other_elements(rss, rss.items) end end class Item < ItemBase 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 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 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 {|t| t.have_required_values?} vars end end class Guid < GuidBase def to_feed(*args) end end class Enclosure < EnclosureBase def to_feed(*args) end end class Source < SourceBase 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_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_feed(rss) if @link textinput = RDF::Textinput.new(@link) set = setup_values(textinput) if set rss.textinput = textinput set_parent(textinput, rss) setup_other_elements(rss, textinput) end end end def 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("1.0", "1.0", RSS10) add_maker("rss1.0", "1.0", RSS10) end end maker/0.9.rb000064400000027453147634040010006510 0ustar00require "rss/0.9" require "rss/maker/base" module RSS module Maker class RSS09 < RSSBase def initialize(feed_version) super @feed_type = "rss" end private def make_feed Rss.new(@feed_version, @version, @encoding, @standalone) end def setup_elements(rss) setup_channel(rss) end class Channel < ChannelBase def to_feed(rss) channel = Rss::Channel.new setup_values(channel) _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, channel) rss else raise NotSetError.new("maker.channel", _not_set_required_variables) end end private def setup_items(rss) @maker.items.to_feed(rss) end def setup_image(rss) @maker.image.to_feed(rss) end def setup_textinput(rss) @maker.textinput.to_feed(rss) end def variables super + ["pubDate"] end def required_variable_names %w(link language) end def not_set_required_variables vars = super vars << "description" unless description {|d| d.have_required_values?} vars << "title" unless title {|t| t.have_required_values?} vars end class SkipDays < SkipDaysBase 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_feed(rss, skipDays.days) end end end class Day < DayBase def to_feed(rss, days) day = Rss::Channel::SkipDays::Day.new set = setup_values(day) if set days << day set_parent(day, days) setup_other_elements(rss, day) end end private def required_variable_names %w(content) end end end class SkipHours < SkipHoursBase 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_feed(rss, skipHours.hours) end end end class Hour < HourBase def to_feed(rss, hours) hour = Rss::Channel::SkipHours::Hour.new set = setup_values(hour) if set hours << hour set_parent(hour, hours) setup_other_elements(rss, hour) end end private def required_variable_names %w(content) end end end class Cloud < CloudBase def to_feed(*args) end end class Categories < CategoriesBase 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_feed(rss) image = Rss::Channel::Image.new set = setup_values(image) if set image.link = link rss.channel.image = image set_parent(image, rss.channel) setup_other_elements(rss, image) elsif required_element? raise NotSetError.new("maker.image", not_set_required_variables) end end private def required_variable_names %w(url title link) end def required_element? true end end class Items < ItemsBase def to_feed(rss) if rss.channel normalize.each do |item| item.to_feed(rss) end setup_other_elements(rss, rss.items) end end class Item < ItemBase def to_feed(rss) item = Rss::Channel::Item.new setup_values(item) _not_set_required_variables = not_set_required_variables if _not_set_required_variables.empty? rss.items << item 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 required_variable_names [] end def not_set_required_variables vars = super if @maker.feed_version == "0.91" vars << "title" unless title {|t| t.have_required_values?} vars << "link" unless link {|l| l.have_required_values?} end vars end class Guid < GuidBase def to_feed(*args) end end class Enclosure < EnclosureBase def to_feed(*args) end end class Source < SourceBase 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_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_feed(rss) textInput = Rss::Channel::TextInput.new set = setup_values(textInput) if set rss.channel.textInput = textInput set_parent(textInput, rss.channel) setup_other_elements(rss, textInput) end end private def required_variable_names %w(title description name link) end end end class RSS091 < RSS09 def initialize(feed_version="0.91") super end class Channel < RSS09::Channel end class Items < RSS09::Items class Item < RSS09::Items::Item end end class Image < RSS09::Image end class Textinput < RSS09::Textinput end end class RSS092 < RSS09 def initialize(feed_version="0.92") super end class Channel < RSS09::Channel end class Items < RSS09::Items class Item < RSS09::Items::Item end end class Image < RSS09::Image end class Textinput < RSS09::Textinput end end add_maker("0.9", "0.92", RSS092) add_maker("0.91", "0.91", RSS091) add_maker("0.92", "0.92", RSS092) add_maker("rss0.9", "0.92", RSS092) add_maker("rss0.91", "0.91", RSS091) add_maker("rss0.92", "0.92", RSS092) end end converter.rb000064400000007570147634040010007110 0ustar00require "rss/utils" module RSS class Converter include Utils def initialize(to_enc, from_enc=nil) if "".respond_to?(:encode) @to_encoding = to_enc return end normalized_to_enc = to_enc.downcase.gsub(/-/, '_') from_enc ||= 'utf-8' normalized_from_enc = from_enc.downcase.gsub(/-/, '_') if normalized_to_enc == normalized_from_enc def_same_enc() else def_diff_enc = "def_to_#{normalized_to_enc}_from_#{normalized_from_enc}" if respond_to?(def_diff_enc) __send__(def_diff_enc) else def_else_enc(to_enc, from_enc) end end end def convert(value) if value.is_a?(String) and value.respond_to?(:encode) value.encode(@to_encoding) else value end end def def_convert(depth=0) instance_eval(<<-EOC, *get_file_and_line_from_caller(depth)) def convert(value) if value.kind_of?(String) #{yield('value')} else value end end EOC end def def_iconv_convert(to_enc, from_enc, depth=0) begin require "iconv" @iconv = Iconv.new(to_enc, from_enc) def_convert(depth+1) do |value| <<-EOC begin @iconv.iconv(#{value}) rescue Iconv::Failure raise ConversionError.new(#{value}, "#{to_enc}", "#{from_enc}") end EOC end rescue LoadError, ArgumentError, SystemCallError raise UnknownConversionMethodError.new(to_enc, from_enc) end end def def_else_enc(to_enc, from_enc) def_iconv_convert(to_enc, from_enc, 0) end def def_same_enc() def_convert do |value| value end end def def_uconv_convert_if_can(meth, to_enc, from_enc, nkf_arg) begin require "uconv" def_convert(1) do |value| <<-EOC begin Uconv.#{meth}(#{value}) rescue Uconv::Error raise ConversionError.new(#{value}, "#{to_enc}", "#{from_enc}") end EOC end rescue LoadError require 'nkf' if NKF.const_defined?(:UTF8) def_convert(1) do |value| "NKF.nkf(#{nkf_arg.dump}, #{value})" end else def_iconv_convert(to_enc, from_enc, 1) end end end def def_to_euc_jp_from_utf_8 def_uconv_convert_if_can('u8toeuc', 'EUC-JP', 'UTF-8', '-We') end def def_to_utf_8_from_euc_jp def_uconv_convert_if_can('euctou8', 'UTF-8', 'EUC-JP', '-Ew') end def def_to_shift_jis_from_utf_8 def_uconv_convert_if_can('u8tosjis', 'Shift_JIS', 'UTF-8', '-Ws') end def def_to_utf_8_from_shift_jis def_uconv_convert_if_can('sjistou8', 'UTF-8', 'Shift_JIS', '-Sw') end def def_to_euc_jp_from_shift_jis require "nkf" def_convert do |value| "NKF.nkf('-Se', #{value})" end end def def_to_shift_jis_from_euc_jp require "nkf" def_convert do |value| "NKF.nkf('-Es', #{value})" end end def def_to_euc_jp_from_iso_2022_jp require "nkf" def_convert do |value| "NKF.nkf('-Je', #{value})" end end def def_to_iso_2022_jp_from_euc_jp require "nkf" def_convert do |value| "NKF.nkf('-Ej', #{value})" end end def def_to_utf_8_from_iso_8859_1 def_convert do |value| "#{value}.unpack('C*').pack('U*')" end end def def_to_iso_8859_1_from_utf_8 def_convert do |value| <<-EOC array_utf8 = #{value}.unpack('U*') array_enc = [] array_utf8.each do |num| if num <= 0xFF array_enc << num else array_enc.concat "&\#\#{num};".unpack('C*') end end array_enc.pack('C*') EOC end end end end 1.0.rb000064400000020730147634040010005370 0ustar00require "rss/parser" module RSS module RSS10 NSPOOL = {} ELEMENTS = [] def self.append_features(klass) super klass.install_must_call_validator('', ::RSS::URI) end end class RDF < Element include RSS10 include RootElementMixin class << self def required_uri URI end end @tag_name = 'RDF' PREFIX = 'rdf' URI = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" install_ns('', ::RSS::URI) install_ns(PREFIX, URI) [ ["channel", nil], ["image", "?"], ["item", "+", :children], ["textinput", "?"], ].each do |tag, occurs, type| type ||= :child __send__("install_have_#{type}_element", tag, ::RSS::URI, occurs) end alias_method(:rss_version, :feed_version) def initialize(version=nil, encoding=nil, standalone=nil) super('1.0', version, encoding, standalone) @feed_type = "rss" end def full_name tag_name_with_prefix(PREFIX) end class Li < Element include RSS10 class << self def required_uri URI end end [ ["resource", [URI, ""], true] ].each do |name, uri, required| install_get_attribute(name, uri, required) end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.resource = args[0] end end def full_name tag_name_with_prefix(PREFIX) end end class Seq < Element include RSS10 Li = ::RSS::RDF::Li class << self def required_uri URI end end @tag_name = 'Seq' install_have_children_element("li", URI, "*") install_must_call_validator('rdf', ::RSS::RDF::URI) def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() @li = args[0] if args[0] end end def full_name tag_name_with_prefix(PREFIX) end def setup_maker(target) lis.each do |li| target << li.resource end end end class Bag < Element include RSS10 Li = ::RSS::RDF::Li class << self def required_uri URI end end @tag_name = 'Bag' install_have_children_element("li", URI, "*") install_must_call_validator('rdf', URI) def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() @li = args[0] if args[0] end end def full_name tag_name_with_prefix(PREFIX) end def setup_maker(target) lis.each do |li| target << li.resource end end end class Channel < Element include RSS10 class << self def required_uri ::RSS::URI end end [ ["about", URI, true] ].each do |name, uri, required| install_get_attribute(name, uri, required, nil, nil, "#{PREFIX}:#{name}") end [ ['title', nil, :text], ['link', nil, :text], ['description', nil, :text], ['image', '?', :have_child], ['items', nil, :have_child], ['textinput', '?', :have_child], ].each do |tag, occurs, type| __send__("install_#{type}_element", tag, ::RSS::URI, occurs) end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.about = args[0] end end private def maker_target(maker) maker.channel end def setup_maker_attributes(channel) channel.about = about end class Image < Element include RSS10 class << self def required_uri ::RSS::URI end end [ ["resource", URI, true] ].each do |name, uri, required| install_get_attribute(name, uri, required, nil, nil, "#{PREFIX}:#{name}") end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.resource = args[0] end end end class Textinput < Element include RSS10 class << self def required_uri ::RSS::URI end end [ ["resource", URI, true] ].each do |name, uri, required| install_get_attribute(name, uri, required, nil, nil, "#{PREFIX}:#{name}") end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.resource = args[0] end end end class Items < Element include RSS10 Seq = ::RSS::RDF::Seq class << self def required_uri ::RSS::URI end end install_have_child_element("Seq", URI, nil) install_must_call_validator('rdf', URI) def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.Seq = args[0] end self.Seq ||= Seq.new end def resources if @Seq @Seq.lis.collect do |li| li.resource end else [] end end end end class Image < Element include RSS10 class << self def required_uri ::RSS::URI end end [ ["about", URI, true] ].each do |name, uri, required| install_get_attribute(name, uri, required, nil, nil, "#{PREFIX}:#{name}") end %w(title url link).each do |name| install_text_element(name, ::RSS::URI, nil) end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.about = args[0] end end private def maker_target(maker) maker.image end end class Item < Element include RSS10 class << self def required_uri ::RSS::URI end end [ ["about", URI, true] ].each do |name, uri, required| install_get_attribute(name, uri, required, nil, nil, "#{PREFIX}:#{name}") end [ ["title", nil], ["link", nil], ["description", "?"], ].each do |tag, occurs| install_text_element(tag, ::RSS::URI, occurs) end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.about = args[0] end end private def maker_target(items) if items.respond_to?("items") # For backward compatibility items = items.items end items.new_item end end class Textinput < Element include RSS10 class << self def required_uri ::RSS::URI end end [ ["about", URI, true] ].each do |name, uri, required| install_get_attribute(name, uri, required, nil, nil, "#{PREFIX}:#{name}") end %w(title description name link).each do |name| install_text_element(name, ::RSS::URI, nil) end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.about = args[0] end end private def maker_target(maker) maker.textinput end end end RSS10::ELEMENTS.each do |name| BaseListener.install_get_text_element(URI, name, name) end module ListenerMixin private def initial_start_RDF(tag_name, prefix, attrs, ns) check_ns(tag_name, prefix, ns, RDF::URI, false) @rss = RDF.new(@version, @encoding, @standalone) @rss.do_validate = @do_validate @rss.xml_stylesheets = @xml_stylesheets @last_element = @rss pr = Proc.new do |text, tags| @rss.validate_for_stream(tags, @ignore_unknown_element) if @do_validate end @proc_stack.push(pr) end end end xml-stylesheet.rb000064400000004203147634040010010056 0ustar00require "rss/utils" module RSS module XMLStyleSheetMixin attr_accessor :xml_stylesheets def initialize(*args) super @xml_stylesheets = [] end private def xml_stylesheet_pi xsss = @xml_stylesheets.collect do |xss| pi = xss.to_s pi = nil if /\A\s*\z/ =~ pi pi end.compact xsss.push("") unless xsss.empty? xsss.join("\n") end end class XMLStyleSheet include Utils ATTRIBUTES = %w(href type title media charset alternate) GUESS_TABLE = { "xsl" => "text/xsl", "css" => "text/css", } attr_accessor(*ATTRIBUTES) attr_accessor(:do_validate) def initialize(*attrs) if attrs.size == 1 and (attrs.first.is_a?(Hash) or attrs.first.is_a?(Array)) attrs = attrs.first end @do_validate = true ATTRIBUTES.each do |attr| __send__("#{attr}=", nil) end vars = ATTRIBUTES.dup vars.unshift(:do_validate) attrs.each do |name, value| if vars.include?(name.to_s) __send__("#{name}=", value) end end end def to_s rv = "" if @href rv << %Q[] end rv end remove_method(:href=) def href=(value) @href = value if @href and @type.nil? @type = guess_type(@href) end @href end remove_method(:alternate=) def alternate=(value) if value.nil? or /\A(?:yes|no)\z/ =~ value @alternate = value else if @do_validate args = ["?xml-stylesheet?", %Q[alternate="#{value}"]] raise NotAvailableValueError.new(*args) end end @alternate end def setup_maker(maker) xss = maker.xml_stylesheets.new_xml_stylesheet ATTRIBUTES.each do |attr| xss.__send__("#{attr}=", __send__(attr)) end end private def guess_type(filename) /\.([^.]+)$/ =~ filename GUESS_TABLE[$1] end end end maker.rb000064400000003401147634040010006165 0ustar00require "rss/rss" module RSS ## # # Provides a set of builders for various RSS objects # # * Feeds # * RSS 0.91 # * RSS 1.0 # * RSS 2.0 # * Atom 1.0 # # * Elements # * Atom::Entry module Maker # Collection of supported makers MAKERS = {} class << self # Builder for an RSS object # Creates an object of the type passed in +args+ # # Executes the +block+ to populate elements of the created RSS object def make(version, &block) self[version].make(&block) end # Returns the maker for the +version+ def [](version) maker_info = maker(version) raise UnsupportedMakerVersionError.new(version) if maker_info.nil? maker_info[:maker] end # Adds a maker to the set of supported makers def add_maker(version, normalized_version, maker) MAKERS[version] = {:maker => maker, :version => normalized_version} end # Returns collection of supported maker versions def versions MAKERS.keys.uniq.sort end # Returns collection of supported makers def makers MAKERS.values.collect { |info| info[:maker] }.uniq end # Returns true if the version is supported def supported?(version) versions.include?(version) end private # Can I remove this method? def maker(version) MAKERS[version] end end end end require "rss/maker/1.0" require "rss/maker/2.0" require "rss/maker/feed" require "rss/maker/entry" require "rss/maker/content" require "rss/maker/dublincore" require "rss/maker/slash" require "rss/maker/syndication" require "rss/maker/taxonomy" require "rss/maker/trackback" require "rss/maker/image" require "rss/maker/itunes" xml.rb000064400000002737147634040010005701 0ustar00require "rss/utils" module RSS module XML class Element include Enumerable attr_reader :name, :prefix, :uri, :attributes, :children def initialize(name, prefix=nil, uri=nil, attributes={}, children=[]) @name = name @prefix = prefix @uri = uri @attributes = attributes if children.is_a?(String) or !children.respond_to?(:each) @children = [children] else @children = children end end def [](name) @attributes[name] end def []=(name, value) @attributes[name] = value end def <<(child) @children << child end def each(&block) @children.each(&block) end def ==(other) other.kind_of?(self.class) and @name == other.name and @uri == other.uri and @attributes == other.attributes and @children == other.children end def to_s rv = "<#{full_name}" attributes.each do |key, value| rv << " #{Utils.html_escape(key)}=\"#{Utils.html_escape(value)}\"" end if children.empty? rv << "/>" else rv << ">" children.each do |child| rv << child.to_s end rv << "" end rv end def full_name if @prefix "#{@prefix}:#{@name}" else @name end end end end end xmlscanner.rb000064400000004152147634040010007244 0ustar00require 'xmlscan/scanner' require 'stringio' module RSS class XMLScanParser < BaseParser class << self def listener XMLScanListener end end private def _parse begin if @rss.is_a?(String) input = StringIO.new(@rss) else input = @rss end scanner = XMLScan::XMLScanner.new(@listener) scanner.parse(input) rescue XMLScan::Error => e lineno = e.lineno || scanner.lineno || input.lineno raise NotWellFormedError.new(lineno){e.message} end end end class XMLScanListener < BaseListener include XMLScan::Visitor include ListenerMixin ENTITIES = { 'lt' => '<', 'gt' => '>', 'amp' => '&', 'quot' => '"', 'apos' => '\'' } def on_xmldecl_version(str) @version = str end def on_xmldecl_encoding(str) @encoding = str end def on_xmldecl_standalone(str) @standalone = str end def on_xmldecl_end xmldecl(@version, @encoding, @standalone == "yes") end alias_method(:on_pi, :instruction) alias_method(:on_chardata, :text) alias_method(:on_cdata, :text) def on_etag(name) tag_end(name) end def on_entityref(ref) text(entity(ref)) end def on_charref(code) text([code].pack('U')) end alias_method(:on_charref_hex, :on_charref) def on_stag(name) @attrs = {} end def on_attribute(name) @attrs[name] = @current_attr = '' end def on_attr_value(str) @current_attr << str end def on_attr_entityref(ref) @current_attr << entity(ref) end def on_attr_charref(code) @current_attr << [code].pack('U') end alias_method(:on_attr_charref_hex, :on_attr_charref) def on_stag_end(name) tag_start(name, @attrs) end def on_stag_end_empty(name) tag_start(name, @attrs) tag_end(name) end private def entity(ref) ent = ENTITIES[ref] if ent ent else wellformed_error("undefined entity: #{ref}") end end end end 0.9.rb000064400000022564147634040010005407 0ustar00require "rss/parser" module RSS module RSS09 NSPOOL = {} ELEMENTS = [] def self.append_features(klass) super klass.install_must_call_validator('', "") end end class Rss < Element include RSS09 include RootElementMixin %w(channel).each do |name| install_have_child_element(name, "", nil) end attr_writer :feed_version alias_method(:rss_version, :feed_version) alias_method(:rss_version=, :feed_version=) def initialize(feed_version, version=nil, encoding=nil, standalone=nil) super @feed_type = "rss" end def items if @channel @channel.items else [] end end def image if @channel @channel.image else nil end end def textinput if @channel @channel.textInput else nil end end def setup_maker_elements(maker) super items.each do |item| item.setup_maker(maker.items) end image.setup_maker(maker) if image textinput.setup_maker(maker) if textinput end private def _attrs [ ["version", true, "feed_version"], ] end class Channel < Element include RSS09 [ ["title", nil, :text], ["link", nil, :text], ["description", nil, :text], ["language", nil, :text], ["copyright", "?", :text], ["managingEditor", "?", :text], ["webMaster", "?", :text], ["rating", "?", :text], ["pubDate", "?", :date, :rfc822], ["lastBuildDate", "?", :date, :rfc822], ["docs", "?", :text], ["cloud", "?", :have_attribute], ["skipDays", "?", :have_child], ["skipHours", "?", :have_child], ["image", nil, :have_child], ["item", "*", :have_children], ["textInput", "?", :have_child], ].each do |name, occurs, type, *args| __send__("install_#{type}_element", name, "", occurs, name, *args) end alias date pubDate alias date= pubDate= private def maker_target(maker) maker.channel end def setup_maker_elements(channel) super [ [skipDays, "day"], [skipHours, "hour"], ].each do |skip, key| if skip skip.__send__("#{key}s").each do |val| target_skips = channel.__send__("skip#{key.capitalize}s") new_target = target_skips.__send__("new_#{key}") new_target.content = val.content end end end end def not_need_to_call_setup_maker_variables %w(image textInput) end class SkipDays < Element include RSS09 [ ["day", "*"] ].each do |name, occurs| install_have_children_element(name, "", occurs) end class Day < Element include RSS09 content_setup def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.content = args[0] end end end end class SkipHours < Element include RSS09 [ ["hour", "*"] ].each do |name, occurs| install_have_children_element(name, "", occurs) end class Hour < Element include RSS09 content_setup(:integer) def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.content = args[0] end end end end class Image < Element include RSS09 %w(url title link).each do |name| install_text_element(name, "", nil) end [ ["width", :integer], ["height", :integer], ["description"], ].each do |name, type| install_text_element(name, "", "?", name, type) end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.url = args[0] self.title = args[1] self.link = args[2] self.width = args[3] self.height = args[4] self.description = args[5] end end private def maker_target(maker) maker.image end end class Cloud < Element include RSS09 [ ["domain", "", true], ["port", "", true, :integer], ["path", "", true], ["registerProcedure", "", true], ["protocol", "", true], ].each do |name, uri, required, type| install_get_attribute(name, uri, required, type) end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.domain = args[0] self.port = args[1] self.path = args[2] self.registerProcedure = args[3] self.protocol = args[4] end end end class Item < Element include RSS09 [ ["title", '?', :text], ["link", '?', :text], ["description", '?', :text], ["category", '*', :have_children, "categories"], ["source", '?', :have_child], ["enclosure", '?', :have_child], ].each do |tag, occurs, type, *args| __send__("install_#{type}_element", tag, "", occurs, tag, *args) end private def maker_target(items) if items.respond_to?("items") # For backward compatibility items = items.items end items.new_item end def setup_maker_element(item) super @enclosure.setup_maker(item) if @enclosure @source.setup_maker(item) if @source end class Source < Element include RSS09 [ ["url", "", true] ].each do |name, uri, required| install_get_attribute(name, uri, required) end content_setup def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.url = args[0] self.content = args[1] end end private def maker_target(item) item.source end def setup_maker_attributes(source) source.url = url source.content = content end end class Enclosure < Element include RSS09 [ ["url", "", true], ["length", "", true, :integer], ["type", "", true], ].each do |name, uri, required, type| install_get_attribute(name, uri, required, type) end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.url = args[0] self.length = args[1] self.type = args[2] end end private def maker_target(item) item.enclosure end def setup_maker_attributes(enclosure) enclosure.url = url enclosure.length = length enclosure.type = type end end class Category < Element include RSS09 [ ["domain", "", false] ].each do |name, uri, required| install_get_attribute(name, uri, required) end content_setup def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.domain = args[0] self.content = args[1] end end private def maker_target(item) item.new_category end def setup_maker_attributes(category) category.domain = domain category.content = content end end end class TextInput < Element include RSS09 %w(title description name link).each do |name| install_text_element(name, "", nil) end def initialize(*args) if Utils.element_initialize_arguments?(args) super else super() self.title = args[0] self.description = args[1] self.name = args[2] self.link = args[3] end end private def maker_target(maker) maker.textinput end end end end RSS09::ELEMENTS.each do |name| BaseListener.install_get_text_element("", name, name) end module ListenerMixin private def initial_start_rss(tag_name, prefix, attrs, ns) check_ns(tag_name, prefix, ns, "", false) @rss = Rss.new(attrs['version'], @version, @encoding, @standalone) @rss.do_validate = @do_validate @rss.xml_stylesheets = @xml_stylesheets @last_element = @rss pr = Proc.new do |text, tags| @rss.validate_for_stream(tags, @ignore_unknown_element) if @do_validate end @proc_stack.push(pr) end end end utils.rb000064400000005163147634040010006235 0ustar00module RSS module Utils module_function # Convert a name_with_underscores to CamelCase. def to_class_name(name) name.split(/[_\-]/).collect do |part| "#{part[0, 1].upcase}#{part[1..-1]}" end.join("") end def get_file_and_line_from_caller(i=0) file, line, = caller[i].split(':') line = line.to_i line += 1 if i.zero? [file, line] end # escape '&', '"', '<' and '>' for use in HTML. def html_escape(s) s.to_s.gsub(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/