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
change oyyosywtsnoswxlklqxzrskovrxknqrs
commit f39fed6e287cd1af633afa12b4b928c578fc6998
author Alpha Chen <alpha@kejadlen.dev>
date
parent ulkzspyk
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