Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ source 'https://rubygems.org'

git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }

gem 'html2rss', '~> 0.19'
# gem 'html2rss', '~> 0.19'
# gem 'html2rss', github: 'html2rss/html2rss', branch: 'master'
gem 'html2rss-configs', github: 'html2rss/html2rss-configs'

# Use these instead of the two above (uncomment them) when developing locally:
# gem 'html2rss', path: '../html2rss'
gem 'html2rss', path: '../html2rss'
# gem 'html2rss-configs', path: '../html2rss-configs'

gem 'concurrent-ruby'
Expand Down
46 changes: 25 additions & 21 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@ GIT
html2rss-configs (0.2.0)
html2rss

PATH
remote: ../html2rss
specs:
html2rss (0.19.1)
addressable (~> 2.7)
brotli
dry-validation
faraday (> 2.0.1, < 3.0)
faraday-follow_redirects
faraday-gzip (~> 3)
kramdown
mime-types (> 3.0)
nokogiri (>= 1.10, < 2.0)
parallel
puppeteer-ruby
regexp_parser
reverse_markdown (~> 3.0)
rss
sanitize
thor
tzinfo
zeitwerk

GEM
remote: https://rubygems.org/
specs:
Expand Down Expand Up @@ -136,25 +159,6 @@ GEM
fiber-storage
fiber-storage (1.0.1)
hashdiff (1.2.1)
html2rss (0.19.1)
addressable (~> 2.7)
brotli
dry-validation
faraday (> 2.0.1, < 3.0)
faraday-follow_redirects
faraday-gzip (~> 3)
kramdown
mime-types (> 3.0)
nokogiri (>= 1.10, < 2.0)
parallel
puppeteer-ruby
regexp_parser
reverse_markdown (~> 3.0)
rss
sanitize
thor
tzinfo
zeitwerk
i18n (1.14.8)
concurrent-ruby (~> 1.0)
io-console (0.8.2)
Expand Down Expand Up @@ -371,7 +375,7 @@ PLATFORMS
DEPENDENCIES
climate_control
concurrent-ruby
html2rss (~> 0.19)
html2rss!
html2rss-configs!
irb
parallel
Expand Down Expand Up @@ -439,7 +443,7 @@ CHECKSUMS
fiber-local (1.1.0) sha256=c885f94f210fb9b05737de65d511136ea602e00c5105953748aa0f8793489f06
fiber-storage (1.0.1) sha256=f48e5b6d8b0be96dac486332b55cee82240057065dc761c1ea692b2e719240e1
hashdiff (1.2.1) sha256=9c079dbc513dfc8833ab59c0c2d8f230fa28499cc5efb4b8dd276cf931457cd1
html2rss (0.19.1) sha256=0af89c99421eb45ad386885cd4e9d946fbb1a887e757922c6f6bc5d825a32ac2
html2rss (0.19.1)
html2rss-configs (0.2.0)
i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
Expand Down
10 changes: 10 additions & 0 deletions app/web/boot/setup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,21 @@ def call!
configure_sentry!
configure_request_service!
configure_runtime_logging!
configure_gem_defaults!
log_startup!
end

private

# @return [void]
def configure_gem_defaults!
global_config = LocalConfig.global
Html2rss.configure do |config|
config.headers = global_config[:headers] if global_config[:headers]
config.stylesheets = global_config[:stylesheets] if global_config[:stylesheets]
end
end

# @return [void]
def validate_environment!
EnvironmentValidator.validate_environment!
Expand Down
15 changes: 1 addition & 14 deletions app/web/config/local_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def find(name)
config_hash = local_feed_config(normalized_name) || embedded_feed_config(normalized_name)
raise NotFound, "Did not find local feed config at '#{normalized_name}'" unless config_hash

apply_global_defaults(config_hash)
config_hash
end

##
Expand Down Expand Up @@ -120,19 +120,6 @@ def embedded_feed_config(normalized_name)
nil
end

# Applies global defaults only when feed-level keys are absent.
#
# @param config [Hash{Symbol=>Object}]
# @return [Hash{Symbol=>Object}]
def apply_global_defaults(config)
global_config = global

config[:stylesheets] ||= deep_dup(global_config[:stylesheets]) if global_config[:stylesheets]
config[:headers] ||= deep_dup(global_config[:headers]) if global_config[:headers]

config
end

# @param name [String, Symbol, #to_s]
# @return [String] path without feed extension for feed lookup.
def normalize_name(name)
Expand Down
81 changes: 36 additions & 45 deletions app/web/rendering/xml_builder.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# frozen_string_literal: true

