mirror of
https://github.com/samsonjs/samhuri.net.git
synced 2026-03-25 09:05:47 +00:00
Derive site metadata from posts and config
This commit is contained in:
parent
54a0543a7f
commit
ca316bf470
10 changed files with 169 additions and 21 deletions
|
|
@ -141,10 +141,18 @@ module Pressa
|
|||
entries.map.with_index do |item, index|
|
||||
case item
|
||||
when String
|
||||
validate_asset_path!(
|
||||
item,
|
||||
context: "#{context}[#{index}]"
|
||||
)
|
||||
Script.new(src: item, defer: true)
|
||||
when Hash
|
||||
src = item["src"]
|
||||
raise ValidationError, "Expected #{context}[#{index}].src to be a String" unless src.is_a?(String) && !src.empty?
|
||||
validate_asset_path!(
|
||||
src,
|
||||
context: "#{context}[#{index}].src"
|
||||
)
|
||||
|
||||
defer = item.key?("defer") ? item["defer"] : true
|
||||
unless [true, false].include?(defer)
|
||||
|
|
@ -164,10 +172,18 @@ module Pressa
|
|||
entries.map.with_index do |item, index|
|
||||
case item
|
||||
when String
|
||||
validate_asset_path!(
|
||||
item,
|
||||
context: "#{context}[#{index}]"
|
||||
)
|
||||
Stylesheet.new(href: item)
|
||||
when Hash
|
||||
href = item["href"]
|
||||
raise ValidationError, "Expected #{context}[#{index}].href to be a String" unless href.is_a?(String) && !href.empty?
|
||||
validate_asset_path!(
|
||||
href,
|
||||
context: "#{context}[#{index}].href"
|
||||
)
|
||||
|
||||
Stylesheet.new(href:)
|
||||
else
|
||||
|
|
@ -190,6 +206,12 @@ module Pressa
|
|||
normalized = value.start_with?("/") ? value : "/#{value}"
|
||||
"#{site_url}#{normalized}"
|
||||
end
|
||||
|
||||
def validate_asset_path!(value, context:)
|
||||
return if value.start_with?("/", "http://", "https://")
|
||||
|
||||
raise ValidationError, "Expected #{context} to start with / or use http(s) scheme"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -86,6 +86,10 @@ module Pressa
|
|||
def recent_posts(limit = 10)
|
||||
all_posts.take(limit)
|
||||
end
|
||||
|
||||
def earliest_year
|
||||
by_year.keys.min
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ module Pressa
|
|||
attribute :description, Types::String
|
||||
attribute :url, Types::String
|
||||
attribute :image_url, Types::String.optional.default(nil)
|
||||
attribute :copyright_start_year, Types::Integer.optional.default(nil)
|
||||
attribute :scripts, Types::Array.of(Script).default([].freeze)
|
||||
attribute :styles, Types::Array.of(Stylesheet).default([].freeze)
|
||||
attribute :plugins, Types::Array.default([].freeze)
|
||||
|
|
|
|||
|
|
@ -15,8 +15,10 @@ module Pressa
|
|||
FileUtils.rm_rf(target_path)
|
||||
FileUtils.mkdir_p(target_path)
|
||||
|
||||
site.plugins.each { |plugin| plugin.setup(site:, source_path:) }
|
||||
setup_site = site
|
||||
setup_site.plugins.each { |plugin| plugin.setup(site: setup_site, source_path:) }
|
||||
|
||||
@site = site_with_copyright_start_year(setup_site)
|
||||
site.plugins.each { |plugin| plugin.render(site:, target_path:) }
|
||||
|
||||
copy_static_files(source_path, target_path)
|
||||
|
|
@ -99,5 +101,23 @@ module Pressa
|
|||
basename = File.basename(source_file)
|
||||
basename.start_with?(".")
|
||||
end
|
||||
|
||||
def site_with_copyright_start_year(base_site)
|
||||
start_year = find_copyright_start_year(base_site)
|
||||
Site.new(**base_site.to_h.merge(copyright_start_year: start_year))
|
||||
end
|
||||
|
||||
def find_copyright_start_year(base_site)
|
||||
years = base_site.plugins.filter_map do |plugin|
|
||||
next unless plugin.respond_to?(:posts_by_year)
|
||||
|
||||
posts_by_year = plugin.posts_by_year
|
||||
next unless posts_by_year.respond_to?(:earliest_year)
|
||||
|
||||
posts_by_year.earliest_year
|
||||
end
|
||||
|
||||
years.min || Time.now.year
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -4,8 +4,6 @@ require "pressa/views/icons"
|
|||
module Pressa
|
||||
module Views
|
||||
class Layout < Phlex::HTML
|
||||
START_YEAR = 2006
|
||||
|
||||
attr_reader :site,
|
||||
:page_subtitle,
|
||||
:page_description,
|
||||
|
|
@ -34,10 +32,6 @@ module Pressa
|
|||
@content = content
|
||||
end
|
||||
|
||||
def format_output?
|
||||
true
|
||||
end
|
||||
|
||||
def view_template
|
||||
doctype
|
||||
|
||||
|
|
@ -172,7 +166,7 @@ module Pressa
|
|||
|
||||
def render_footer
|
||||
footer do
|
||||
plain "© #{START_YEAR} - #{Time.now.year} "
|
||||
plain "© #{footer_years} "
|
||||
a(href: site.url_for("/about")) { site.author }
|
||||
end
|
||||
end
|
||||
|
|
@ -201,6 +195,14 @@ module Pressa
|
|||
normalized = path.start_with?("/") ? path : "/#{path}"
|
||||
site.url_for(normalized)
|
||||
end
|
||||
|
||||
def footer_years
|
||||
current_year = Time.now.year
|
||||
start_year = site.copyright_start_year || current_year
|
||||
return current_year.to_s if start_year >= current_year
|
||||
|
||||
"#{start_year} - #{current_year}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@ description = "Sami Samhuri's blog about programming, mainly about iOS and Ruby
|
|||
url = "https://samhuri.net"
|
||||
image_url = "/images/me.jpg"
|
||||
scripts = []
|
||||
styles = ["css/normalize.css", "css/style.css", "css/syntax.css"]
|
||||
styles = ["/css/normalize.css", "/css/style.css", "/css/syntax.css"]
|
||||
plugins = ["posts", "projects"]
|
||||
|
||||
[projects_plugin]
|
||||
scripts = [
|
||||
"https://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js",
|
||||
"js/gitter.js",
|
||||
"js/store.js",
|
||||
"js/projects.js"
|
||||
"/js/gitter.js",
|
||||
"/js/store.js",
|
||||
"/js/projects.js"
|
||||
]
|
||||
styles = []
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ class Pressa::Config::LoaderTest < Minitest::Test
|
|||
assert_equal("Sami Samhuri", site.author)
|
||||
assert_equal("https://samhuri.net", site.url)
|
||||
assert_equal("https://samhuri.net/images/me.jpg", site.image_url)
|
||||
assert_equal(["css/style.css"], site.styles.map(&:href))
|
||||
assert_equal(["/css/style.css"], site.styles.map(&:href))
|
||||
|
||||
projects_plugin = site.plugins.find { |plugin| plugin.is_a?(Pressa::Projects::Plugin) }
|
||||
refute_nil(projects_plugin)
|
||||
assert_equal(["js/projects.js"], projects_plugin.scripts.map(&:src))
|
||||
assert_equal(["/js/projects.js"], projects_plugin.scripts.map(&:src))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -338,6 +338,25 @@ class Pressa::Config::LoaderTest < Minitest::Test
|
|||
end
|
||||
end
|
||||
|
||||
def test_build_site_rejects_non_absolute_local_asset_paths
|
||||
Dir.mktmpdir do |dir|
|
||||
File.write(File.join(dir, "site.toml"), <<~TOML)
|
||||
author = "Sami Samhuri"
|
||||
email = "sami@samhuri.net"
|
||||
title = "samhuri.net"
|
||||
description = "blog"
|
||||
url = "https://samhuri.net"
|
||||
scripts = ["js/site.js"]
|
||||
styles = ["css/site.css"]
|
||||
TOML
|
||||
File.write(File.join(dir, "projects.toml"), "")
|
||||
|
||||
loader = Pressa::Config::Loader.new(source_path: dir)
|
||||
error = assert_raises(Pressa::Config::ValidationError) { loader.build_site }
|
||||
assert_match(%r{start with / or use http\(s\) scheme}, error.message)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_temp_config
|
||||
|
|
@ -350,11 +369,11 @@ class Pressa::Config::LoaderTest < Minitest::Test
|
|||
url = "https://samhuri.net"
|
||||
image_url = "/images/me.jpg"
|
||||
scripts = []
|
||||
styles = ["css/style.css"]
|
||||
styles = ["/css/style.css"]
|
||||
plugins = ["posts", "projects"]
|
||||
|
||||
[projects_plugin]
|
||||
scripts = ["js/projects.js"]
|
||||
scripts = ["/js/projects.js"]
|
||||
styles = []
|
||||
TOML
|
||||
|
||||
|
|
|
|||
|
|
@ -70,7 +70,13 @@ class Pressa::Posts::ModelsTest < Minitest::Test
|
|||
assert_equal([11, 10], year_2025.sorted_months.map { |mp| mp.month.number })
|
||||
assert_equal([regular_post, link_post], year_2025.all_posts)
|
||||
assert_equal([2025, 2024], posts_by_year.sorted_years)
|
||||
assert_equal(2024, posts_by_year.earliest_year)
|
||||
assert_equal(3, posts_by_year.all_posts.length)
|
||||
assert_equal([regular_post], posts_by_year.recent_posts(1))
|
||||
end
|
||||
|
||||
def test_posts_by_year_earliest_year_is_nil_for_empty_collection
|
||||
posts_by_year = Pressa::Posts::PostsByYear.new(by_year: {})
|
||||
assert_nil(posts_by_year.earliest_year)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -20,6 +20,20 @@ class Pressa::SiteGeneratorRenderingTest < Minitest::Test
|
|||
end
|
||||
end
|
||||
|
||||
class PostsPluginSpy < PluginSpy
|
||||
attr_reader :posts_by_year, :render_site_year
|
||||
|
||||
def initialize(posts_by_year:)
|
||||
super()
|
||||
@posts_by_year = posts_by_year
|
||||
end
|
||||
|
||||
def render(site:, target_path:)
|
||||
@render_site_year = site.copyright_start_year
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
class MarkdownRendererSpy
|
||||
attr_reader :calls
|
||||
|
||||
|
|
@ -51,6 +65,27 @@ class Pressa::SiteGeneratorRenderingTest < Minitest::Test
|
|||
)
|
||||
end
|
||||
|
||||
def build_posts_by_year(year:)
|
||||
post = Pressa::Posts::Post.new(
|
||||
slug: "first-post",
|
||||
title: "First Post",
|
||||
author: "Sami Samhuri",
|
||||
date: DateTime.parse("#{year}-02-01T10:00:00-08:00"),
|
||||
formatted_date: "1st February, #{year}",
|
||||
body: "<p>First post</p>",
|
||||
excerpt: "First post...",
|
||||
path: "/posts/#{year}/02/first-post"
|
||||
)
|
||||
|
||||
month_posts = Pressa::Posts::MonthPosts.new(
|
||||
month: Pressa::Posts::Month.new(name: "February", number: 2, padded: "02"),
|
||||
posts: [post]
|
||||
)
|
||||
|
||||
year_posts = Pressa::Posts::YearPosts.new(year:, by_month: {2 => month_posts})
|
||||
Pressa::Posts::PostsByYear.new(by_year: {year => year_posts})
|
||||
end
|
||||
|
||||
def test_generate_runs_plugins_copies_static_files_and_renders_supported_files
|
||||
Dir.mktmpdir do |root|
|
||||
source_path = File.join(root, "source")
|
||||
|
|
@ -110,4 +145,20 @@ class Pressa::SiteGeneratorRenderingTest < Minitest::Test
|
|||
assert_empty(renderer.calls)
|
||||
end
|
||||
end
|
||||
|
||||
def test_generate_sets_copyright_start_year_from_earliest_post_year
|
||||
Dir.mktmpdir do |root|
|
||||
source_path = File.join(root, "source")
|
||||
target_path = File.join(root, "target")
|
||||
FileUtils.mkdir_p(source_path)
|
||||
|
||||
plugin = PostsPluginSpy.new(posts_by_year: build_posts_by_year(year: 2006))
|
||||
renderer = MarkdownRendererSpy.new
|
||||
site = build_site(plugin:, renderer:)
|
||||
|
||||
Pressa::SiteGenerator.new(site:).generate(source_path:, target_path:)
|
||||
|
||||
assert_equal(2006, plugin.render_site_year)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -21,6 +21,17 @@ class Pressa::Views::LayoutTest < Minitest::Test
|
|||
)
|
||||
end
|
||||
|
||||
def site_with_copyright_start_year(year)
|
||||
Pressa::Site.new(
|
||||
author: "Sami Samhuri",
|
||||
email: "sami@samhuri.net",
|
||||
title: "samhuri.net",
|
||||
description: "blog",
|
||||
url: "https://samhuri.net",
|
||||
copyright_start_year: year
|
||||
)
|
||||
end
|
||||
|
||||
def test_rendering_child_components_as_html_instead_of_escaped_text
|
||||
html = Pressa::Views::Layout.new(
|
||||
site:,
|
||||
|
|
@ -64,13 +75,25 @@ class Pressa::Views::LayoutTest < Minitest::Test
|
|||
assert_includes(html, %(<link rel="stylesheet" type="text/css" href="https://cdn.example.com/site.css">))
|
||||
end
|
||||
|
||||
def test_format_output_is_enabled
|
||||
layout = Pressa::Views::Layout.new(
|
||||
site:,
|
||||
def test_footer_renders_year_range_using_copyright_start_year
|
||||
html = Pressa::Views::Layout.new(
|
||||
site: site_with_copyright_start_year(2006),
|
||||
canonical_url: "https://samhuri.net/posts/",
|
||||
content: content_view
|
||||
)
|
||||
).call
|
||||
|
||||
assert(layout.format_output?)
|
||||
assert_includes(html, "<footer>© 2006 - #{Time.now.year} <a href=\"https://samhuri.net/about\">Sami Samhuri</a></footer>")
|
||||
end
|
||||
|
||||
def test_footer_renders_single_year_when_start_year_matches_current_year
|
||||
current_year = Time.now.year
|
||||
html = Pressa::Views::Layout.new(
|
||||
site: site_with_copyright_start_year(current_year),
|
||||
canonical_url: "https://samhuri.net/posts/",
|
||||
content: content_view
|
||||
).call
|
||||
|
||||
assert_includes(html, "<footer>© #{current_year} <a href=\"https://samhuri.net/about\">Sami Samhuri</a></footer>")
|
||||
refute_includes(html, "<footer>© #{current_year} - #{current_year} ")
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue