x-resources 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +9 -0
  3. data/CHANGELOG.md +254 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +148 -0
  6. data/lib/x/resources/abstract_class.rb +29 -0
  7. data/lib/x/resources/actions/direct_messages.rb +99 -0
  8. data/lib/x/resources/actions/engagement.rb +95 -0
  9. data/lib/x/resources/actions/lists.rb +126 -0
  10. data/lib/x/resources/actions/posts.rb +77 -0
  11. data/lib/x/resources/actions/relationships.rb +91 -0
  12. data/lib/x/resources/actions.rb +22 -0
  13. data/lib/x/resources/api.rb +40 -0
  14. data/lib/x/resources/attributes.rb +203 -0
  15. data/lib/x/resources/batch.rb +43 -0
  16. data/lib/x/resources/batch_finders.rb +185 -0
  17. data/lib/x/resources/bookmark_folder.rb +23 -0
  18. data/lib/x/resources/community.rb +137 -0
  19. data/lib/x/resources/cursor.rb +493 -0
  20. data/lib/x/resources/direct_message.rb +325 -0
  21. data/lib/x/resources/direct_message_conversations.rb +147 -0
  22. data/lib/x/resources/errors.rb +104 -0
  23. data/lib/x/resources/finders.rb +255 -0
  24. data/lib/x/resources/identity.rb +65 -0
  25. data/lib/x/resources/includes.rb +216 -0
  26. data/lib/x/resources/list.rb +336 -0
  27. data/lib/x/resources/lookups/communities.rb +56 -0
  28. data/lib/x/resources/lookups/direct_messages.rb +86 -0
  29. data/lib/x/resources/lookups/lists.rb +44 -0
  30. data/lib/x/resources/lookups/media.rb +72 -0
  31. data/lib/x/resources/lookups/posts.rb +198 -0
  32. data/lib/x/resources/lookups/spaces.rb +87 -0
  33. data/lib/x/resources/lookups/trends.rb +38 -0
  34. data/lib/x/resources/lookups/users.rb +221 -0
  35. data/lib/x/resources/lookups.rb +25 -0
  36. data/lib/x/resources/marshalling.rb +93 -0
  37. data/lib/x/resources/matching_rule.rb +107 -0
  38. data/lib/x/resources/media.rb +278 -0
  39. data/lib/x/resources/media_ids.rb +74 -0
  40. data/lib/x/resources/memo.rb +54 -0
  41. data/lib/x/resources/page.rb +394 -0
  42. data/lib/x/resources/page_limit.rb +80 -0
  43. data/lib/x/resources/pages.rb +270 -0
  44. data/lib/x/resources/parallel.rb +82 -0
  45. data/lib/x/resources/personalized_trend.rb +124 -0
  46. data/lib/x/resources/place.rb +107 -0
  47. data/lib/x/resources/poll.rb +75 -0
  48. data/lib/x/resources/post.rb +615 -0
  49. data/lib/x/resources/post_collections.rb +67 -0
  50. data/lib/x/resources/post_counts.rb +215 -0
  51. data/lib/x/resources/post_search.rb +86 -0
  52. data/lib/x/resources/post_usage.rb +203 -0
  53. data/lib/x/resources/post_writes.rb +140 -0
  54. data/lib/x/resources/published_count.rb +31 -0
  55. data/lib/x/resources/references.rb +121 -0
  56. data/lib/x/resources/relation_writes.rb +54 -0
  57. data/lib/x/resources/relationships.rb +77 -0
  58. data/lib/x/resources/resource.rb +535 -0
  59. data/lib/x/resources/serialization.rb +58 -0
  60. data/lib/x/resources/shape.rb +167 -0
  61. data/lib/x/resources/space.rb +332 -0
  62. data/lib/x/resources/topic.rb +59 -0
  63. data/lib/x/resources/trend.rb +130 -0
  64. data/lib/x/resources/user.rb +502 -0
  65. data/lib/x/resources/user_collections.rb +213 -0
  66. data/lib/x/resources/user_finders.rb +282 -0
  67. data/lib/x/resources/utils.rb +358 -0
  68. data/lib/x/resources/value_equality.rb +38 -0
  69. data/lib/x/resources/value_marshalling.rb +89 -0
  70. data/lib/x/resources/version.rb +25 -0
  71. data/lib/x/resources.rb +22 -0
  72. data/sig/manifest.yaml +7 -0
  73. data/sig/x-resources.rbs +813 -0
  74. metadata +140 -0