require 'nokogiri'
require 'rss'
require 'time'

module Html2rss
module Web
##
Expand All @@ -18,17 +19,11 @@ class << self
# @param timestamp [Time, nil]
# @return [String] serialized RSS XML document.
def build_rss_feed(title:, description:, link: nil, items: [], timestamp: nil)
current_time = timestamp || Time.now
formatted_now = format_pub_date(current_time)

Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |xml|
xml.rss(version: '2.0') do
xml.channel do
build_channel(xml, title:, description:, link:, now: formatted_now)
build_items(xml, items, default_pub_date: formatted_now)
end
end
end.doc.to_xml(save_with: Nokogiri::XML::Node::SaveOptions::AS_XML)
RSS::Maker.make('2.0') do |maker|
apply_stylesheets(maker)
build_channel(maker.channel, title:, description:, link:, timestamp:)
build_items(maker, items, default_timestamp: timestamp)
end.to_s
end

# @param message [String]
Expand Down Expand Up @@ -61,6 +56,16 @@ def build_empty_feed_warning(url:, strategy:, site_title: nil)

private

# @param maker [RSS::Maker::RSS20]
# @return [void]
def apply_stylesheets(maker)
# Use the gem's internal stylesheet support.
stylesheets = Html2rss.configuration.stylesheets.map do |s|
Html2rss::RssBuilder::Stylesheet.new(**s)
end
Html2rss::RssBuilder::Stylesheet.add(maker, stylesheets)
end

# @param title [String]
# @param description [String]
# @param item [Hash{Symbol=>Object}]
Expand All @@ -81,57 +86,43 @@ def build_single_item_feed(title:, description:, item:, link: nil)
# @param timestamp [Time]
# @return [Hash{Symbol=>Object}] normalized item with required RSS fields.
def feed_item(item, timestamp:)
feed_item = {
{
title: item[:title],
description: item[:description],
link: item[:link],
pubDate: timestamp
}
feed_item[:link] = item[:link] if item[:link]
feed_item
end

# @param xml [Nokogiri::XML::Builder]
# @param channel [RSS::Maker::RSS20::Channel]
# @param title [String]
# @param description [String]
# @param link [String, nil]
# @param now [String]
# @param timestamp [Time, nil]
# @return [void]
def build_channel(xml, title:, description:, link:, now:)
xml.title(title.to_s)
xml.description(description.to_s)
xml.link(link.to_s) if link
xml.lastBuildDate(now)
xml.pubDate(now)
def build_channel(channel, title:, description:, link:, timestamp:)
now = timestamp || Time.now
channel.title = title.to_s
channel.description = description.to_s
channel.link = link.to_s
channel.lastBuildDate = now
channel.pubDate = now
end

# @param xml [Nokogiri::XML::Builder]
# @param maker [RSS::Maker::RSS20]
# @param items [Array<Hash{Symbol=>Object}>]
# @param default_pub_date [String]
# @param default_timestamp [Time, nil]
# @return [void]
def build_items(xml, items, default_pub_date:)
def build_items(maker, items, default_timestamp:)
items.each do |item|
xml.item do
append_text_node(xml, :title, item[:title])
append_text_node(xml, :description, item[:description])
append_text_node(xml, :link, item[:link])
xml.pubDate(format_pub_date(item[:pubDate] || default_pub_date))
maker.items.new_item do |i|
i.title = item[:title].to_s
i.description = item[:description].to_s
i.link = item[:link].to_s
i.pubDate = item[:pubDate] || default_timestamp || Time.now
end
end
end

# @param xml [Nokogiri::XML::Builder]
# @param node_name [Symbol]
# @param value [Object]
# @return [void]
def append_text_node(xml, node_name, value)
xml.public_send(node_name, value.to_s) if value
end

# @param pub_date [Time, String]
# @return [String] RFC2822 date string for RSS output.
def format_pub_date(pub_date)
pub_date.is_a?(Time) ? pub_date.rfc2822 : pub_date.to_s
end
end
end
end
Expand Down
4 changes: 4 additions & 0 deletions spec/html2rss/web/xml_builder_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,9 @@
it 'does not mention hidden strategy controls in item text' do
expect(xml_doc.at_xpath('//item/description').text).not_to include('browserless strategy')
end

it 'includes the default stylesheet processing instruction' do
expect(xml_doc.to_s).to include('<?xml-stylesheet href="/rss.xsl" type="text/xsl" media="all"?>')
end
end
end
Loading