From 6b1b5c1fc59a52fce255409a1a4e7f9bb6e3f2df Mon Sep 17 00:00:00 2001 From: Gustavo Ribeiro Date: Wed, 8 Nov 2023 20:31:53 -0300 Subject: [PATCH] Refactor `FeedParser` to use lambdas --- lib/pod/infrastructure/feed_parser.rb | 50 ++++++++++++++++++--- lib/pod/infrastructure/function_pipeline.rb | 42 +++++++++++++++++ 2 files changed, 86 insertions(+), 6 deletions(-) create mode 100644 lib/pod/infrastructure/function_pipeline.rb diff --git a/lib/pod/infrastructure/feed_parser.rb b/lib/pod/infrastructure/feed_parser.rb index d48de10..cbebcb2 100644 --- a/lib/pod/infrastructure/feed_parser.rb +++ b/lib/pod/infrastructure/feed_parser.rb @@ -4,25 +4,57 @@ require "feedjira" require_relative "dto" +require_relative "function_pipeline" module Infrastructure module FeedParser def self.call(feed) + pipeline = FunctionPipeline.new( + functions: [ + FetchContent, + ParseContent, + InitializePodcastDTO, + InitializeEpisodesDTO, + InitializeFeedDTO + ], + on_error: InitializeEmptyFeedDTO, + context: {feed: feed} + ) + + pipeline.call + end + + private + + FetchContent = ->(ctx) do + feed = ctx[:feed] + content = if File.exist?(feed) File.new(feed).read else Net::HTTP.get(URI(feed)) end - parsed_content = Feedjira.parse(content) - podcast = Infrastructure::DTO.new( + ctx[:content] = content + end + + ParseContent = ->(ctx) do + ctx[:parsed_content] = Feedjira.parse(ctx[:content]) + end + + InitializePodcastDTO = ->(ctx) do + parsed_content = ctx[:parsed_content] + ctx[:podcast] = Infrastructure::DTO.new( name: parsed_content.title, description: parsed_content.description, feed: parsed_content.itunes_new_feed_url, website: parsed_content.url ) + end - episodes = parsed_content.entries.map do |e| + InitializeEpisodesDTO = ->(ctx) do + parsed_content = ctx[:parsed_content] + ctx[:episodes] = parsed_content.entries.map do |e| Infrastructure::DTO.new( title: e.title, release_date: e.published.iso8601, @@ -31,11 +63,17 @@ def self.call(feed) external_id: e.entry_id ) end + end + InitializeFeedDTO = ->(ctx) do + podcast = ctx[:podcast] + episodes = ctx[:episodes] # The #reverse is necessary to put the oldest episodes on the top of the feed. - Infrastructure::DTO.new(podcast: podcast, episodes: episodes.reverse) - rescue NoMethodError - Infrastructure::DTO.new(podcast: nil, episodes: nil) + ctx[:feed] = Infrastructure::DTO.new(podcast: podcast, episodes: episodes.reverse) + end + + InitializeEmptyFeedDTO = ->(ctx) do + ctx[:feed] = Infrastructure::DTO.new(podcast: nil, episodes: nil) end end end diff --git a/lib/pod/infrastructure/function_pipeline.rb b/lib/pod/infrastructure/function_pipeline.rb new file mode 100644 index 0000000..5a30cfb --- /dev/null +++ b/lib/pod/infrastructure/function_pipeline.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Infrastructure + class FunctionPipeline + def initialize(functions:, on_error:, context: {}) + @functions = functions + @on_error = on_error + @context = Context.new(context) + end + + def call + @functions.each { |f| f.call(@context) } + rescue => e + @context[:error] = e + @on_error.call(@context) + ensure + return @context.last_entry # standard:disable Lint/EnsureReturn + end + + private + + class Context + def initialize(context) + @state = context + end + + def [](key) + @state[key.to_sym] + end + + def []=(key, value) + @state[key.to_sym] = value + @state[:last_entry] = key + end + + def last_entry + key = @state[:last_entry] + @state[key] + end + end + end +end