podcast_index 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b65c6fe11a9ccf6a3747e6c3c5cd862686978ee63cfcf7b1fd88ad79dc46367
4
- data.tar.gz: 9695467e8f4bcfe6507ef16d8a91e6745de89fc0601d0cc63899886766b96e88
3
+ metadata.gz: be3811eb9cbde914e7e314e4d02e35a335fcb06250b8e22519ed0b59487d2e4b
4
+ data.tar.gz: 19de21d136dbc55f8cf0adcc65a710d773baceb99398ff22182d7e67b26ebc6b
5
5
  SHA512:
6
- metadata.gz: 6a24dfe1841426f4b8aede0afa6470060e6dcbcbaca97312b249d7918b0c45d19bcf26cb201ffb0f38a40285e71ecea42613b42424d58f67968bc0c91b0e9da0
7
- data.tar.gz: 4bd0f8edc4931a7b8c54a46fa56c80ae12247903ae546bde090051cd7fb61efc44da3b2884675fe211247f90f04ce2e94abdcedf610329a3d029ef32f0338227
6
+ metadata.gz: 353f4074705cd1e93675d8bb8908698a57b07f321f54510d6d8e23af483736d6fd127b94aafcc67cd3d29e84e628c505aac290823e33425e30bc7c4343044cf8
7
+ data.tar.gz: 03075bb9e16b26edaa2b4d865435b0bdf919fd896f7b19e0b5c3f7d204cb0093a2f35e02d1312b2e0248c2a3385dbb54cd240a504b92afd1bbae3bb8d8a22d40
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2023-04-24
4
+
5
+ * BREAKING: Switching to be more consistent with ActiveRecord conventions, using `find_by` and `where` methods for all models
6
+ * Add support for all "Search", "Podcasts", "Episodes", "Recent" and "Value" sections of the API. This introduces the `Soundbite` and `Value` domain models.
7
+ * Update README.md
8
+
3
9
  ## [0.1.0] - 2023-01-06
4
10
 
5
11
  - Initial release
data/Gemfile.lock CHANGED
@@ -1,25 +1,24 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- podcast_index (0.1.0)
4
+ podcast_index (0.2.0)
5
5
  activesupport (>= 6.0, < 8)
6
6
  addressable (~> 2)
7
7
 
8
8
  GEM
9
9
  remote: https://rubygems.org/
10
10
  specs:
11
- activesupport (6.1.7)
11
+ activesupport (7.0.3.1)
12
12
  concurrent-ruby (~> 1.0, >= 1.0.2)
13
13
  i18n (>= 1.6, < 2)
14
14
  minitest (>= 5.1)
15
15
  tzinfo (~> 2.0)
16
- zeitwerk (~> 2.3)
17
16
  addressable (2.8.1)
18
17
  public_suffix (>= 2.0.2, < 6.0)
19
18
  ast (2.4.2)
20
19
  codecov (0.6.0)
21
20
  simplecov (>= 0.15, < 0.22)
22
- concurrent-ruby (1.1.10)
21
+ concurrent-ruby (1.2.2)
23
22
  crack (0.4.5)
24
23
  rexml
25
24
  debug (1.7.1)
@@ -94,7 +93,6 @@ GEM
94
93
  addressable (>= 2.8.0)
95
94
  crack (>= 0.3.2)
96
95
  hashdiff (>= 0.4.0, < 2.0.0)
97
- zeitwerk (2.6.6)
98
96
 
99
97
  PLATFORMS
100
98
  x86_64-darwin-20
data/README.md CHANGED
@@ -32,6 +32,21 @@ In a Rails app, this configuration would typically be placed in an initializer f
32
32
 
33
33
  ## Usage
34
34
 
