feat: add fediverse_creator handle for Mastodon attribution

Expose a per-post fediverse_creator frontmatter field plus a site-wide
default in config.toml ([site].fediverse_creator). The handle is returned
in the post and site payloads, rendered as <dc:creator> in the RSS feed
and as authors[].name in the JSON feed, so a front-end can drop it into
<meta name="fediverse:creator"> on rendered pages. Per-post value
overrides the site default. Validated against a simple @user@host regex.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-06-26 17:40:48 +02:00
co-authored by Qwen-Coder
parent 1440e9a1fd
commit 5437cef1cd
10 changed files with 250 additions and 8 deletions
+57
View File
@@ -77,4 +77,61 @@ class PostTest < Minitest::Test
post = Volumen::Post.new(metadata: {})
refute_predicate post, :scheduled?
end
def test_fediverse_creator_returns_metadata_value
post = Volumen::Post.new(
metadata: { "slug" => "x", "fediverse_creator" => "@user@example.social" }
)
assert_equal "@user@example.social", post.fediverse_creator
end
def test_fediverse_creator_is_nil_when_missing
post = Volumen::Post.new(metadata: { "slug" => "x" })
assert_nil post.fediverse_creator
end
def test_fediverse_creator_is_nil_when_blank
post = Volumen::Post.new(metadata: { "slug" => "x", "fediverse_creator" => " " })
assert_nil post.fediverse_creator
end
def test_fediverse_creator_validation_accepts_well_formed_handles
post = Volumen::Post.new(
metadata: { "slug" => "x", "fediverse_creator" => "@ada@lovelace.org" }
)
assert_predicate post, :valid_fediverse_creator?
end
def test_fediverse_creator_validation_rejects_malformed_handles
[
"ada@example.com",
"@only-one-at",
"@user@example.com extra",
"@user@",
"@@example.com"
].each do |value|
post = Volumen::Post.new(metadata: { "slug" => "x", "fediverse_creator" => value })
refute_predicate post, :valid_fediverse_creator?, "expected #{value.inspect} to be invalid"
end
end
def test_fediverse_creator_validation_accepts_padded_value
post = Volumen::Post.new(
metadata: { "slug" => "x", "fediverse_creator" => " @user@example.com " }
)
assert_predicate post, :valid_fediverse_creator?
end
def test_summary_includes_fediverse_creator
post = Volumen::Post.new(
metadata: { "slug" => "x", "fediverse_creator" => "@user@example.social" },
body: "body"
)
assert_equal "@user@example.social", post.summary["fediverse_creator"]
end
def test_summary_omits_fediverse_creator_when_absent
post = Volumen::Post.new(metadata: { "slug" => "x" }, body: "body")
assert_nil post.summary["fediverse_creator"]
end
end