feedjira_extension.rb 6.1KB

    require 'feedjira' require 'digest' require 'mail' module FeedjiraExtension AUTHOR_ATTRS = %i[name email uri] LINK_ATTRS = %i[href rel type hreflang title length] ENCLOSURE_ATTRS = %i[url type length] class Author < Struct.new(*AUTHOR_ATTRS) def to_json(options = nil) members.flat_map { |key| if value = self[key].presence case key when :email "<#{value}>" when :uri "(#{value})" else value end else [] end }.join(' ').to_json(options) end end class AtomAuthor < Author include SAXMachine AUTHOR_ATTRS.each do |attr| element attr end end class RssAuthor < Author include SAXMachine def content=(content) @content = content begin addr = Mail::Address.new(content) rescue self.name = content else self.name = addr.name self.email = addr.address end end value :content end class Enclosure include SAXMachine ENCLOSURE_ATTRS.each do |attr| attribute attr end def to_json(options = nil) ENCLOSURE_ATTRS.each_with_object({}) { |key, hash| if value = __send__(key) hash[key] = value end }.to_json(options) end end class AtomLink include SAXMachine LINK_ATTRS.each do |attr| attribute attr end def to_json(options = nil) LINK_ATTRS.each_with_object({}) { |key, hash| if value = __send__(key) hash[key] = value end }.to_json(options) end end class RssLinkElement include SAXMachine value :href def to_json(options = nil) { href: href }.to_json(options) end end module HasAuthors def self.included(mod) mod.module_exec do case name when /RSS/ %w[ itunes:author dc:creator author managingEditor ].each do |name| sax_config.top_level_elements[name].clear elements name, class: RssAuthor, as: :authors end else elements :author, class: AtomAuthor, as: :authors end def alternate_link links.find { |link| link.is_a?(AtomLink) && link.rel == 'alternate' && (link.type == 'text/html'|| link.type.nil?) } end def url @url ||= (alternate_link || links.first).try!(:href) end end end end module HasEnclosure def self.included(mod) mod.module_exec do sax_config.top_level_elements['enclosure'].clear element :enclosure, class: Enclosure def image_enclosure case enclosure.try!(:type) when %r{\Aimage/} enclosure end end def image @image ||= image_enclosure.try!(:url) end end end end module HasLinks def self.included(mod) mod.module_exec do sax_config.top_level_elements['link'].clear sax_config.collection_elements['link'].clear case name when /RSS/ elements :link, class: RssLinkElement, as: :rss_links case name when /FeedBurner/ elements :'atok10:link', class: AtomLink, as: :atom_links def links @links ||= [*rss_links, *atom_links] end else alias_method :links, :rss_links end else elements :link, class: AtomLink, as: :links end def alternate_link links.find { |link| link.is_a?(AtomLink) && link.rel == 'alternate' && (link.type == 'text/html'|| link.type.nil?) } end def url @url ||= (alternate_link || links.first).try!(:href) end end end end module HasTimestamps attr_reader :published, :updated # Keep the "oldest" publish time found def published=(value) parsed = parse_datetime(value) @published = parsed if !@published || parsed < @published end # Keep the most recent update time found def updated=(value) parsed = parse_datetime(value) @updated = parsed if !@updated || parsed > @updated end def date_published published.try(:iso8601) end def last_updated (updated || published).try(:iso8601) end private def parse_datetime(string) DateTime.parse(string) rescue nil end end module FeedEntryExtensions def self.included(mod) mod.module_exec do include HasAuthors include HasEnclosure include HasLinks include HasTimestamps end end def id entry_id || Digest::MD5.hexdigest(content || summary || '') end end module FeedExtensions def self.included(mod) mod.module_exec do include HasAuthors include HasEnclosure include HasLinks include HasTimestamps element :id, as: :feed_id element :generator elements :rights element :published element :updated element :icon if /RSS/ === name element :guid, as: :feed_id element :copyright element :pubDate, as: :published element :'dc:date', as: :published element :lastBuildDate, as: :updated element :image, value: :url, as: :icon def copyright @copyright || super end end sax_config.collection_elements.each_value do |collection_elements| collection_elements.each do |collection_element| collection_element.accessor == 'entries' && (entry_class = collection_element.data_class).is_a?(Class) or next entry_class.send :include, FeedEntryExtensions end end end end def copyright rights.join("\n").presence end end Feedjira::Feed.feed_classes.each do |feed_class| feed_class.send :include, FeedExtensions end end