@@ -0,0 +1,336 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require_relative "cursor"
5
+ require_relative "finders"
6
+ require_relative "page_limit"
7
+ require_relative "resource"
8
+
9
+ module X
10
+ module Resources
11
+ # A curated list of users
12
+ # @api public
13
+ class ::X::List < Resource
14
+ extend Finders
15
+
16
+ # The mixins of the class by their full names, which YARD needs to resolve them in a class opened under X
17
+ #
18
+ # @!parse
19
+ # extend X::Resources::Finders
20
+
21
+ # Every public list field
22
+ #
23
+ # A minor release may add to it the fields the API adds, so that a lookup asks for them too; see
24
+ # {Resource#hydrated?} for what that means for a resource looked up with a list of fields of its own.
25
+ FIELDS = %w[created_at description follower_count id member_count name private].freeze
26
+ # Every expansion available on list endpoints
27
+ #
28
+ # A minor release may add to it the expansions the API adds, so that a lookup asks for them too; see
29
+ # {Resource#hydrated?} for what that means for a resource looked up with a list of expansions of its own.
30
+ EXPANSIONS = %w[owner_id].freeze
31
+ # Maximum number of users or posts per page
32
+ # @api private
33
+ MAX_RESULTS = 100
34
+ private_constant :MAX_RESULTS
35
+
36
+ class << self
37
+ # The API endpoint used to look up lists by identifier
38
+ #
39
+ # @api private
40
+ # @return [String] the endpoint
41
+ # @example Get the endpoint
42
+ # X::List.__send__(:endpoint) # => "lists"
43
+ def endpoint
44
+ "lists"
45
+ end
46
+
47
+ # The query parameter that selects list fields
48
+ #
49
+ # @api private
50
+ # @return [String] the fields parameter
51
+ # @example Get the fields parameter
52
+ # X::List.__send__(:fields_key) # => "list.fields"
53
+ def fields_key = "list.fields"
54
+
55
+ private :endpoint, :fields_key
56
+
57
+ # The default query parameters requesting every list field and expansion
58
+ #
59
+ # @api public
60
+ # @return [Hash{String => Array<String>}] the default query parameters
61
+ # @example Get the default parameters
62
+ # X::List.default_params["list.fields"]
63
+ def default_params
64
+ {"list.fields" => FIELDS, "user.fields" => User::FIELDS, "expansions" => EXPANSIONS}
65
+ end
66
+
67
+ # Create a list owned by the authenticated user
68
+ #
69
+ # @api public
70
+ # @param name [String] the name of the list
71
+ # @param client [Object] the client used to make the request
72
+ # @param params [Hash] additional request body fields: description and private
73
+ # @return [List] the created list, holding only its identifier and name
74
+ # @raise [MissingResource] if the API answers without the list
75
+ # @example Create a private list
76
+ # X::List.create("Rubyists", client: client, description: "People who write Ruby", private: true)
77
+ def create(name, client:, **params)
78
+ body = client.post("lists", {name:, **params}, **Utils::JSON_CLASSES)
79
+ created_from_response(body, "POST lists", client:)
80
+ end
81
+
82
+ # Update the name, description, or privacy of a list as the authenticated user
83
+ #
84
+ # @api public
85
+ # @param list [List, String, Integer] the list or its identifier
86
+ # @param client [Object] the client used to make the request
87
+ # @param params [Hash] the request body fields to change: name, description, and private
88
+ # @return [Boolean] true if the list was updated
89
+ # @raise [ArgumentError] if no field is given to change, before any request
90
+ # @example Rename a list and make it private
91
+ # X::List.update("1234567890", client: client, name: "Rubyists", private: true)
92
+ def update(list, client:, **params)
93
+ raise ArgumentError, "a list update needs a field to change, such as name, description, or private" if params.empty?
94
+
95
+ body = client.put("lists/#{Utils.id_of(list, self)}", params, **Utils::JSON_CLASSES)
96
+ Utils.written(body, "updated").eql?(true)
97
+ end
98
+
99
+ # Delete a list as the authenticated user
100
+ #
101
+ # @api public
102
+ # @param list [List, String, Integer] the list or its identifier
103
+ # @param client [Object] the client used to make the request
104
+ # @return [Boolean] true if the list was deleted
105
+ # @example Delete a list
106
+ # X::List.delete("1234567890", client: client)
107
+ def delete(list, client:)
108
+ body = client.delete("lists/#{Utils.id_of(list, self)}", **Utils::JSON_CLASSES)
109
+ Utils.written(body, "deleted").eql?(true)
110
+ end
111
+ end
112
+
113
+ # @!attribute [r] name
114
+ # The name
115
+ # @api public
116
+ # @return [String, nil] the name
117
+ # @example Get the name
118
+ # list.name
119
+ attribute :name
120
+
121
+ # @!attribute [r] description
122
+ # The description
123
+ # @api public
124
+ # @return [String, nil] the description
125
+ # @example Get the description
126
+ # list.description
127
+ attribute :description
128
+
129
+ # @!attribute [r] created_at
130
+ # The time when the list was created
131
+ # @api public
132
+ # @return [Time, nil] the creation time
133
+ # @example Get the creation time
134
+ # list.created_at
135
+ attribute :created_at, :time
136
+
137
+ # @!attribute [r] follower_count
138
+ # The number of followers
139
+ # @api public
140
+ # @return [Integer, nil] the follower count
141
+ # @example Get the follower count
142
+ # list.follower_count
143
+ attribute :follower_count, :integer
144
+
145
+ # @!attribute [r] member_count
146
+ # The number of members
147
+ # @api public
148
+ # @return [Integer, nil] the member count
149
+ # @example Get the member count
150
+ # list.member_count
151
+ attribute :member_count, :integer
152
+
153
+ # @!attribute [r] owner_id
154
+ # The identifier of the owner
155
+ # @api public
156
+ # @return [Integer, nil] the owner identifier
157
+ # @example Get the owner identifier
158
+ # list.owner_id
159
+ attribute :owner_id, :integer
160
+
161
+ # @!attribute [r] private
162
+ # Whether the list is private
163
+ # @api public
164
+ # @return [Boolean, nil] true if the list is private
165
+ # @example Check whether a list is private
166
+ # list.private?
167
+ attribute :private, :boolean
168
+
169
+ # @!method private?
170
+ # Check whether the list is private
171
+ # @api public
172
+ # @return [Boolean] true if the list is private
173
+ # @example Check whether the list is private
174
+ # list.private?
175
+
176
+ # @!method owner
177
+ # The owner, resolved from the includes or as a stub holding only its identifier
178
+ # @api public
179
+ # @return [User, nil] the owner
180
+ # @example Get the owner's username
181
+ # list.owner.username
182
+ reference :owner, :User, key: %w[owner_id]
183
+
184
+ # The members of this list
185
+ #
186
+ # @api public
187
+ # @param params [Hash] query parameters merged over the default parameters
188
+ # @return [Cursor] a cursor over the members
189
+ # @example Print every member
190
+ # list.members.each { |user| puts user.username }
191
+ def members(**params)
192
+ cursor(User, "lists/#{id}/members", max_results: MAX_RESULTS, total: :member_count, **params)
193
+ end
194
+
195
+ # The followers of this list
196
+ #
197
+ # @api public
198
+ # @param params [Hash] query parameters merged over the default parameters
199
+ # @return [Cursor] a cursor over the followers
200
+ # @example Print every follower
201
+ # list.followers.each { |user| puts user.username }
202
+ def followers(**params)
203
+ cursor(User, "lists/#{id}/followers", max_results: MAX_RESULTS, total: :follower_count, **params)
204
+ end
205
+
206
+ # The posts by members of this list
207
+ #
208
+ # @api public
209
+ # @param params [Hash] query parameters merged over the default parameters
210
+ # @return [Cursor] a cursor over the posts
211
+ # @example Print the most recent posts
212
+ # list.posts.first(10).each { |post| puts post.text }
213
+ def posts(**params)
214
+ cursor(Post, "lists/#{id}/tweets", max_results: MAX_RESULTS, **params)
215
+ end
216
+
217
+ # Check whether a user is a member of this list, scanning until one matches
218
+ #
219
+ # The API has no lookup for a membership, so this scans either the members of the list or the lists
220
+ # the user is on. A private list scans its members, since a user's memberships leave private lists
221
+ # out. A public list scans the lists the user is on when there are fewer of them than members, as its
222
+ # member_count and the user's listed_count tell, looking up the list or the user first when either is
223
+ # a stub. The API bills every resource a scan returns, so max_pages limits the pages it reads, raising
224
+ # PageLimitReached rather than read past them.
225
+ #
226
+ # @api public
227
+ # @param user [User, String, Integer] the user or their identifier
228
+ # @param max_pages [Integer, nil] the most pages of members or memberships to read, or nil for no limit
229
+ # @return [Boolean] true if the user is a member
230
+ # @raise [ArgumentError] if max_pages is neither an Integer of at least 1 nor nil, before a request
231
+ # @raise [PageLimitReached] if the scan reads max_pages pages without the user, and the API names another
232
+ # @example Check whether a user is on a list
233
+ # list.member?(user)
234
+ # @example Read no more than ten pages to tell
235
+ # list.member?(user, max_pages: 10)
236
+ def member?(user, max_pages: nil)
237
+ member, max_pages = User.from_id(user), PageLimit.check!(max_pages)
238
+ return PageLimit.scan(members.stubs, member, what: "List#member?", max_pages:) unless fewer_memberships?(user)
239
+
240
+ PageLimit.scan(User.from_id(member, client: client!).list_memberships.stubs, self, what: "List#member?", max_pages:)
241
+ end
242
+
243
+ # The permalink of the list
244
+ #
245
+ # @api public
246
+ # @return [String] the x.com address of the list
247
+ # @example Get the permalink
248
+ # list.permalink # => "https://x.com/i/lists/1234567890"
249
+ def permalink = "https://x.com/i/lists/#{id}"
250
+
251
+ # The permalink of the list as a URI
252
+ #
253
+ # @api public
254
+ # @return [URI::Generic] the x.com address of the list
255
+ # @example Get the address as a URI
256
+ # list.uri # => #<URI::HTTPS https://x.com/i/lists/1234567890>
257
+ def uri = URI(permalink)
258
+
259
+ # Add a member to this list as the authenticated user
260
+ #
261
+ # @api public
262
+ # @param user [User, String, Integer] the user or their identifier
263
+ # @return [Boolean] true if the user is now a member
264
+ # @example Add a member
265
+ # list.add_member(user)
266
+ def add_member(user)
267
+ body = client!.post("lists/#{id}/members", {user_id: Utils.id_of(user, User)}, **Utils::JSON_CLASSES)
268
+ Utils.written(body, "is_member").eql?(true)
269
+ end
270
+
271
+ # Remove a member from this list as the authenticated user
272
+ #
273
+ # @api public
274
+ # @param user [User, String, Integer] the user or their identifier
275
+ # @return [Boolean] true if the user is no longer a member
276
+ # @example Remove a member
277
+ # list.remove_member(user)
278
+ def remove_member(user)
279
+ body = client!.delete("lists/#{id}/members/#{Utils.id_of(user, User)}", **Utils::JSON_CLASSES)
280
+ Utils.written(body, "is_member").eql?(false)
281
+ end
282
+
283
+ # Update the name, description, or privacy of this list as the authenticated user
284
+ #
285
+ # The list keeps the attributes it was built with, so refresh it to read the new ones.
286
+ #
287
+ # @api public
288
+ # @param params [Hash] the request body fields to change: name, description, and private
289
+ # @return [Boolean] true if the list was updated
290
+ # @raise [ArgumentError] if no field is given to change, before any request
291
+ # @example Change the description of a list
292
+ # list.update(description: "People who write Ruby")
293
+ def update(**params)
294
+ self.class.update(self, client: client!, **params)
295
+ end
296
+
297
+ # Delete this list as the authenticated user
298
+ #
299
+ # @api public
300
+ # @return [Boolean] true if the list was deleted
301
+ # @example Delete a list
302
+ # list.delete
303
+ def delete
304
+ self.class.delete(self, client: client!)
305
+ end
306
+
307
+ alias_method :tweets, :posts
308
+
309
+ private
310
+
311
+ # Check whether the user is on fewer lists than this public list has members
312
+ # @api private
313
+ # @param user [User, String, Integer] the user or their identifier
314
+ # @return [Boolean] true if the lists the user is on are fewer than the members of this public list
315
+ def fewer_memberships?(user)
316
+ list = hydrate
317
+ return false if list.nil? || list.private?
318
+
319
+ member_count = list.member_count
320
+ return false if member_count.nil?
321
+
322
+ listed_count = listed_count_of(user)
323
+ !listed_count.nil? && listed_count < member_count
324
+ end
325
+
326
+ # The number of lists a user is on, looking up a user that is not hydrated
327
+ # @api private
328
+ # @param user [User, String, Integer] the user or their identifier
329
+ # @return [Integer, nil] the listed count, or nil if the user was not found
330
+ def listed_count_of(user)
331
+ known = user if user.is_a?(User) && user.hydrated?
332
+ (known || User.find(User.from_id(user), client: client!))&.listed_count
333
+ end
334
+ end
335
+ end
336
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../community"
4
+
5
+ module X
6
+ module Resources
7
+ module Lookups
8
+ # Look up and search communities, mixed into a client through API
9
+ #
10
+ # Internal to x-resources: X::Resources::API includes it, and its methods are public API of the client that
11
+ # includes API, but the module is only how they are grouped, and some of them need the methods of another,
12
+ # so include API rather than this module alone.
13
+ #
14
+ # @api semipublic
15
+ module Communities
16
+ # Look up a community by identifier
17
+ #
18
+ # @api public
19
+ # @param id [String, Integer, Community] the identifier
20
+ # @param params [Hash] query parameters merged over the default parameters
21
+ # @return [Community, nil] the community or nil if the community was not found
22
+ # @yieldparam problem [Problem] each problem the API reported, such as a resource that was not found
23
+ # @example Look up a community
24
+ # client.find_community(1234567890).name
25
+ def find_community(id, **params, &)
26
+ Community.find(id, client: self, **params, &)
27
+ end
28
+
29
+ # Look up a community by identifier, which must exist
30
+ #
31
+ # @api public
32
+ # @param id [String, Integer, Community] the identifier
33
+ # @param params [Hash] query parameters merged over the default parameters
34
+ # @return [Community] the community
35
+ # @raise [MissingResource] if the community was not found
36
+ # @example Look up a community
37
+ # client.find_community!(1234567890).name
38
+ def find_community!(id, **params)
39
+ Community.find!(id, client: self, **params)
40
+ end
41
+
42
+ # Search communities
43
+ #
44
+ # @api public
45
+ # @param query [String] the search query
46
+ # @param params [Hash] query parameters merged over the default parameters
47
+ # @return [Cursor] a cursor over the matching communities
48
+ # @example Print the communities matching a query
49
+ # client.search_communities("ruby").each { |community| puts community.name }
50
+ def search_communities(query, **params)
51
+ Community.search(query, client: self, **params)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../direct_message"
4
+
5
+ module X
6
+ module Resources
7
+ module Lookups
8
+ # Look up direct messages, and the conversations they belong to, mixed into a client through API
9
+ #
10
+ # Internal to x-resources: X::Resources::API includes it, and its methods are public API of the client that
11
+ # includes API, but the module is only how they are grouped, and some of them need the methods of another,
12
+ # so include API rather than this module alone.
13
+ #
14
+ # @api semipublic
15
+ module DirectMessages
16
+ # Look up a direct message event by identifier
17
+ #
18
+ # @api public
19
+ # @param id [String, Integer, DirectMessage] the identifier
20
+ # @param params [Hash] query parameters merged over the default parameters
21
+ # @return [DirectMessage, nil] the event or nil if the event was not found
22
+ # @yieldparam problem [Problem] each problem the API reported, such as a resource that was not found
23
+ # @example Look up a direct message
24
+ # client.find_direct_message(1234567890).text
25
+ def find_direct_message(id, **params, &)
26
+ DirectMessage.find(id, client: self, **params, &)
27
+ end
28
+
29
+ # Look up a direct message event by identifier, which must exist
30
+ #
31
+ # @api public
32
+ # @param id [String, Integer, DirectMessage] the identifier
33
+ # @param params [Hash] query parameters merged over the default parameters
34
+ # @return [DirectMessage] the event
35
+ # @raise [MissingResource] if the event was not found
36
+ # @example Look up a direct message
37
+ # client.find_direct_message!(1234567890).text
38
+ def find_direct_message!(id, **params)
39
+ DirectMessage.find!(id, client: self, **params)
40
+ end
41
+
42
+ # The most recent direct message events across every conversation
43
+ #
44
+ # @api public
45
+ # @param params [Hash] query parameters merged over the default parameters
46
+ # @return [Cursor] a cursor over the events
47
+ # @example Print the most recent direct messages
48
+ # client.direct_messages.first(10).each { |message| puts message.text }
49
+ def direct_messages(**params)
50
+ DirectMessage.all(client: self, **params)
51
+ end
52
+
53
+ # The direct message events in the one-to-one conversation with a user
54
+ #
55
+ # @api public
56
+ # @param user [User, String, Integer] the other participant or their identifier
57
+ # @param params [Hash] query parameters merged over the default parameters
58
+ # @return [Cursor] a cursor over the events
59
+ # @example Print the conversation with a user
60
+ # client.direct_messages_with(user).each { |message| puts message.text }
61
+ def direct_messages_with(user, **params)
62
+ DirectMessage.with(user, client: self, **params)
63
+ end
64
+
65
+ # The direct message events of a conversation, one-to-one or group
66
+ #
67
+ # @api public
68
+ # @param conversation [DirectMessage, String, Integer] a message of the conversation, or the conversation's identifier
69
+ # @param params [Hash] query parameters merged over the default parameters, such as event_types
70
+ # @return [Cursor] a cursor over the events
71
+ # @raise [ArgumentError] if the conversation identifier is not one
72
+ # @example Print the conversation a message belongs to
73
+ # client.direct_messages_in(message).each { |event| puts event.text }
74
+ def direct_messages_in(conversation, **params)
75
+ DirectMessage.in(conversation, client: self, **params)
76
+ end
77
+
78
+ alias_method :find_dm, :find_direct_message
79
+ alias_method :find_dm!, :find_direct_message!
80
+ alias_method :dms, :direct_messages
81
+ alias_method :dms_with, :direct_messages_with
82
+ alias_method :dms_in, :direct_messages_in
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../list"
4
+
5
+ module X
6
+ module Resources
7
+ module Lookups
8
+ # Look up lists, mixed into a client through API
9
+ #
10
+ # Internal to x-resources: X::Resources::API includes it, and its methods are public API of the client that
11
+ # includes API, but the module is only how they are grouped, and some of them need the methods of another,
12
+ # so include API rather than this module alone.
13
+ #
14
+ # @api semipublic
15
+ module Lists
16
+ # Look up a list by identifier
17
+ #
18
+ # @api public
19
+ # @param id [String, Integer, List] the identifier
20
+ # @param params [Hash] query parameters merged over the default parameters
21
+ # @return [List, nil] the list or nil if the list was not found
22
+ # @yieldparam problem [Problem] each problem the API reported, such as a resource that was not found
23
+ # @example Look up a list
24
+ # client.find_list(1234567890).name
25
+ def find_list(id, **params, &)
26
+ List.find(id, client: self, **params, &)
27
+ end
28
+
29
+ # Look up a list by identifier, which must exist
30
+ #
31
+ # @api public
32
+ # @param id [String, Integer, List] the identifier
33
+ # @param params [Hash] query parameters merged over the default parameters
34
+ # @return [List] the list
35
+ # @raise [MissingResource] if the list was not found
36
+ # @example Look up a list
37
+ # client.find_list!(1234567890).name
38
+ def find_list!(id, **params)
39
+ List.find!(id, client: self, **params)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../batch_finders"
4
+ require_relative "../media"
5
+
6
+ module X
7
+ module Resources
8
+ module Lookups
9
+ # Look up media by media key
10
+ #
11
+ # Internal to x-resources: X::Resources::API includes it, and its methods are public API of the client that
12
+ # includes API, but the module is only how they are grouped, and some of them need the methods of another,
13
+ # so include API rather than this module alone.
14
+ #
15
+ # @api semipublic
16
+ module Media
17
+ # Look up media by media key
18
+ #
19
+ # A post refers to its media by media key, and so does what an upload returns, so this reads the photo,
20
+ # video, or animated GIF that was uploaded, with its URL and variants.
21
+ #
22
+ # @api public
23
+ # @param media_key [String, X::Media, #media_key] the media key, such as 3_1880028106020515840, media, or what
24
+ # an upload returned
25
+ # @param params [Hash] query parameters merged over the default parameters; one that overrides a default field
26
+ # parameter to leave some out builds media that is not hydrated, so hydrate fetches the rest
27
+ # @return [X::Media, nil] the media, or nil if it was not found
28
+ # @yieldparam problem [Problem] each problem the API reported
29
+ # @example Look up media by media key
30
+ # client.find_media("3_1880028106020515840")
31
+ # @example Look up media that was uploaded
32
+ # client.find_media(uploaded)
33
+ def find_media(media_key, **params, &)
34
+ X::Media.find(media_key, client: self, **params, &)
35
+ end
36
+
37
+ # Look up media by media key, in parallel batches
38
+ #
39
+ # @api public
40
+ # @param media [Array<String, X::Media, #media_key>] the media keys, or the media, or what the uploads
41
+ # returned, whose keys are taken
42
+ # @param concurrency [Integer] the number of batches looked up at once, which must be at least one; each is
43
+ # a request of up to 100 media keys, so a lower number spends a rate limit more slowly
44
+ # @param params [Hash] query parameters merged over the default parameters; one that overrides a default
45
+ # field parameter builds media that is not hydrated, so hydrate fetches the rest
46
+ # @return [Array<X::Media>] the media that was found
47
+ # @raise [ArgumentError] if the concurrency is less than one
48
+ # @yieldparam problem [Problem] each problem the API reported, such as a media key that was not found
49
+ # @example Look up the media of a post
50
+ # client.find_all_media(post.media)
51
+ # @example Look up media by media key
52
+ # client.find_all_media(%w[3_1880028106020515840 3_1880028106020515841])
53
+ def find_all_media(media, concurrency: BatchFinders::DEFAULT_CONCURRENCY, **params, &)
54
+ X::Media.find_all(media, client: self, concurrency:, **params, &)
55
+ end
56
+
57
+ # Look up media by media key, raising if it is not found
58
+ #
59
+ # @api public
60
+ # @param media_key [String, X::Media, #media_key] the media key, media, or what an upload returned
61
+ # @param params [Hash] query parameters merged over the default parameters
62
+ # @return [X::Media] the media
63
+ # @raise [MissingResource] if the media was not found
64
+ # @example Look up media that must exist
65
+ # client.find_media!("3_1880028106020515840")
66
+ def find_media!(media_key, **params)
67
+ X::Media.find!(media_key, client: self, **params)
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end