samhuri.net/lib/pressa/posts/writer.rb
Sami Samhuri e43bb0a71b Add tag tooling: bake tags report and unlinked /tags/ pages
Adds Posts::TagIndex (counts, per-tag posts, per-tag/year breakdown)
and Posts::TagReport, wired into a new `bake tags` task that prints a
frequency table plus a tag-by-year sparkline straight to the terminal.

Also generates /tags/ and /tags/<tag>/ HTML pages via the existing
Posts plugin, listing tags with post counts and per-tag post listings.
Not linked from nav yet -- reachable by URL only until tags are
curated enough to surface properly.

Extracted PostListItemView out of YearPostsView so the post-link
rendering (incl. link posts) is shared with the new tag pages instead
of duplicated.
2026-06-21 20:43:30 -07:00

179 lines
5.5 KiB
Ruby

require "pressa/utils/file_writer"
require "pressa/views/layout"
require "pressa/views/post_view"
require "pressa/views/recent_posts_view"
require "pressa/views/archive_view"
require "pressa/views/year_posts_view"
require "pressa/views/month_posts_view"
require "pressa/views/tags_index_view"
require "pressa/views/tag_posts_view"
require "pressa/posts/tag_index"
module Pressa
module Posts
class PostWriter
def initialize(site:, posts_by_year:)
@site = site
@posts_by_year = posts_by_year
end
def write_posts(target_path:)
@posts_by_year.all_posts.each do |post|
write_post(post:, target_path:)
end
end
def write_recent_posts(target_path:, limit: 10)
recent = @posts_by_year.recent_posts(limit)
content_view = Views::RecentPostsView.new(posts: recent, site: @site)
html = render_layout(
page_subtitle: nil,
canonical_url: @site.url,
content: content_view,
page_description: "Recent posts",
page_type: "article"
)
file_path = File.join(target_path, "index.html")
Utils::FileWriter.write(path: file_path, content: html)
end
def write_posts_archive(target_path:)
content_view = Views::ArchiveView.new(posts_by_year: @posts_by_year, site: @site)
html = render_layout(
page_subtitle: "Posts",
canonical_url: @site.url_for("/posts/"),
content: content_view,
page_description: "Archive of all posts"
)
file_path = File.join(target_path, "posts", "index.html")
Utils::FileWriter.write(path: file_path, content: html)
end
def write_year_indexes(target_path:)
@posts_by_year.sorted_years.each do |year|
year_posts = @posts_by_year.by_year[year]
write_year_index(year:, year_posts:, target_path:)
end
end
def write_month_rollups(target_path:)
@posts_by_year.by_year.each do |year, year_posts|
year_posts.by_month.each do |_month_num, month_posts|
write_month_rollup(year:, month_posts:, target_path:)
end
end
end
def write_tags_index(target_path:)
content_view = Views::TagsIndexView.new(tag_index:, site: @site)
html = render_layout(
page_subtitle: "Tags",
canonical_url: @site.url_for("/tags/"),
content: content_view,
page_description: "Browse posts by tag"
)
file_path = File.join(target_path, "tags", "index.html")
Utils::FileWriter.write(path: file_path, content: html)
end
def write_tag_pages(target_path:)
tag_index.tags.each do |tag|
write_tag_page(tag:, target_path:)
end
end
private
def tag_index
@tag_index ||= Posts::TagIndex.from_posts_by_year(@posts_by_year)
end
def write_tag_page(tag:, target_path:)
content_view = Views::TagPostsView.new(tag:, posts: tag_index.posts_for(tag), site: @site)
slug = tag_index.slug(tag)
html = render_layout(
page_subtitle: "Tag: #{tag}",
canonical_url: @site.url_for("/tags/#{slug}/"),
content: content_view,
page_description: "Posts tagged #{tag}"
)
file_path = File.join(target_path, "tags", slug, "index.html")
Utils::FileWriter.write(path: file_path, content: html)
end
def write_post(post:, target_path:)
content_view = Views::PostView.new(post:, site: @site, article_class: "container")
html = render_layout(
page_subtitle: post.title,
canonical_url: @site.url_for(post.path),
content: content_view,
page_description: post.excerpt,
page_type: "article"
)
file_path = File.join(target_path, post.path.sub(/^\//, ""), "index.html")
Utils::FileWriter.write(path: file_path, content: html)
end
def write_year_index(year:, year_posts:, target_path:)
content_view = Views::YearPostsView.new(year:, year_posts:, site: @site)
html = render_layout(
page_subtitle: year.to_s,
canonical_url: @site.url_for("/posts/#{year}/"),
content: content_view,
page_description: "Archive of all posts from #{year}",
page_type: "article"
)
file_path = File.join(target_path, "posts", year.to_s, "index.html")
Utils::FileWriter.write(path: file_path, content: html)
end
def write_month_rollup(year:, month_posts:, target_path:)
month = month_posts.month
content_view = Views::MonthPostsView.new(year:, month_posts:, site: @site)
title = "#{month.name} #{year}"
html = render_layout(
page_subtitle: title,
canonical_url: @site.url_for("/posts/#{year}/#{month.padded}/"),
content: content_view,
page_description: "Archive of all posts from #{title}",
page_type: "article"
)
file_path = File.join(target_path, "posts", year.to_s, month.padded, "index.html")
Utils::FileWriter.write(path: file_path, content: html)
end
def render_layout(
page_subtitle:,
canonical_url:,
content:,
page_description: nil,
page_type: "website"
)
layout = Views::Layout.new(
site: @site,
page_subtitle:,
canonical_url:,
page_description:,
page_type:,
content:
)
layout.call
end
end
end
end