refactor: extract feed and sitemap helpers into FeedHelpers module

Move feed_xml, feed_item, feed_json, json_feed_item, rfc822_date,
iso_date, and sitemap_xml out of Server into a dedicated FeedHelpers
module included via helpers. Reduces Server from ~500 to ~400 lines
and separates feed generation concerns from routing.
This commit is contained in:
2026-06-26 20:43:27 +02:00
parent 0ba6e379d7
commit 2da9ec9bba
2 changed files with 106 additions and 95 deletions
+104
View File
@@ -0,0 +1,104 @@
# frozen_string_literal: true
module Volumen
# RSS, JSON Feed, and sitemap helpers extracted from Server to keep the
# main class focused on configuration and routing.
module FeedHelpers
private
def feed_xml
site = config.site
items = published_posts.first(20).map { |post| feed_item(post) }.join("\n")
<<~XML
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>#{escape(site["title"])}</title>
<link>#{escape(base_url)}</link>
<description>#{escape(site["description"])}</description>
<language>#{escape(site["language"])}</language>
#{items}
</channel>
</rss>
XML
end
def feed_item(post)
link = "#{base_url}/#{post.slug}"
pub = if post.metadata["date"]
"\n <pubDate>#{escape(rfc822_date(post))}</pubDate>"
else
""
end
creator = post.fediverse_creator
creator_tag = creator ? "\n <dc:creator>#{escape(creator)}</dc:creator>" : ""
<<~XML.chomp
<item>
<title>#{escape(post.title)}</title>
<link>#{escape(link)}</link>
<guid>#{escape(link)}</guid>
<description>#{escape(post.excerpt)}</description>#{pub}#{creator_tag}
</item>
XML
end
def rfc822_date(post)
value = post.metadata["date"]
case value
when Date then value.to_time.rfc822
when Time then value.rfc822
end
end
def feed_json
site = config.site
feed_url = "#{base_url}/api/volumen/feed.json"
{
"version" => "https://jsonfeed.org/version/1.1",
"title" => site["title"],
"home_page_url" => base_url,
"feed_url" => feed_url,
"description" => site["description"],
"language" => site["language"],
"items" => published_posts.first(20).map { |post| json_feed_item(post) }
}
end
def json_feed_item(post)
link = "#{base_url}/#{post.slug}"
item = {
"id" => link,
"url" => link,
"title" => post.title,
"content_html" => post.html,
"summary" => post.excerpt,
"date_published" => iso_date(post.metadata["date"]),
"tags" => post.tags
}
author = post.fediverse_creator || site_fediverse_creator
item["authors"] = [{ "name" => author }] if author
item.reject { |_k, v| v.nil? || v == "" || v == [] }
end
def iso_date(value)
case value
when Date then value.iso8601
when Time then value.to_date.iso8601
end
end
def sitemap_xml
urls = published_posts.map do |post|
url = " <url><loc>#{escape("#{base_url}/#{post.slug}")}</loc>"
url << "<lastmod>#{escape(post.date_string)}</lastmod>" if post.date_string
url << "</url>"
end.join("\n")
<<~XML
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
#{urls}
</urlset>
XML
end
end
end
+2 -95
View File
@@ -10,6 +10,7 @@ require_relative "markdown"
require_relative "password"
require_relative "api_routes"
require_relative "admin_routes"
require_relative "feed_helpers"
module Volumen
# Sinatra application: the public JSON API at /api/volumen and the
@@ -25,6 +26,7 @@ module Volumen
register ApiRoutes
register AdminRoutes
helpers FeedHelpers
configure do
set :show_exceptions, false
@@ -167,101 +169,6 @@ module Volumen
config.site["base_url"].to_s.chomp("/")
end
def feed_xml
site = config.site
items = published_posts.first(20).map { |post| feed_item(post) }.join("\n")
<<~XML
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
<title>#{escape(site["title"])}</title>
<link>#{escape(base_url)}</link>
<description>#{escape(site["description"])}</description>
<language>#{escape(site["language"])}</language>
#{items}
</channel>
</rss>
XML
end
def feed_item(post)
link = "#{base_url}/#{post.slug}"
pub = if post.metadata["date"]
"\n <pubDate>#{escape(rfc822_date(post))}</pubDate>"
else
""
end
creator = post.fediverse_creator
creator_tag = creator ? "\n <dc:creator>#{escape(creator)}</dc:creator>" : ""
<<~XML.chomp
<item>
<title>#{escape(post.title)}</title>
<link>#{escape(link)}</link>
<guid>#{escape(link)}</guid>
<description>#{escape(post.excerpt)}</description>#{pub}#{creator_tag}
</item>
XML
end
def rfc822_date(post)
value = post.metadata["date"]
case value
when Date then value.to_time.rfc822
when Time then value.rfc822
end
end
def feed_json
site = config.site
feed_url = "#{base_url}/api/volumen/feed.json"
{
"version" => "https://jsonfeed.org/version/1.1",
"title" => site["title"],
"home_page_url" => base_url,
"feed_url" => feed_url,
"description" => site["description"],
"language" => site["language"],
"items" => published_posts.first(20).map { |post| json_feed_item(post) }
}
end
def json_feed_item(post)
link = "#{base_url}/#{post.slug}"
item = {
"id" => link,
"url" => link,
"title" => post.title,
"content_html" => post.html,
"summary" => post.excerpt,
"date_published" => iso_date(post.metadata["date"]),
"tags" => post.tags
}
author = post.fediverse_creator || site_fediverse_creator
item["authors"] = [{ "name" => author }] if author
item.reject { |_k, v| v.nil? || v == "" || v == [] }
end
def iso_date(value)
case value
when Date then value.iso8601
when Time then value.to_date.iso8601
end
end
def sitemap_xml
urls = published_posts.map do |post|
url = " <url><loc>#{escape("#{base_url}/#{post.slug}")}</loc>"
url << "<lastmod>#{escape(post.date_string)}</lastmod>" if post.date_string
url << "</url>"
end.join("\n")
<<~XML
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
#{urls}
</urlset>
XML
end
def escape(text)
Rack::Utils.escape_html(text.to_s)
end