-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Integrations interface #1136
Integrations interface #1136
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
module Integrations | ||
module Base | ||
class Episode | ||
attr_reader :feeder_episode | ||
|
||
def initialize(feeder_episode) | ||
@feeder_episode = feeder_episode | ||
end | ||
|
||
def synced_with_integration? | ||
raise NotImplementedError, "Subclasses must implement synced_with_integration?" | ||
end | ||
|
||
def integration_new? | ||
raise NotImplementedError, "Subclasses must implement integration_new?" | ||
end | ||
|
||
def archived? | ||
raise NotImplementedError, "Subclasses must implement archived?" | ||
end | ||
|
||
def video_content_type? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Megaphone doesn't support video at all, so this is also not applicable There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. my bad, that is how this is used! perfect |
||
feeder_episode.video_content_type? | ||
end | ||
|
||
# Delegate methods to feeder_episode | ||
def method_missing(method_name, *arguments, &block) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The episode integration written for megaphone act as their own entity, passing through methods to the underlying feeder episode is likely to cause issues, this is not needed. |
||
if feeder_episode.respond_to?(method_name) | ||
feeder_episode.send(method_name, *arguments, &block) | ||
else | ||
super | ||
end | ||
end | ||
|
||
def respond_to_missing?(method_name, include_private = false) | ||
feeder_episode.respond_to?(method_name) || super | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
module Integrations | ||
module Base | ||
module EpisodeSetOperations | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The root of the episode set manipulation. The consumption side: Deriving the sets of episodes and filtering them for CRUD sync |
||
# In the case where there are duplicate guids in the feeds, we want to make | ||
# sure that the most "current" episode is the one that maps to the remote state. | ||
def sort_by_episode_properties(eps) | ||
# Sort the episodes by: | ||
# 1. Non-deleted episodes first | ||
# 2. Published episodes first | ||
# 3. Published date most recent first | ||
# 4. Created date most recent first | ||
eps = | ||
eps.sort_by do |e| | ||
[ | ||
e.deleted_at.nil? ? 1 : -1, | ||
e.published_at.present? ? 1 : -1, | ||
e.published_at || e.created_at, | ||
e.created_at | ||
] | ||
end | ||
|
||
# return sorted list, reversed | ||
# modeling a priority queue -- most important first | ||
eps.reverse | ||
end | ||
|
||
def filter_episodes_to_sync(eps) | ||
# Reject episodes if the audio is marked as uploaded/complete | ||
# or if the episode is a video | ||
eps | ||
.reject(&:synced_with_integration?) | ||
.reject(&:video_content_type?) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, that is common to megaphone as well, but that's sorta luck - other integrations may have no issue with video, so this method may not be common/reusable |
||
end | ||
|
||
def filter_episodes_to_archive(eps, eps_in_feed) | ||
# Episodes to archive can include: | ||
# - episodes that are now excluded from the feed | ||
# - episodes that are deleted or unpublished | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe? megaphone doesn't have an "archived" status for episodes, that is an apple concept |
||
# - episodes that have fallen off the end of the feed (Feed#display_episodes_count) | ||
eps | ||
.reject { |ep| eps_in_feed.include?(ep) } | ||
.reject(&:integration_new?) | ||
.reject(&:archived?) | ||
end | ||
|
||
def filter_episodes_to_unarchive(eps) | ||
eps.filter(&:archived?) | ||
end | ||
|
||
# Only select episodes that have an remote integration state | ||
def only_episodes_with_integration_state(eps) | ||
eps.reject(&:integration_new?) | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
module Integrations | ||
module Base | ||
class Publisher | ||
include EpisodeSetOperations | ||
|
||
attr_reader :show | ||
|
||
def initialize(show:) | ||
@show = show | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A "show" is an apple concept, not in megaphone, in megaphone it is a Podcast. |
||
end | ||
|
||
def episodes_to_sync | ||
filter_episodes_to_sync(show.episodes) | ||
end | ||
|
||
def episodes_to_archive | ||
filter_episodes_to_archive(show.podcast_episodes, Set.new(show.episodes)) | ||
end | ||
|
||
def episodes_to_unarchive | ||
filter_episodes_to_unarchive(show.episodes) | ||
end | ||
|
||
def publish! | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes sense as it is the only part of this that I see called by another class/module, this seems like the interface of the publisher |
||
raise NotImplementedError, "Subclasses must implement publish!" | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
module Integrations | ||
module Base | ||
class Show | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The show gives the handle on the episodes. The production side: baseline unfiltered, sorted set of episodes. |
||
include EpisodeSetOperations | ||
|
||
attr_reader :feed | ||
|
||
def initialize(public_feed:, private_feed:) | ||
@public_feed = public_feed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. public feed not used in this |
||
@private_feed = private_feed | ||
end | ||
|
||
def podcast | ||
private_feed.podcast | ||
end | ||
|
||
def podcast_feeder_episodes | ||
@podcast_feeder_episodes ||= | ||
podcast.episodes | ||
.reset | ||
.with_deleted | ||
.group_by(&:item_guid) | ||
.values | ||
.map { |eps| sort_by_episode_properties(eps) } | ||
.map(&:first) | ||
end | ||
|
||
# All the episodes -- including deleted and unpublished | ||
def podcast_episodes | ||
@podcast_episodes ||= podcast_feeder_episodes.map { |e| build_integration_episode(e) } | ||
end | ||
|
||
# Does not include deleted episodes | ||
def episodes | ||
@episodes ||= begin | ||
feed_episode_ids = Set.new(private_feed.feed_episodes.feed_ready.map(&:id)) | ||
|
||
podcast_episodes | ||
.filter { |e| feed_episode_ids.include?(e.feeder_episode.id) } | ||
end | ||
end | ||
|
||
private | ||
|
||
def build_integration_episode(feeder_episode) | ||
raise NotImplementedError, "Subclasses must implement create_integration_episode" | ||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this means not persisted to the integration?
it would be helpful of there were some common way for integrations to save external ids and state of persistence to the 3rd party