Simplify Feed to a module that returns an array
Episode is now a top-level Data class in the CookingIssues module.
Assisted-by: Claude Opus 4.6 via pi
diff --git a/Rakefile b/Rakefile
index d4e66cc..d44d2cf 100644
--- a/Rakefile
+++ b/Rakefile
@@ -21,8 +21,8 @@ file HRN_FEED => CACHE_DIR do
File.write(HRN_FEED, response.body)
end
-def feed
- @feed ||= CookingIssues::Feed.new(HRN_FEED)
+def episodes
+ @episodes ||= CookingIssues::Feed.parse(HRN_FEED)
end
# --- Tasks ---
@@ -31,7 +31,6 @@ task default: :sync
desc "Fetch the feed, download episodes missing transcripts"
task sync: HRN_FEED do
- episodes = feed.episodes
puts "Found #{episodes.size} episodes in feed."
sync = CookingIssues::Sync.new(episodes)
@@ -40,7 +39,7 @@ end
desc "List all episodes from the feed"
task episodes: HRN_FEED do
- feed.episodes.sort_by(&:number).each do |ep|
+ episodes.sort_by(&:number).each do |ep|
status = Dir.glob(File.join(TRANSCRIPTS_DIR, "#{ep.slug}.*")).any? ? "✓" : " "
puts "[#{status}] #{ep.number}. #{ep.title}"
end
@@ -50,8 +49,9 @@ desc "Transcribe an episode by number (e.g., rake transcribe[42])"
task :transcribe, [:number] => HRN_FEED do |_t, args|
abort "Usage: rake transcribe[NUMBER]" unless args[:number]
- ep = feed[args[:number].to_i]
+ ep = episodes.find { |e| e.number == args[:number].to_i }
abort "Episode #{args[:number]} not found in feed." unless ep
+
audio = File.join(AUDIO_DIR, "#{ep.slug}.mp3")
abort "Audio not found: #{audio}\nRun `rake sync` first." unless File.exist?(audio)
diff --git a/lib/feed.rb b/lib/feed.rb
index 2619dbd..6e259f8 100644
--- a/lib/feed.rb
+++ b/lib/feed.rb
@@ -1,41 +1,26 @@
require "nokogiri"
module CookingIssues
- # Parses a podcast RSS feed and returns episode data.
- class Feed
- Episode = Data.define(:number, :title, :published_at, :audio_url) do
- def slug
- formatted_number = format("%03d", number)
- safe_title = title
- .downcase
- .gsub(/[^a-z0-9\s-]/, "")
- .gsub(/\s+/, "-")
- .gsub(/-+/, "-")
- .sub(/-$/, "")
- "#{formatted_number}-#{safe_title}"
- end
+ Episode = Data.define(:number, :title, :published_at, :audio_url) do
+ def slug
+ formatted_number = format("%03d", number)
+ safe_title = title
+ .downcase
+ .gsub(/[^a-z0-9\s-]/, "")
+ .gsub(/\s+/, "-")
+ .gsub(/-+/, "-")
+ .sub(/-$/, "")
+ "#{formatted_number}-#{safe_title}"
end
+ end
- def initialize(path)
- @path = path
- end
-
- def episodes
- @episodes ||= parse(File.read(@path))
- end
-
- def [](number)
- episodes.find { |e| e.number == number }
- end
-
- private
-
- def parse(xml)
- doc = Nokogiri::XML(xml)
+ module Feed
+ def self.parse(path)
+ doc = Nokogiri::XML(File.read(path))
doc.xpath("//item").filter_map { |item| parse_item(item) }
end
- def parse_item(item)
+ def self.parse_item(item)
number = item.at_xpath("itunes:episode")&.text&.to_i
return nil unless number && number > 0
@@ -46,5 +31,7 @@ module CookingIssues
audio_url: item.at_xpath("enclosure")&.[]("url")
)
end
+
+ private_class_method :parse_item
end
end