mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
e859e668d2
[#15206] [Fix GH-1976] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@65505 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
1025 lines
29 KiB
Ruby
1025 lines
29 KiB
Ruby
# frozen_string_literal: false
|
|
require_relative '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
|
|
# Returns the Atom URI W3C Namespace
|
|
def required_uri
|
|
URI
|
|
end
|
|
|
|
# Returns true
|
|
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
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#text.constructs
|
|
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
|
|
|
|
# Returns or builds the XHTML content.
|
|
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
|
|
|
|
# Raises a MissingTagError or NotExpectedTagError
|
|
# if the element is not properly formatted.
|
|
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.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#atomPersonConstruct
|
|
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.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.name
|
|
class Name < RSS::Element
|
|
include CommonModel
|
|
include ContentModel
|
|
end
|
|
|
|
# The URI of the person or entity.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.uri
|
|
class Uri < RSS::Element
|
|
include CommonModel
|
|
include URIContentModel
|
|
end
|
|
|
|
# The email of the person or entity.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.email
|
|
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
|
|
|
|
# Defines the top-level element of an Atom Feed Document.
|
|
# It consists of a number of children Entry elements,
|
|
# and has the following attributes:
|
|
#
|
|
# * author
|
|
# * categories
|
|
# * category
|
|
# * content
|
|
# * contributor
|
|
# * entries (aliased as items)
|
|
# * entry
|
|
# * generator
|
|
# * icon
|
|
# * id
|
|
# * link
|
|
# * logo
|
|
# * rights
|
|
# * subtitle
|
|
# * title
|
|
# * updated
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.feed
|
|
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
|
|
|
|
# PersonConstruct that contains information regarding the author
|
|
# of a Feed or Entry.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.author
|
|
class Author < RSS::Element
|
|
include CommonModel
|
|
include PersonConstruct
|
|
end
|
|
|
|
# Contains information about a category associated with a Feed or Entry.
|
|
# It has the following attributes:
|
|
#
|
|
# * term
|
|
# * scheme
|
|
# * label
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.category
|
|
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
|
|
|
|
# PersonConstruct that contains information regarding the
|
|
# contributors of a Feed or Entry.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.contributor
|
|
class Contributor < RSS::Element
|
|
include CommonModel
|
|
include PersonConstruct
|
|
end
|
|
|
|
# Contains information on the agent used to generate the feed.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.generator
|
|
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
|
|
|
|
# Defines an image that provides a visual identification for a eed.
|
|
# The image should have an aspect ratio of 1:1.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.icon
|
|
class Icon < RSS::Element
|
|
include CommonModel
|
|
include URIContentModel
|
|
end
|
|
|
|
# Defines the Universally Unique Identifier (UUID) for a Feed or Entry.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.id
|
|
class Id < RSS::Element
|
|
include CommonModel
|
|
include URIContentModel
|
|
end
|
|
|
|
# Defines a reference to a Web resource. It has the following
|
|
# attributes:
|
|
#
|
|
# * href
|
|
# * rel
|
|
# * type
|
|
# * hreflang
|
|
# * title
|
|
# * length
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.link
|
|
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
|
|
|
|
# Defines an image that provides a visual identification for the Feed.
|
|
# The image should have an aspect ratio of 2:1 (horizontal:vertical).
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.logo
|
|
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
|
|
|
|
# TextConstruct that contains copyright information regarding
|
|
# the content in an Entry or Feed. It should not be used to
|
|
# convey machine readable licensing information.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.rights
|
|
class Rights < RSS::Element
|
|
include CommonModel
|
|
include TextConstruct
|
|
end
|
|
|
|
# TextConstruct that conveys a description or subtitle for a Feed.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.subtitle
|
|
class Subtitle < RSS::Element
|
|
include CommonModel
|
|
include TextConstruct
|
|
end
|
|
|
|
# TextConstruct that conveys a description or title for a Feed or Entry.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.title
|
|
class Title < RSS::Element
|
|
include CommonModel
|
|
include TextConstruct
|
|
end
|
|
|
|
# DateConstruct indicating the most recent time when a Feed or
|
|
# Entry was modified in a way the publisher considers
|
|
# significant.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.updated
|
|
class Updated < RSS::Element
|
|
include CommonModel
|
|
include DateConstruct
|
|
end
|
|
|
|
# Defines a child Atom Entry element of an Atom Feed element.
|
|
# It has the following attributes:
|
|
#
|
|
# * author
|
|
# * category
|
|
# * categories
|
|
# * content
|
|
# * contributor
|
|
# * id
|
|
# * link
|
|
# * published
|
|
# * rights
|
|
# * source
|
|
# * summary
|
|
# * title
|
|
# * updated
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.entry
|
|
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
|
|
|
|
# Feed::Author
|
|
Author = Feed::Author
|
|
# Feed::Category
|
|
Category = Feed::Category
|
|
|
|
# Contains or links to the content of the Entry.
|
|
# It has the following attributes:
|
|
#
|
|
# * type
|
|
# * src
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.content
|
|
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")
|
|
|
|
# Returns the element content in XML.
|
|
attr_writer :xml
|
|
|
|
# Returns true if the element has inline XML content.
|
|
def have_xml_content?
|
|
inline_xhtml? or inline_other_xml?
|
|
end
|
|
|
|
# Returns or builds the element content in XML.
|
|
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
|
|
|
|
# Returns the element content in XHTML.
|
|
def xhtml
|
|
if inline_xhtml?
|
|
xml
|
|
else
|
|
nil
|
|
end
|
|
end
|
|
|
|
# Raises a MissingAttributeError, NotAvailableValueError,
|
|
# MissingTagError or NotExpectedTagError if the element is
|
|
# not properly formatted.
|
|
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
|
|
|
|
# Returns true if the element contains inline content
|
|
# that has a text or HTML media type, or no media type at all.
|
|
def inline_text?
|
|
!out_of_line? and [nil, "text", "html"].include?(@type)
|
|
end
|
|
|
|
# Returns true if the element contains inline content that
|
|
# has a HTML media type.
|
|
def inline_html?
|
|
return false if out_of_line?
|
|
@type == "html" or mime_split == ["text", "html"]
|
|
end
|
|
|
|
# Returns true if the element contains inline content that
|
|
# has a XHTML media type.
|
|
def inline_xhtml?
|
|
!out_of_line? and @type == "xhtml"
|
|
end
|
|
|
|
# Returns true if the element contains inline content that
|
|
# has a MIME media type.
|
|
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
|
|
|
|
# Returns true if the element contains inline content that
|
|
# has a text media type.
|
|
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
|
|
|
|
# Returns true if the element contains inline content that
|
|
# has a XML media type.
|
|
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
|
|
|
|
# Returns true if the element contains inline content
|
|
# encoded in base64.
|
|
def inline_other_base64?
|
|
inline_other? and !inline_other_text? and !inline_other_xml?
|
|
end
|
|
|
|
# Returns true if the element contains linked content.
|
|
def out_of_line?
|
|
not @src.nil?
|
|
end
|
|
|
|
# Splits the type attribute into an array, e.g. ["text", "xml"]
|
|
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
|
|
|
|
# Returns true if the content needs to be encoded in base64.
|
|
def need_base64_encode?
|
|
inline_other_base64?
|
|
end
|
|
|
|
private
|
|
def empty_content?
|
|
out_of_line? or super
|
|
end
|
|
end
|
|
|
|
# Feed::Contributor
|
|
Contributor = Feed::Contributor
|
|
# Feed::Id
|
|
Id = Feed::Id
|
|
# Feed::Link
|
|
Link = Feed::Link
|
|
|
|
# DateConstruct that usually indicates the time of the initial
|
|
# creation of an Entry.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.published
|
|
class Published < RSS::Element
|
|
include CommonModel
|
|
include DateConstruct
|
|
end
|
|
|
|
# Feed::Rights
|
|
Rights = Feed::Rights
|
|
|
|
# Defines a Atom Source element. It has the following attributes:
|
|
#
|
|
# * author
|
|
# * category
|
|
# * categories
|
|
# * content
|
|
# * contributor
|
|
# * generator
|
|
# * icon
|
|
# * id
|
|
# * link
|
|
# * logo
|
|
# * rights
|
|
# * subtitle
|
|
# * title
|
|
# * updated
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.source
|
|
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
|
|
|
|
# Returns true if the Source element has an author.
|
|
def have_author?
|
|
!author.to_s.empty?
|
|
end
|
|
|
|
# Feed::Author
|
|
Author = Feed::Author
|
|
# Feed::Category
|
|
Category = Feed::Category
|
|
# Feed::Contributor
|
|
Contributor = Feed::Contributor
|
|
# Feed::Generator
|
|
Generator = Feed::Generator
|
|
# Feed::Icon
|
|
Icon = Feed::Icon
|
|
# Feed::Id
|
|
Id = Feed::Id
|
|
# Feed::Link
|
|
Link = Feed::Link
|
|
# Feed::Logo
|
|
Logo = Feed::Logo
|
|
# Feed::Rights
|
|
Rights = Feed::Rights
|
|
# Feed::Subtitle
|
|
Subtitle = Feed::Subtitle
|
|
# Feed::Title
|
|
Title = Feed::Title
|
|
# Feed::Updated
|
|
Updated = Feed::Updated
|
|
end
|
|
|
|
# TextConstruct that describes a summary of the Entry.
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.summary
|
|
class Summary < RSS::Element
|
|
include CommonModel
|
|
include TextConstruct
|
|
end
|
|
|
|
# Feed::Title
|
|
Title = Feed::Title
|
|
# Feed::Updated
|
|
Updated = Feed::Updated
|
|
end
|
|
end
|
|
|
|
# Defines a top-level Atom Entry element,
|
|
# used as the document element of a stand-alone Atom Entry Document.
|
|
# It has the following attributes:
|
|
#
|
|
# * author
|
|
# * category
|
|
# * categories
|
|
# * content
|
|
# * contributor
|
|
# * id
|
|
# * link
|
|
# * published
|
|
# * rights
|
|
# * source
|
|
# * summary
|
|
# * title
|
|
# * updated
|
|
#
|
|
# Reference: https://validator.w3.org/feed/docs/rfc4287.html#element.entry]
|
|
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
|
|
|
|
# Feed::Entry::Author
|
|
Author = Feed::Entry::Author
|
|
# Feed::Entry::Category
|
|
Category = Feed::Entry::Category
|
|
# Feed::Entry::Content
|
|
Content = Feed::Entry::Content
|
|
# Feed::Entry::Contributor
|
|
Contributor = Feed::Entry::Contributor
|
|
# Feed::Entry::Id
|
|
Id = Feed::Entry::Id
|
|
# Feed::Entry::Link
|
|
Link = Feed::Entry::Link
|
|
# Feed::Entry::Published
|
|
Published = Feed::Entry::Published
|
|
# Feed::Entry::Rights
|
|
Rights = Feed::Entry::Rights
|
|
# Feed::Entry::Source
|
|
Source = Feed::Entry::Source
|
|
# Feed::Entry::Summary
|
|
Summary = Feed::Entry::Summary
|
|
# Feed::Entry::Title
|
|
Title = Feed::Entry::Title
|
|
# Feed::Entry::Updated
|
|
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
|