Skip to content

Commit

Permalink
RSS/Podcast feeds: Invalidate cache when item count changed (#18)
Browse files Browse the repository at this point in the history
When new items were added, even if those items don’t have a later
modification date than the previous generated site, always re-generate
all RSS and podcast feeds.
  • Loading branch information
JohnSundell authored Jan 3, 2020
1 parent 37473e5 commit 105089c
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 17 deletions.
5 changes: 3 additions & 2 deletions Sources/Publish/Internal/PodcastFeedGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal struct PodcastFeedGenerator<Site: Website> where Site.ItemMetadata: Pod
let items = section.items.sorted(by: { $0.date > $1.date })

if let date = context.lastGenerationDate, let cache = oldCache {
if cache.config == config {
if cache.config == config, cache.itemCount == items.count {
let newlyModifiedItem = items.first { $0.lastModified > date }

guard newlyModifiedItem != nil else {
Expand All @@ -33,7 +33,7 @@ internal struct PodcastFeedGenerator<Site: Website> where Site.ItemMetadata: Pod
let feed = try makeFeed(containing: items, section: section)
.render(indentedBy: config.indentation)

let newCache = Cache(config: config, feed: feed)
let newCache = Cache(config: config, feed: feed, itemCount: items.count)
try cacheFile.write(newCache.encoded())
try outputFile.write(feed)
}
Expand All @@ -43,6 +43,7 @@ private extension PodcastFeedGenerator {
struct Cache: Codable {
let config: PodcastFeedConfiguration<Site>
let feed: String
let itemCount: Int
}

func makeFeed(containing items: [Item<Site>],
Expand Down
5 changes: 3 additions & 2 deletions Sources/Publish/Internal/RSSFeedGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ internal struct RSSFeedGenerator<Site: Website> {
items.sort { $0.date > $1.date }

if let date = context.lastGenerationDate, let cache = oldCache {
if cache.config == config {
if cache.config == config, cache.itemCount == items.count {
let newlyModifiedItem = items.first { $0.lastModified > date }

guard newlyModifiedItem != nil else {
Expand All @@ -38,7 +38,7 @@ internal struct RSSFeedGenerator<Site: Website> {

let feed = makeFeed(containing: items).render(indentedBy: config.indentation)

let newCache = Cache(config: config, feed: feed)
let newCache = Cache(config: config, feed: feed, itemCount: items.count)
try cacheFile.write(newCache.encoded())
try outputFile.write(feed)
}
Expand All @@ -48,6 +48,7 @@ private extension RSSFeedGenerator {
struct Cache: Codable {
let config: RSSFeedConfiguration
let feed: String
let itemCount: Int
}

func makeFeed(containing items: [Item<Site>]) -> RSS {
Expand Down
62 changes: 55 additions & 7 deletions Tests/PublishTests/Tests/PodcastFeedGenerationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,47 @@ final class PodcastFeedGenerationTests: PublishTestCase {

XCTAssertNotEqual(feedA, feedB)
}

func testNotReusingPreviousFeedIfItemWasAdded() throws {
let folder = try Folder.createTemporary()

let audio = try Audio(
url: require(URL(string: "https://audio.mp3")),
duration: Audio.Duration(),
byteSize: 55
)

let itemA = Item<Site>(
path: "a",
sectionID: .one,
metadata: .init(podcast: .init()),
content: Content(audio: audio)
)

let itemB = Item<Site>(
path: "b",
sectionID: .one,
metadata: .init(podcast: .init()),
content: Content(
lastModified: itemA.lastModified,
audio: audio
)
)

try generateFeed(in: folder, generationSteps: [
.addItem(itemA)
])

let feedA = try folder.file(at: "Output/feed.rss").readAsString()

try generateFeed(in: folder, generationSteps: [
.addItem(itemA),
.addItem(itemB)
])

let feedB = try folder.file(at: "Output/feed.rss").readAsString()
XCTAssertNotEqual(feedA, feedB)
}
}

extension PodcastFeedGenerationTests {
Expand All @@ -90,13 +131,15 @@ extension PodcastFeedGenerationTests {
("testOnlyIncludingSpecifiedSection", testOnlyIncludingSpecifiedSection),
("testConvertingRelativeLinksToAbsolute", testConvertingRelativeLinksToAbsolute),
("testReusingPreviousFeedIfNoItemsWereModified", testReusingPreviousFeedIfNoItemsWereModified),
("testNotReusingPreviousFeedIfConfigChanged", testNotReusingPreviousFeedIfConfigChanged)
("testNotReusingPreviousFeedIfConfigChanged", testNotReusingPreviousFeedIfConfigChanged),
("testNotReusingPreviousFeedIfItemWasAdded", testNotReusingPreviousFeedIfItemWasAdded)
]
}
}

private extension PodcastFeedGenerationTests {
typealias Configuration = PodcastFeedConfiguration<WebsiteStub.WithPodcastMetadata>
typealias Site = WebsiteStub.WithPodcastMetadata
typealias Configuration = PodcastFeedConfiguration<Site>

func makeConfigStub() throws -> Configuration {
try Configuration(
Expand All @@ -123,12 +166,17 @@ private extension PodcastFeedGenerationTests {
"""
}

func generateFeed(in folder: Folder,
config: Configuration? = nil,
date: Date = Date(),
content: [Path : String] = [:]) throws {
func generateFeed(
in folder: Folder,
config: Configuration? = nil,
generationSteps: [PublishingStep<Site>] = [
.addMarkdownFiles()
],
date: Date = Date(),
content: [Path : String] = [:]
) throws {
try publishWebsiteWithPodcast(in: folder, using: [
.addMarkdownFiles(),
.group(generationSteps),
.generatePodcastFeed(
for: .one,
config: config ?? makeConfigStub(),
Expand Down
40 changes: 34 additions & 6 deletions Tests/PublishTests/Tests/RSSFeedGenerationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,26 @@ final class RSSFeedGenerationTests: PublishTestCase {

XCTAssertNotEqual(feedA, feedB)
}

func testNotReusingPreviousFeedIfItemWasAdded() throws {
let folder = try Folder.createTemporary()
let itemA = Item.stub()
let itemB = Item.stub().setting(\.lastModified, to: itemA.lastModified)

try generateFeed(in: folder, generationSteps: [
.addItem(itemA)
])

let feedA = try folder.file(at: "Output/feed.rss").readAsString()

try generateFeed(in: folder, generationSteps: [
.addItem(itemA),
.addItem(itemB)
])

let feedB = try folder.file(at: "Output/feed.rss").readAsString()
XCTAssertNotEqual(feedA, feedB)
}
}

extension RSSFeedGenerationTests {
Expand All @@ -84,18 +104,26 @@ extension RSSFeedGenerationTests {
("testOnlyIncludingSpecifiedSections", testOnlyIncludingSpecifiedSections),
("testConvertingRelativeLinksToAbsolute", testConvertingRelativeLinksToAbsolute),
("testReusingPreviousFeedIfNoItemsWereModified", testReusingPreviousFeedIfNoItemsWereModified),
("testNotReusingPreviousFeedIfConfigChanged", testNotReusingPreviousFeedIfConfigChanged)
("testNotReusingPreviousFeedIfConfigChanged", testNotReusingPreviousFeedIfConfigChanged),
("testNotReusingPreviousFeedIfItemWasAdded", testNotReusingPreviousFeedIfItemWasAdded)
]
}
}

private extension RSSFeedGenerationTests {
func generateFeed(in folder: Folder,
config: RSSFeedConfiguration = .default,
date: Date = Date(),
content: [Path : String] = [:]) throws {
typealias Site = WebsiteStub.WithoutItemMetadata

func generateFeed(
in folder: Folder,
config: RSSFeedConfiguration = .default,
generationSteps: [PublishingStep<Site>] = [
.addMarkdownFiles()
],
date: Date = Date(),
content: [Path : String] = [:]
) throws {
try publishWebsite(in: folder, using: [
.addMarkdownFiles(),
.group(generationSteps),
.generateRSSFeed(
including: [.one],
config: config,
Expand Down

0 comments on commit 105089c

Please sign in to comment.