fix: derive a title from the URL when an external post has none - #12
Merged
Conversation
A degraded fetch left external posts with an empty title. `fetch_content_from_url` returns `''` when the request fails or the page has no `<title>`, and RSS items can carry a blank `<title>` of their own; `create_document` copied that straight into `doc.data['title']`. The result in a real build of the al-folio starter was a blank but clickable row in the blog index and on the home page, an untitled entry in the search index (`id: "post-"`), and 103 `Warning: Empty \`slug\` generated for ''.` lines from Jekyll slugifying the empty title downstream. #10 made `build_slug` nil-safe, which stopped the build aborting with a NoMethodError, and #11 optimized the slug pass. Neither filled in the title, so the failure mode went from "build crashes" to "build silently publishes a blank post". This fixes the remaining half. These URLs are listed in `_config.yml` by the user, so the entry is kept rather than dropped: `resolve_title` derives a readable title from the last meaningful path segment of the URL and logs a warning naming that URL, so the degradation is visible. Trailing slashes, query strings, fragments, percent-escapes, page extensions and segments with no words of their own (ids, `/2024/05/` date parts) are all handled; a bare domain falls back to its host. It never raises and never returns an empty string. The slug still comes from the raw title, so post URLs are unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The defect
A degraded fetch left external posts with an empty title, which was then published.
fetch_content_from_urlreturns''when the request fails or the page has no<title>, and an RSS<item>can carry a blank<title>of its own.create_documentcopied that straight intodoc.data['title'].Reproduced in a real build of the al-folio starter, where the
Google Blogentry inexternal_sourcesproduced:a blank but clickable row in the blog index and on the home page, an untitled entry in the search index (
id: "post-"), and 103Warning: Empty `slug` generated for ''.lines from Jekyll (jekyll/utils.rb:222) slugifying that empty title downstream. Withexternal_sources: []the same build emits 0 such warnings.How we got here
build_slugnil-safe, fixing aNoMethodErrorthat previously aborted the whole build on a nil title.Neither is wrong, but neither filled in the title. The failure mode went from "build crashes" to "build silently publishes a blank post". This PR fixes the remaining half. (
al_ext_postshas its own privateslugifyand never callsJekyll::Utils.slugify; the Jekyll warnings come from Jekyll slugifying the synthetic posts' empty titles downstream, so fixing the title fixes the warnings too.)The fix
Derive rather than skip. These URLs are explicitly listed by the user in
_config.yml, so dropping them would silently lose content they asked for. The entry is kept, made readable, and the degradation is reported.resolve_title(lib/al_ext_posts.rb:113) returns the title when it carries any word character, and otherwise derives one from the URL's last meaningful path segment and logs a warning naming the URL:It is called from
create_document, the single funnel both the RSS path (fetch_from_rss→process_entries) and the explicit-URL path (fetch_from_urls) pass through, so an RSS item with a missing or blank<title>gets exactly the same treatment.Post slugs, and therefore post URLs, are unchanged:
build_slugstill receives the raw title and still uses its source-name fallback. The built sitemap is byte-identical across the before/after builds.The usability check for a display title is
/[[:word:]]/(Unicode-aware), not the ASCII\wthatbuild_sluguses for a filesystem slug — so a CJK title like你好世界is preserved as the title even though the slug still falls back.Edge cases
.../google-gemini-update-flash-ai-assistant-io-2024/Google Gemini Update Flash Ai Assistant Io 2024https://example.com/posts/my-post/(trailing slash)My Post.../my-post.html?utm_source=x#intro(extension, query, fragment)My Post.../hello%20world%21(percent-encoding)Hello World.../my_post_titleMy Post Titlehttps://example.com/archive/2024/05/deep-dive/Deep Dive(numeric segments skipped)https://example.com/posts/12345(numeric last segment)Postshttps://example.com/posts/---/(no words in last segment)Postshttps://example.com/post.v2(unknown extension kept)Post V2https://blog.google(domain only)blog.googlehttps://www.example.com/example.comhttps://example.com/////(empty segments only)example.comhttps://example.com/2024/05/12/(no segment carries words)example.comnil,''External postMalformed input never raises:
URI::Errorfalls back to trimming the query/fragment by hand, bad percent-escapes keep the raw segment, and invalid UTF-8 bytes are scrubbed before any regexp match. The function cannot return an empty string.Verification
Rebuilt the al-folio starter against this branch, in a sandbox with no general outbound network — a faithful simulation of the degraded-fetch case.
Empty `slug` generatedwarningspost-titleanchors inblog/index.htmlThe blog index now renders:
The home page
news-titleanchor and the search-index entry (id: "post-"→id: "post-google-gemini-update-flash-ai-assistant-io-2024", with a real title) are fixed by the same change. The sitemap is unchanged, confirming no URL churn.Tests
Added coverage for a missing title, a blank/whitespace title, a title of only non-word characters, a preserved non-Latin title, the warning naming the URL, every URL edge case above, a never-empty/never-raises sweep over malformed URLs, an end-to-end
create_documentassertion (title filled, slug unchanged), and the RSS path viaprocess_entries.CaptureGenerator#initializenow accepts and ignores an argument, becauseJekyll::Site#setupinstantiates everyGeneratorsubclass with the site config once a test builds a real site.npm run lint:prettierpasses.Release note
Version bumped 1.0.2 → 1.0.3. 1.0.2 was never published to RubyGems —
mainis ahead of the published 1.0.1 — so the eventual release should cover both the 1.0.2 slug-generation optimization (#11) and this 1.0.3 fix.Generated by Claude Code