35
+ This client currently implements the following sections of the API:
36
+ * [Search](https://podcastindex-org.github.io/docs-api/#tag--Search)
37
+ * [Podcasts](https://podcastindex-org.github.io/docs-api/#tag--Podcasts)
38
+ * [Episodes](https://podcastindex-org.github.io/docs-api/#tag--Episodes)
39
+ * [Recent](https://podcastindex-org.github.io/docs-api/#tag--Recent)
40
+ * [Value](https://podcastindex-org.github.io/docs-api/#tag--Value)
41
+
42
+ These are exposed through the following domain models:
43
+ * [Episode](lib/podcast_index/episode.rb)
44
+ * [Podcast](lib/podcast_index/podcast.rb)
45
+ * [Soundbite](lib/podcast_index/soundbite.rb)
46
+ * [Value](lib/podcast_index/value.rb)
47
+
48
+ The intent is to follow ActiveRecord conventions as reasonably possible. Therefore, most of the requests are accessed through the model's `.find_by` and `.where` methods.
49
+
35
50
  ### Examples
36
51
 
37
52
  Find a podcast by podcastindex id:
@@ -44,23 +59,73 @@ podcast.title # => "Podcasting 2.0"
44
59
  Find an episode by guid:
45
60
 
46
61
  ```ruby
47
- episode = PodcastIndex::Episode.find_by_guid("PC2084", feedurl: "http://mp3s.nashownotes.com/pc20rss.xml")
62
+ episode = PodcastIndex::Episode.find_by(guid: "PC2084", feedurl: "http://mp3s.nashownotes.com/pc20rss.xml")
48
63
  episode.title # => "Episode 84: All Aboard to On-Board!"
49
64
  ```
50
65
 
66
+ Find a Value block by feed_id:
67
+
68
+ ```ruby
69
+ value = PodcastIndex::Value.find_by(feed_id: 920666)
70
+ value.model.type # => "lightning"
71
+ ```
72
+
51
73
  Methods that return multiple results are represented as an array of objects:
52
74
 
53
75
  ```ruby
54
- episodes = PodcastIndex::Episode.find_by_person("Adam Curry")
76
+ episodes = PodcastIndex::Episode.where(person: "Adam Curry")
55
77
  episodes.count # => 57
56
78
  episodes.first.title # => "Episode #2: A conversation with Adam Curry"
57
79
  ```
58
80
 
81
+ ```ruby
82
+ soundbite = PodcastIndex::Soundbite.where(recent: true)
83
+ soundbite.first.episode_id # => 15082076307
84
+ ```
85
+
86
+
87
+
59
88
  ### Supported Methods
60
89
 
61
- This client currently implements most of the API, focusing on searching for Podcasts and Episodes. For the list of supported methods, see the [Podcast](lib/podcast_index/podcast.rb) and [Episode](lib/podcast_index/episode.rb) models.
90
+ ```ruby
91
+ # Episode
92
+ Episode.find(id, fulltext: nil)
93
+ Episode.find_by(guid, feedurl: nil, feedid: nil, fulltext: nil)
94
+ Episode.where(feed_id:, since: nil, max: nil, fulltext: nil)
95
+ Episode.where(feed_url:, since: nil, max: nil, fulltext: nil)
96
+ Episode.where(podcast_guid:, since: nil, max: nil, fulltext: nil)
97
+ Episode.where(live: true, max: nil)
98
+ Episode.where(itunes_id:, since: nil, max: nil, fulltext: nil)
99
+ Episode.where(person:, fulltext: nil)
100
+ Episode.where(recent: true, max: nil, exclude_string: nil, before: nil, fulltext: nil)
101
+ Episode.sample(max: nil, lang: nil, categories: [], exclude_categories: [], fulltext: nil) # Find a random episode
102
+
103
+ # Podcast
104
+ Podcast.find(id)
105
+ Podcast.find_by(feed_url)
106
+ Podcast.find_by(guid)
107
+ Podcast.find_by(itunes_id)
108
+ Podcast.where(tag:)
109
+ Podcast.where(medium:)
110
+ # Additional parameters only for searching with "music" medium
111
+ Podcast.where(medium: "music", term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
112
+ Podcast.where(term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
113
+ Podcast.where(title:, val: nil, clean: nil, fulltext: nil)
114
+ Podcast.where(trending: true, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
115
+ Podcast.where(dead: true)
116
+ Podcast.where(recent: true, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
117
+ Podcast.where(new: true, max: nil, since: nil, feedid: nil, desc: nil)
118
+ Podcast.where(newly_found: true, max: nil, since: nil)
119
+
120
+ # Soundbite
121
+ Soundbite.where(recent: true, max: nil)
122
+
123
+ # Value
124
+ Value.find_by(feed_id)
125
+ Value.find_by(feed_url)
126
+ ```
62
127
 
63
- The attributes of the response object mirror the names in the API, but have been translated to "underscore" format more closely follow Ruby conventions. For example, the `lastUpdateTime` attribute for a `Podcast` is renamed to `last_update_time`.
128
+ The attributes of the models mirror the names in the API, but have been translated to "underscore" format to more closely follow Ruby conventions. For example, the `lastUpdateTime` attribute for a `Podcast` is exposed as `last_update_time`.
64
129
 
65
130
  ### Exception Handling
66
131
 
@@ -4,30 +4,53 @@ module PodcastIndex
4
4
  extend Request
5
5
 
6
6
  class << self
7
+ # https://podcastindex-org.github.io/docs-api/#get-/episodes/byid
7
8
  def by_id(id:, fulltext: nil)
8
9
  response = get("/episodes/byid", id: id, fulltext: fulltext)
9
10
  JSON.parse(response.body)
10
11
  end
11
12
 
13
+ # https://podcastindex-org.github.io/docs-api/#get-/episodes/byfeedid
12
14
  def by_feed_id(id:, since: nil, max: nil, fulltext: nil)
13
15
  response = get("/episodes/byfeedid", id: id, since: since, max: max, fulltext: fulltext)
14
16
  JSON.parse(response.body)
15
17
  end
16
18
 
19
+ # https://podcastindex-org.github.io/docs-api/#get-/episodes/byfeedurl
17
20
  def by_feed_url(url:, since: nil, max: nil, fulltext: nil)
18
21
  response = get("/episodes/byfeedurl", url: url, since: since, max: max, fulltext: fulltext)
19
22
  JSON.parse(response.body)
20
23
  end
21
24
 
25
+ # https://podcastindex-org.github.io/docs-api/#get-/episodes/bypodcastguid
26
+ def by_podcast_guid(podcast_guid:, since: nil, max: nil, fulltext: nil)
27
+ response = get("/episodes/bypodcastguid", guid: podcast_guid, since: since, max: max, fulltext: fulltext)
28
+ JSON.parse(response.body)
29
+ end
30
+
31
+ # https://podcastindex-org.github.io/docs-api/#get-/episodes/byguid
22
32
  def by_guid(guid:, feedurl: nil, feedid: nil, fulltext: nil)
23
33
  response = get("/episodes/byguid", guid: guid, feedurl: feedurl, feedid: feedid, fulltext: fulltext)
24
34
  JSON.parse(response.body)
25
35
  end
26
36
 
37
+ # https://podcastindex-org.github.io/docs-api/#get-/episodes/byitunesid
27
38
  def by_itunes_id(id:, since: nil, max: nil, fulltext: nil)
28
39
  response = get("/episodes/byitunesid", id: id, since: since, max: max, fulltext: fulltext)
29
40
  JSON.parse(response.body)
30
41
  end
42
+
43
+ # https://podcastindex-org.github.io/docs-api/#get-/episodes/live
44
+ def live(max: nil)
45
+ response = get("/episodes/live", max: max)
46
+ JSON.parse(response.body)
47
+ end
48
+
49
+ # https://podcastindex-org.github.io/docs-api/#get-/episodes/random
50
+ def random(max: nil, lang: nil, cat: nil, notcat: nil, fulltext: nil)
51
+ response = get("/episodes/random", max: max, lang: lang, cat: cat, notcat: notcat, fulltext: fulltext)
52
+ JSON.parse(response.body)
53
+ end
31
54
  end
32
55
  end
33
56
  end
@@ -23,6 +23,27 @@ module PodcastIndex
23
23
  response = get("/podcasts/byguid", guid: guid)
24
24
  JSON.parse(response.body)
25
25
  end
26
+
27
+ def by_tag(tag:)
28
+ params = {}.tap { |p| p[tag] = true }
29
+ response = get("/podcasts/bytag", params)
30
+ JSON.parse(response.body)
31
+ end
32
+
33
+ def by_medium(medium:)
34
+ response = get("/podcasts/bymedium", medium: medium)
35
+ JSON.parse(response.body)
36
+ end
37
+
38
+ def trending(max: nil, since: nil, lang: nil, cat: nil, notcat: nil)
39
+ response = get("/podcasts/trending", max: max, since: since, lang: lang, cat: cat, notcat: notcat)
40
+ JSON.parse(response.body)
41
+ end
42
+
43
+ def dead
44
+ response = get("/podcasts/dead", {})
45
+ JSON.parse(response.body)
46
+ end
26
47
  end
27
48
  end
28
49
  end
@@ -0,0 +1,35 @@
1
+ module PodcastIndex
2
+ module Api
3
+ class Recent
4
+ extend Request
5
+
6
+ class << self
7
+ def episodes(max: nil, exclude_string: nil, before: nil, fulltext: nil)
8
+ response = get("/recent/episodes", max: max, exclude_string: exclude_string, before: before,
9
+ fulltext: fulltext)
10
+ JSON.parse(response.body)
11
+ end
12
+
13
+ def feeds(max: nil, since: nil, lang: nil, cat: nil, notcat: nil)
14
+ response = get("/recent/feeds", max: max, since: since, lang: lang, cat: cat, notcat: notcat)
15
+ JSON.parse(response.body)
16
+ end
17
+
18
+ def new_feeds(max: nil, since: nil, feedid: nil, desc: nil)
19
+ response = get("/recent/newfeeds", max: max, since: since, feedid: feedid, desc: desc)
20
+ JSON.parse(response.body)
21
+ end
22
+
23
+ def data(max: nil, since: nil)
24
+ response = get("/recent/data", max: max, since: since)
25
+ JSON.parse(response.body)
26
+ end
27
+
28
+ def soundbites(max: nil)
29
+ response = get("/recent/soundbites", max: max)
30
+ JSON.parse(response.body)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -19,10 +19,10 @@ module PodcastIndex
19
19
  JSON.parse(response.body)
20
20
  end
21
21
 
22
- # def music_by_term(term, val: nil, aponly: nil, clean: nil, fulltext: nil)
23
- # response = get("/search/music/byterm", q: term, val:, aponly:, clean:, fulltext:)
24
- # JSON.parse(response.body)
25
- # end
22
+ def music_by_term(term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
23
+ response = get("/search/music/byterm", q: term, val: val, aponly: aponly, clean: clean, fulltext: fulltext)
24
+ JSON.parse(response.body)
25
+ end
26
26
  end
27
27
  end
28
28
  end
@@ -0,0 +1,19 @@
1
+ module PodcastIndex
2
+ module Api
3
+ class Value
4
+ extend Request
5
+
6
+ class << self
7
+ def by_feed_id(id:)
8
+ response = get("/value/byfeedid", id: id)
9
+ JSON.parse(response.body)
10
+ end
11
+
12
+ def by_feed_url(url:)
13
+ response = get("/value/byfeedurl", url: url)
14
+ JSON.parse(response.body)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -4,18 +4,50 @@ require "delegate"
4
4
  module PodcastIndex
5
5
  class Episode < SimpleDelegator
6
6
  class << self
7
+ FIND_ONE_ATTRIBUTES = %i[guid].freeze
8
+ FIND_MANY_ATTRIBUTES = %i[feed_id feed_url podcast_guid live itunes_id person recent].freeze
9
+
7
10
  def find(id, fulltext: nil)
8
11
  response = Api::Episodes.by_id(id: id, fulltext: fulltext)
9
12
  from_response(response)
10
13
  end
11
14
 
12
- def find_by_feed_id(feed_id, since: nil, max: nil, fulltext: nil)
15
+ def find_by(attributes)
16
+ match = (attributes.keys & FIND_ONE_ATTRIBUTES)
17
+ raise ArgumentError, "Must supply one of the attributes: #{FIND_ONE_ATTRIBUTES}" unless match.present?
18
+ raise ArgumentError, "Must supply only one of the attributes: #{FIND_ONE_ATTRIBUTES}" if match.length > 1
19
+
20
+ send("find_by_#{match.first}", attributes[match.first])
21
+ end
22
+
23
+ def where(attributes)
24
+ match = (attributes.keys & FIND_MANY_ATTRIBUTES)
25
+ raise ArgumentError, "Must supply one of the attributes: #{FIND_MANY_ATTRIBUTES}" unless match.present?
26
+ raise ArgumentError, "Must supply only one of the attributes: #{FIND_MANY_ATTRIBUTES}" if match.length > 1
27
+
28
+ send("find_all_by_#{match.first}", **attributes)
29
+ end
30
+
31
+ def sample(max: nil, lang: nil, categories: [], exclude_categories: [], fulltext: nil)
32
+ response = Api::Episodes.random(max: max, lang: lang, cat: categories.join(","),
33
+ notcat: exclude_categories.join(","), fulltext: fulltext)
34
+ from_response_collection(response, "episodes")
35
+ end
36
+
37
+ private
38
+
39
+ def find_all_by_feed_id(feed_id:, since: nil, max: nil, fulltext: nil)
13
40
  response = Api::Episodes.by_feed_id(id: feed_id, since: since, max: max, fulltext: fulltext)
14
41
  from_response_collection(response)
15
42
  end
16
43
 
17
- def find_by_feed_url(url, since: nil, max: nil, fulltext: nil)
18
- response = Api::Episodes.by_feed_url(url: url, since: since, max: max, fulltext: fulltext)
44
+ def find_all_by_feed_url(feed_url:, since: nil, max: nil, fulltext: nil)
45
+ response = Api::Episodes.by_feed_url(url: feed_url, since: since, max: max, fulltext: fulltext)
46
+ from_response_collection(response)
47
+ end
48
+
49
+ def find_all_by_podcast_guid(podcast_guid:, since: nil, max: nil, fulltext: nil)
50
+ response = Api::Episodes.by_podcast_guid(podcast_guid: podcast_guid, since: since, max: max, fulltext: fulltext)
19
51
  from_response_collection(response)
20
52
  end
21
53
 
@@ -24,25 +56,33 @@ module PodcastIndex
24
56
  from_response(response)
25
57
  end
26
58
 
27
- def find_by_itunes_id(itunes_id, since: nil, max: nil, fulltext: nil)
59
+ def find_all_by_live(live:, max: nil) # rubocop:disable Lint/UnusedMethodArgument
60
+ response = Api::Episodes.live(max: max)
61
+ from_response_collection(response)
62
+ end
63
+
64
+ def find_all_by_itunes_id(itunes_id:, since: nil, max: nil, fulltext: nil)
28
65
  response = Api::Episodes.by_itunes_id(id: itunes_id, since: since, max: max, fulltext: fulltext)
29
66
  from_response_collection(response)
30
67
  end
31
68
 
32
- def find_by_person(person, fulltext: nil)
69
+ def find_all_by_person(person:, fulltext: nil)
33
70
  response = Api::Search.by_person(person: person, fulltext: fulltext)
34
71
  from_response_collection(response)
35
72
  end
36
73
 
37
- private
74
+ def find_all_by_recent(recent:, max: nil, exclude_string: nil, before: nil, fulltext: nil) # rubocop:disable Lint/UnusedMethodArgument
75
+ response = Api::Recent.episodes(max: max, exclude_string: exclude_string, before: before, fulltext: fulltext)
76
+ from_response_collection(response)
77
+ end
38
78
 
39
79
  def from_response(response)
40
80
  feed = response["episode"].transform_keys(&:underscore)
41
81
  new(JSON.parse(feed.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
42
82
  end
43
83
 
44
- def from_response_collection(response)
45
- response["items"].map do |item|
84
+ def from_response_collection(response, collection_key = "items")
85
+ response[collection_key].map do |item|
46
86
  episode = item.transform_keys(&:underscore)
47
87
  new(JSON.parse(episode.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
48
88
  end
@@ -1,16 +1,40 @@
1
1
  require "ostruct"
2
2
  require "delegate"
3
3
 
4
+ # rubocop:disable Metrics/ParameterLists, Lint/UnusedMethodArgument
4
5
  module PodcastIndex
5
6
  class Podcast < SimpleDelegator
6
7
  class << self
8
+ FIND_ONE_ATTRIBUTES = %i[feed_url guid itunes_id].freeze
9
+ FIND_MANY_ATTRIBUTES = %i[tag medium term title trending dead recent new newly_found].freeze
10
+
7
11
  def find(id)
8
12
  response = Api::Podcasts.by_feed_id(id: id)
9
13
  from_response(response)
10
14
  end
11
15
 
12
- def find_by_feed_url(url)
13
- response = Api::Podcasts.by_feed_url(url: url)
16
+ def find_by(attributes)
17
+ match = (attributes.keys & FIND_ONE_ATTRIBUTES)
18
+ raise ArgumentError, "Must supply one of the attributes: #{FIND_ONE_ATTRIBUTES}" unless match.present?
19
+ raise ArgumentError, "Must supply only one of the attributes: #{FIND_ONE_ATTRIBUTES}" if match.length > 1
20
+
21
+ send("find_by_#{match.first}", attributes[match.first])
22
+ end
23
+
24
+ def where(attributes)
25
+ return find_all_music_by_term(**attributes) if attributes[:medium] == "music" && attributes[:term].present?
26
+
27
+ match = (attributes.keys & FIND_MANY_ATTRIBUTES)
28
+ raise ArgumentError, "Must supply one of the attributes: #{FIND_MANY_ATTRIBUTES}" unless match.present?
29
+ raise ArgumentError, "Must supply only one of the attributes: #{FIND_MANY_ATTRIBUTES}" if match.length > 1
30
+
31
+ send("find_all_by_#{match.first}", **attributes)
32
+ end
33
+
34
+ private
35
+
36
+ def find_by_feed_url(feed_url)
37
+ response = Api::Podcasts.by_feed_url(url: feed_url)
14
38
  from_response(response)
15
39
  end
16
40
 
@@ -19,22 +43,67 @@ module PodcastIndex
19
43
  from_response(response)
20
44
  end
21
45
 
22
- def find_by_itunes_id(id)
23
- response = Api::Podcasts.by_itunes_id(id: id)
46
+ def find_by_itunes_id(itunes_id)
47
+ response = Api::Podcasts.by_itunes_id(id: itunes_id)
24
48
  from_response(response)
25
49
  end
26
50
 
27
- def find_by_term(term, val: nil, aponly: nil, clean: nil, fulltext: nil)
51
+ def find_all_by_tag(tag:)
52
+ response = Api::Podcasts.by_tag(tag: tag)
53
+ from_response_collection(response)
54
+ end
55
+
56
+ def find_all_music_by_term(medium:, term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
57
+ response = Api::Search.music_by_term(term: term, val: val, aponly: aponly, clean: clean, fulltext: fulltext)
58
+ from_response_collection(response)
59
+ end
60
+
61
+ def find_all_by_medium(medium:)
62
+ response = Api::Podcasts.by_medium(medium: medium)
63
+ from_response_collection(response)
64
+ end
65
+
66
+ def find_all_by_trending(trending:, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
67
+ response = Api::Podcasts.trending(max: max, since: since, lang: lang, cat: categories.join(","),
68
+ notcat: exclude_categories.join(","))
69
+ from_response_collection(response)
70
+ end
71
+
72
+ def find_all_by_dead(*)
73
+ response = Api::Podcasts.dead
74
+ from_response_collection(response)
75
+ end
76
+
77
+ def find_all_by_term(term:, val: nil, aponly: nil, clean: nil, fulltext: nil)
28
78
  response = Api::Search.by_term(term: term, val: val, aponly: aponly, clean: clean, fulltext: fulltext)
29
79
  from_response_collection(response)
30
80
  end
31
81
 
32
- def find_by_title(title, val: nil, clean: nil, fulltext: nil)
82
+ def find_all_by_title(title:, val: nil, clean: nil, fulltext: nil)
33
83
  response = Api::Search.by_title(title: title, val: val, clean: clean, fulltext: fulltext)
34
84
  from_response_collection(response)
35
85
  end
36
86
 
37
- private
87
+ def find_all_by_recent(recent:, max: nil, since: nil, lang: nil, categories: [], exclude_categories: [])
88
+ response = Api::Recent.feeds(max: max, since: since, lang: lang, cat: categories.join(","),
89
+ notcat: exclude_categories.join(","))
90
+ from_response_collection(response)
91
+ end
92
+
93
+ def find_all_by_new(new:, max: nil, since: nil, feedid: nil, desc: nil)
94
+ response = Api::Recent.new_feeds(max: max, since: since, feedid: feedid, desc: desc)
95
+ from_response_collection(response)
96
+ end
97
+
98
+ def find_all_by_newly_found(newly_found:, max: nil, since: nil)
99
+ response = Api::Recent.data(max: max, since: since)
100
+ # This one has a bit of an oddball response. It's nested in a "data" attribute and all the keys
101
+ # for each feed are prefixed with "feed" (see example fixture)
102
+ response["data"]["feeds"].map do |item|
103
+ feed = item.transform_keys { |key| key.delete_prefix("feed").underscore }
104
+ new(JSON.parse(feed.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
105
+ end
106
+ end
38
107
 
39
108
  def from_response(response)
40
109
  feed = response["feed"].transform_keys(&:underscore)
@@ -43,10 +112,11 @@ module PodcastIndex
43
112
 
44
113
  def from_response_collection(response)
45
114
  response["feeds"].map do |item|
46
- episode = item.transform_keys(&:underscore)
47
- new(JSON.parse(episode.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
115
+ feed = item.transform_keys(&:underscore)
116
+ new(JSON.parse(feed.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
48
117
  end
49
118
  end
50
119
  end
51
120
  end
52
121
  end
122
+ # rubocop:enable Metrics/ParameterLists, Lint/UnusedMethodArgument
@@ -0,0 +1,30 @@
1
+ require "ostruct"
2
+ require "delegate"
3
+
4
+ module PodcastIndex
5
+ class Soundbite < SimpleDelegator
6
+ class << self
7
+ FIND_MANY_ATTRIBUTES = %i[recent].freeze
8
+
9
+ def where(attributes)
10
+ match = (attributes.keys & FIND_MANY_ATTRIBUTES)
11
+ raise ArgumentError, "Must supply one of the attributes: #{FIND_MANY_ATTRIBUTES}" unless match.present?
12
+ raise ArgumentError, "Must supply only one of the attributes: #{FIND_MANY_ATTRIBUTES}" if match.length > 1
13
+
14
+ send("find_all_by_#{match.first}", **attributes)
15
+ end
16
+
17
+ def find_all_by_recent(recent:, max: nil) # rubocop:disable Lint/UnusedMethodArgument
18
+ response = Api::Recent.soundbites(max: max)
19
+ from_response_collection(response)
20
+ end
21
+
22
+ def from_response_collection(response, collection_key = "items")
23
+ response[collection_key].map do |item|
24
+ soundbite = item.transform_keys(&:underscore)
25
+ new(JSON.parse(soundbite.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,35 @@
1
+ require "ostruct"
2
+ require "delegate"
3
+
4
+ module PodcastIndex
5
+ class Value < SimpleDelegator
6
+ class << self
7
+ FIND_ONE_ATTRIBUTES = %i[feed_id feed_url].freeze
8
+
9
+ def find_by(attributes)
10
+ match = (attributes.keys & FIND_ONE_ATTRIBUTES)
11
+ raise ArgumentError, "Must supply one of the attributes: #{FIND_ONE_ATTRIBUTES}" unless match.present?
12
+ raise ArgumentError, "Must supply only one of the attributes: #{FIND_ONE_ATTRIBUTES}" if match.length > 1
13
+
14
+ send("find_by_#{match.first}", attributes[match.first])
15
+ end
16
+
17
+ private
18
+
19
+ def find_by_feed_id(feed_id)
20
+ response = Api::Value.by_feed_id(id: feed_id)
21
+ from_response(response)
22
+ end
23
+
24
+ def find_by_feed_url(feed_url)
25
+ response = Api::Value.by_feed_url(url: feed_url)
26
+ from_response(response)
27
+ end
28
+
29
+ def from_response(response)
30
+ value = response["value"].transform_keys(&:underscore)
31
+ new(JSON.parse(value.to_json, object_class: OpenStruct)) # rubocop:disable Style/OpenStructUse
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
1
  module PodcastIndex
2
- VERSION = "0.1.0".freeze
2
+ VERSION = "0.2.0".freeze
3
3
  end
data/lib/podcast_index.rb CHANGED
@@ -7,8 +7,12 @@ require_relative "podcast_index/api/request"
7
7
  require_relative "podcast_index/api/podcasts"
8
8
  require_relative "podcast_index/api/episodes"
9
9
  require_relative "podcast_index/api/search"
10
+ require_relative "podcast_index/api/recent"
11
+ require_relative "podcast_index/api/value"
10
12
  require_relative "podcast_index/podcast"
11
13
  require_relative "podcast_index/episode"
14
+ require_relative "podcast_index/soundbite"
15
+ require_relative "podcast_index/value"
12
16
 
13
17
  module PodcastIndex
14
18
  include ActiveSupport::Configurable
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: podcast_index
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jason York
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-07 00:00:00.000000000 Z
11
+ date: 2023-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -173,12 +173,15 @@ files:
173
173
  - lib/podcast_index.rb
174
174
  - lib/podcast_index/api/episodes.rb
175
175
  - lib/podcast_index/api/podcasts.rb
176
+ - lib/podcast_index/api/recent.rb
176
177
  - lib/podcast_index/api/request.rb
177
178
  - lib/podcast_index/api/search.rb
179
+ - lib/podcast_index/api/value.rb
178
180
  - lib/podcast_index/episode.rb
179
181
  - lib/podcast_index/podcast.rb
182
+ - lib/podcast_index/soundbite.rb
183
+ - lib/podcast_index/value.rb
180
184
  - lib/podcast_index/version.rb
181
- - podcast_index.gemspec
182
185
  homepage: https://github.com/jasonyork/podcast-index
183
186
  licenses:
184
187
  - MIT
@@ -1,41 +0,0 @@
1
- require_relative "lib/podcast_index/version"
2
-
3
- Gem::Specification.new do |spec|
4
- spec.name = "podcast_index"
5
- spec.version = PodcastIndex::VERSION
6
- spec.authors = ["Jason York"]
7
-
8
- spec.summary = "Ruby client for the podcastindex.org API"
9
- spec.description = "Exposes the podcastindex.org API through Ruby domain models."
10
- spec.homepage = "https://github.com/jasonyork/podcast-index"
11
- spec.license = "MIT"
12
- spec.required_ruby_version = ">= 3.0.0"
13
-
14
- spec.metadata["homepage_uri"] = spec.homepage
15
- spec.metadata["source_code_uri"] = "https://github.com/jasonyork/podcast-index"
16
- spec.metadata["changelog_uri"] = "https://github.com/jasonyork/podcast-index/blob/master/CHANGELOG.md"
17
- spec.metadata["rubygems_mfa_required"] = "true"
18
-
19
- # Specify which files should be added to the gem when it is released.
20
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
- spec.files = Dir.chdir(__dir__) do
22
- `git ls-files -z`.split("\x0").reject do |f|
23
- (f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
24
- end
25
- end
26
- spec.bindir = "exe"
27
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
28
- spec.require_paths = ["lib"]
29
-
30
- spec.add_dependency "activesupport", ">= 6.0", "< 8"
31
- spec.add_dependency "addressable", "~> 2"
32
-
33
- spec.add_development_dependency "codecov", "~> 0.4"
34
- spec.add_development_dependency "debug", "~> 1"
35
- spec.add_development_dependency "rspec-core", "~> 3"
36
- spec.add_development_dependency "rspec-its", "~> 1"
37
- spec.add_development_dependency "rubocop-performance", "~> 1"
38
- spec.add_development_dependency "rubocop-rake", "~> 0.6"
39
- spec.add_development_dependency "rubocop-rspec", "~> 2"
40
- spec.add_development_dependency "webmock", "~> 3"
41
- end