twitter 6.2.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -7
  3. data/LICENSE.md +1 -1
  4. data/README.md +9 -12
  5. data/lib/twitter.rb +5 -0
  6. data/lib/twitter/client.rb +5 -5
  7. data/lib/twitter/creatable.rb +5 -1
  8. data/lib/twitter/cursor.rb +12 -2
  9. data/lib/twitter/direct_message.rb +2 -0
  10. data/lib/twitter/direct_message_event.rb +44 -0
  11. data/lib/twitter/direct_messages/welcome_message.rb +17 -0
  12. data/lib/twitter/direct_messages/welcome_message_rule.rb +12 -0
  13. data/lib/twitter/direct_messages/welcome_message_rule_wrapper.rb +36 -0
  14. data/lib/twitter/direct_messages/welcome_message_wrapper.rb +42 -0
  15. data/lib/twitter/enumerable.rb +14 -3
  16. data/lib/twitter/error.rb +48 -6
  17. data/lib/twitter/null_object.rb +15 -1
  18. data/lib/twitter/premium_search_results.rb +67 -0
  19. data/lib/twitter/profile.rb +2 -2
  20. data/lib/twitter/rate_limit.rb +2 -2
  21. data/lib/twitter/rest/account_activity.rb +99 -0
  22. data/lib/twitter/rest/api.rb +6 -0
  23. data/lib/twitter/rest/direct_messages.rb +131 -42
  24. data/lib/twitter/rest/direct_messages/welcome_messages.rb +90 -0
  25. data/lib/twitter/rest/favorites.rb +2 -2
  26. data/lib/twitter/rest/lists.rb +1 -0
  27. data/lib/twitter/rest/premium_search.rb +34 -0
  28. data/lib/twitter/rest/request.rb +39 -13
  29. data/lib/twitter/rest/search.rb +4 -3
  30. data/lib/twitter/rest/timelines.rb +1 -0
  31. data/lib/twitter/rest/tweets.rb +2 -31
  32. data/lib/twitter/rest/upload_utils.rb +68 -0
  33. data/lib/twitter/rest/utils.rb +24 -7
  34. data/lib/twitter/search_results.rb +4 -3
  35. data/lib/twitter/streaming/client.rb +5 -1
  36. data/lib/twitter/streaming/connection.rb +16 -3
  37. data/lib/twitter/streaming/message_parser.rb +1 -1
  38. data/lib/twitter/streaming/response.rb +1 -0
  39. data/lib/twitter/tweet.rb +10 -1
  40. data/lib/twitter/utils.rb +4 -2
  41. data/lib/twitter/version.rb +2 -2
  42. data/twitter.gemspec +3 -4
  43. metadata +16 -21
@@ -0,0 +1,90 @@
1
+ require 'twitter/arguments'
2
+ require 'twitter/rest/upload_utils'
3
+ require 'twitter/rest/utils'
4
+ require 'twitter/utils'
5
+
6
+ module Twitter
7
+ module REST
8
+ module DirectMessages
9
+ module WelcomeMessages
10
+ include Twitter::REST::UploadUtils
11
+ include Twitter::REST::Utils
12
+ include Twitter::Utils
13
+
14
+ # Welcome Message
15
+
16
+ def create_welcome_message(text, name = nil, options = {})
17
+ json_options = {
18
+ welcome_message: {
19
+ message_data: {
20
+ text: text,
21
+ },
22
+ },
23
+ }
24
+ json_options[:welcome_message][:name] = name if name
25
+ welcome_message_wrapper = perform_request_with_object(:json_post, '/1.1/direct_messages/welcome_messages/new.json', json_options.merge!(options), Twitter::DirectMessages::WelcomeMessageWrapper)
26
+ welcome_message_wrapper.welcome_message
27
+ end
28
+
29
+ def destroy_welcome_message(*ids)
30
+ perform_requests(:delete, '/1.1/direct_messages/welcome_messages/destroy.json', ids)
31
+ end
32
+
33
+ def update_welcome_message(welcome_message_id, text, options = {})
34
+ params = {
35
+ id: welcome_message_id,
36
+ }
37
+ json_options = {
38
+ message_data: {
39
+ text: text,
40
+ },
41
+ }
42
+ welcome_message_wrapper = perform_request_with_object(:json_put, '/1.1/direct_messages/welcome_messages/update.json', json_options.merge!(options), Twitter::DirectMessages::WelcomeMessageWrapper, params)
43
+ welcome_message_wrapper.welcome_message
44
+ end
45
+
46
+ def welcome_message(id, options = {})
47
+ options = options.dup
48
+ options[:id] = id
49
+ welcome_message_wrapper = perform_get_with_object('/1.1/direct_messages/welcome_messages/show.json', options, Twitter::DirectMessages::WelcomeMessageWrapper)
50
+ welcome_message_wrapper.welcome_message
51
+ end
52
+
53
+ def welcome_message_list(options = {})
54
+ limit = options.fetch(:count, 20)
55
+ welcome_message_wrappers = perform_get_with_cursor('/1.1/direct_messages/welcome_messages/list.json', options.merge!(no_default_cursor: true, count: 50, limit: limit), :welcome_messages, Twitter::DirectMessages::WelcomeMessageWrapper)
56
+ welcome_message_wrappers.collect(&:welcome_message)
57
+ end
58
+
59
+ # Welcome Message Rule
60
+
61
+ def create_welcome_message_rule(welcome_message_id, options = {})
62
+ json_options = {
63
+ welcome_message_rule: {
64
+ welcome_message_id: welcome_message_id,
65
+ },
66
+ }
67
+ rule_wrapper = perform_request_with_object(:json_post, '/1.1/direct_messages/welcome_messages/rules/new.json', json_options.merge!(options), Twitter::DirectMessages::WelcomeMessageRuleWrapper)
68
+ rule_wrapper.welcome_message_rule
69
+ end
70
+
71
+ def destroy_welcome_message_rule(*ids)
72
+ perform_requests(:delete, '/1.1/direct_messages/welcome_messages/rules/destroy.json', ids)
73
+ end
74
+
75
+ def welcome_message_rule(id, options = {})
76
+ options = options.dup
77
+ options[:id] = id
78
+ rule_wrapper = perform_get_with_object('/1.1/direct_messages/welcome_messages/rules/show.json', options, Twitter::DirectMessages::WelcomeMessageRuleWrapper)
79
+ rule_wrapper.welcome_message_rule
80
+ end
81
+
82
+ def welcome_message_rule_list(options = {})
83
+ limit = options.fetch(:count, 20)
84
+ rule_wrappers = perform_get_with_cursor('/1.1/direct_messages/welcome_messages/rules/list.json', options.merge!(no_default_cursor: true, count: 50, limit: limit), :welcome_message_rules, Twitter::DirectMessages::WelcomeMessageRuleWrapper)
85
+ rule_wrappers.collect(&:welcome_message_rule)
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -20,14 +20,14 @@ module Twitter
20
20
  # Returns the 20 most recent favorite Tweets for the authenticating user
21
21
  #
22
22
  # @param options [Hash] A customizable set of options.
23
- # @option options [Integer] :count Specifies the number of records to retrieve. Must be less than or equal to 100.
23
+ # @option options [Integer] :count Specifies the number of records to retrieve. Must be less than or equal to 200.
24
24
  # @option options [Integer] :since_id Returns results with an ID greater than (that is, more recent than) the specified ID.
25
25
  # @overload favorites(user, options = {})
26
26
  # Returns the 20 most recent favorite Tweets for the specified user
27
27
  #
28
28
  # @param user [Integer, String, Twitter::User] A Twitter user ID, screen name, URI, or object.
29
29
  # @param options [Hash] A customizable set of options.
30
- # @option options [Integer] :count Specifies the number of records to retrieve. Must be less than or equal to 100.
30
+ # @option options [Integer] :count Specifies the number of records to retrieve. Must be less than or equal to 200.
31
31
  # @option options [Integer] :since_id Returns results with an ID greater than (that is, more recent than) the specified ID.
32
32
  def favorites(*args)
33
33
  arguments = Twitter::Arguments.new(args)
@@ -484,6 +484,7 @@ module Twitter
484
484
  # @return [Hash]
485
485
  def merge_owner!(hash, user)
486
486
  return hash if hash[:owner_id] || hash[:owner_screen_name]
487
+
487
488
  if user
488
489
  merge_user!(hash, user, 'owner')
489
490
  hash[:owner_id] = hash.delete(:owner_user_id) unless hash[:owner_user_id].nil?
@@ -0,0 +1,34 @@
1
+ require 'twitter/rest/request'
2
+ require 'twitter/premium_search_results'
3
+
4
+ module Twitter
5
+ module REST
6
+ module PremiumSearch
7
+ MAX_TWEETS_PER_REQUEST = 100
8
+
9
+ # Returns tweets from the 30-Day API that match a specified query.
10
+ #
11
+ # @see https://developer.twitter.com/en/docs/tweets/search/overview/premium
12
+ # @see https://developer.twitter.com/en/docs/tweets/search/api-reference/premium-search.html#DataEndpoint
13
+ # @rate_limited Yes
14
+ # @raise [Twitter::Error::Unauthorized] Error raised when supplied user credentials are not valid.
15
+ # @param query [String] A search term.
16
+ # @param options [Hash] A customizable set of options.
17
+ # @option options [String] :tag Tags can be used to segregate rules and their matching data into different logical groups.
18
+ # @option options [Integer] :maxResults The maximum number of search results to be returned by a request. A number between 10 and the system limit (currently 500, 100 for Sandbox environments). By default, a request response will return 100 results
19
+ # @option options [String] :fromDate The oldest UTC timestamp (from most recent 30 days) from which the Tweets will be provided. Date should be formatted as yyyymmddhhmm.
20
+ # @option options [String] :toDate The latest, most recent UTC timestamp to which the activities will be provided. Date should be formatted as yyyymmddhhmm.
21
+ # @option request_config [String] :product Indicates the search endpoint you are making requests to, either 30day or fullarchive. Default 30day
22
+ # @return [Twitter::PremiumSearchResults] Return tweets that match a specified query with search metadata
23
+ def premium_search(query, options = {}, request_config = {})
24
+ options = options.clone
25
+ options[:maxResults] ||= MAX_TWEETS_PER_REQUEST
26
+ request_config[:request_method] = :json_post if request_config[:request_method].nil? || request_config[:request_method] == :post
27
+ request_config[:product] ||= '30day'
28
+ path = "/1.1/tweets/search/#{request_config[:product]}/#{dev_environment}.json"
29
+ request = Twitter::REST::Request.new(self, request_config[:request_method], path, options.merge(query: query))
30
+ Twitter::PremiumSearchResults.new(request, request_config)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -10,7 +10,7 @@ require 'twitter/utils'
10
10
 
11
11
  module Twitter
12
12
  module REST
13
- class Request
13
+ class Request # rubocop:disable Metrics/ClassLength
14
14
  include Twitter::Utils
15
15
  BASE_URL = 'https://api.twitter.com'.freeze
16
16
  attr_accessor :client, :headers, :options, :path, :rate_limit,
@@ -22,18 +22,20 @@ module Twitter
22
22
  # @param path [String]
23
23
  # @param options [Hash]
24
24
  # @return [Twitter::REST::Request]
25
- def initialize(client, request_method, path, options = {})
25
+ def initialize(client, request_method, path, options = {}, params = nil)
26
26
  @client = client
27
27
  @uri = Addressable::URI.parse(path.start_with?('http') ? path : BASE_URL + path)
28
- set_multipart_options!(request_method, options)
28
+ multipart_options = params || options
29
+ set_multipart_options!(request_method, multipart_options)
29
30
  @path = uri.path
30
31
  @options = options
32
+ @options_key = {get: :params, json_post: :json, json_put: :json, delete: :params}[request_method] || :form
33
+ @params = params
31
34
  end
32
35
 
33
36
  # @return [Array, Hash]
34
37
  def perform
35
- options_key = @request_method == :get ? :params : :form
36
- response = http_client.headers(@headers).public_send(@request_method, @uri.to_s, options_key => @options)
38
+ response = http_client.headers(@headers).public_send(@request_method, @uri.to_s, request_options)
37
39
  response_body = response.body.empty? ? '' : symbolize_keys!(response.parse)
38
40
  response_headers = response.headers
39
41
  fail_or_return_response_body(response.code, response_body, response_headers)
@@ -41,29 +43,43 @@ module Twitter
41
43
 
42
44
  private
43
45
 
46
+ def request_options
47
+ options = {@options_key => @options}
48
+ if @params
49
+ if options[:params]
50
+ options[:params].merge(@params)
51
+ else
52
+ options[:params] = @params
53
+ end
54
+ end
55
+ options
56
+ end
57
+
44
58
  def merge_multipart_file!(options)
45
59
  key = options.delete(:key)
46
60
  file = options.delete(:file)
47
61
 
48
62
  options[key] = if file.is_a?(StringIO)
49
- HTTP::FormData::File.new(file, mime_type: 'video/mp4')
63
+ HTTP::FormData::File.new(file, content_type: 'video/mp4')
50
64
  else
51
- HTTP::FormData::File.new(file, filename: File.basename(file), mime_type: mime_type(File.basename(file)))
65
+ HTTP::FormData::File.new(file, filename: File.basename(file), content_type: content_type(File.basename(file)))
52
66
  end
53
67
  end
54
68
 
55
69
  def set_multipart_options!(request_method, options)
56
- if request_method == :multipart_post
57
- merge_multipart_file!(options)
70
+ if %i[multipart_post json_post].include?(request_method)
71
+ merge_multipart_file!(options) if request_method == :multipart_post
72
+ options = {}
58
73
  @request_method = :post
59
- @headers = Twitter::Headers.new(@client, @request_method, @uri).request_headers
74
+ elsif request_method == :json_put
75
+ @request_method = :put
60
76
  else
61
77
  @request_method = request_method
62
- @headers = Twitter::Headers.new(@client, @request_method, @uri, options).request_headers
63
78
  end
79
+ @headers = Twitter::Headers.new(@client, @request_method, @uri, options).request_headers
64
80
  end
65
81
 
66
- def mime_type(basename)
82
+ def content_type(basename)
67
83
  case basename
68
84
  when /\.gif$/i
69
85
  'image/gif'
@@ -79,6 +95,7 @@ module Twitter
79
95
  def fail_or_return_response_body(code, body, headers)
80
96
  error = error(code, body, headers)
81
97
  raise(error) if error
98
+
82
99
  @rate_limit = Twitter::RateLimit.new(headers)
83
100
  body
84
101
  end
@@ -89,6 +106,8 @@ module Twitter
89
106
  forbidden_error(body, headers)
90
107
  elsif !klass.nil?
91
108
  klass.from_response(body, headers)
109
+ elsif body&.is_a?(Hash) && (err = body.dig(:processing_info, :error))
110
+ Twitter::Error::MediaError.from_processing_response(err, headers)
92
111
  end
93
112
  end
94
113
 
@@ -115,10 +134,17 @@ module Twitter
115
134
  object
116
135
  end
117
136
 
137
+ # Returns boolean indicating if all the keys required by HTTP::Client are present in Twitter::Client#timeouts
138
+ #
139
+ # @return [Boolean]
140
+ def timeout_keys_defined
141
+ (%i[write connect read] - (@client.timeouts&.keys || [])).empty?
142
+ end
143
+
118
144
  # @return [HTTP::Client, HTTP]
119
145
  def http_client
120
146
  client = @client.proxy ? HTTP.via(*proxy) : HTTP
121
- client = client.timeout(:per_operation, connect: @client.timeouts[:connect], read: @client.timeouts[:read], write: @client.timeouts[:write]) if @client.timeouts
147
+ client = client.timeout(connect: @client.timeouts[:connect], read: @client.timeouts[:read], write: @client.timeouts[:write]) if timeout_keys_defined
122
148
  client
123
149
  end
124
150
 
@@ -14,7 +14,7 @@ module Twitter
14
14
  # @rate_limited Yes
15
15
  # @authentication Requires user context
16
16
  # @raise [Twitter::Error::Unauthorized] Error raised when supplied user credentials are not valid.
17
- # @param q [String] A search term.
17
+ # @param query [String] A search term.
18
18
  # @param options [Hash] A customizable set of options.
19
19
  # @option options [String] :geocode Returns tweets by users located within a given radius of the given latitude/longitude. The location is preferentially taking from the Geotagging API, but will fall back to their Twitter profile. The parameter value is specified by "latitude,longitude,radius", where radius units must be specified as either "mi" (miles) or "km" (kilometers). Note that you cannot use the near operator via the API to geocode arbitrary locations; however you can use this geocode parameter to search near geocodes directly.
20
20
  # @option options [String] :lang Restricts tweets to the given language, given by an ISO 639-1 code.
@@ -25,11 +25,12 @@ module Twitter
25
25
  # @option options [Integer] :since_id Returns results with an ID greater than (that is, more recent than) the specified ID. There are limits to the number of Tweets which can be accessed through the API. If the limit of Tweets has occured since the since_id, the since_id will be forced to the oldest ID available.
26
26
  # @option options [Integer] :max_id Returns results with an ID less than (that is, older than) or equal to the specified ID.
27
27
  # @option options [Boolean] :include_entities The entities node will be disincluded when set to false.
28
+ # @option options [String] :tweet_mode The entities node will truncate or not tweet text. Options are "compat" and "extended". The current default is "compat" (truncate).
28
29
  # @return [Twitter::SearchResults] Return tweets that match a specified query with search metadata
29
- def search(q, options = {})
30
+ def search(query, options = {})
30
31
  options = options.dup
31
32
  options[:count] ||= MAX_TWEETS_PER_REQUEST
32
- request = Twitter::REST::Request.new(self, :get, '/1.1/search/tweets.json', options.merge(q: q))
33
+ request = Twitter::REST::Request.new(self, :get, '/1.1/search/tweets.json', options.merge(q: query))
33
34
  Twitter::SearchResults.new(request)
34
35
  end
35
36
  end
@@ -188,6 +188,7 @@ module Twitter
188
188
  def collect_with_max_id(collection = [], max_id = nil, &block)
189
189
  tweets = yield(max_id)
190
190
  return collection if tweets.nil?
191
+
191
192
  collection += tweets
192
193
  tweets.empty? ? collection.flatten : collect_with_max_id(collection, tweets.last.id - 1, &block)
193
194
  end
@@ -2,6 +2,7 @@ require 'twitter/arguments'
2
2
  require 'twitter/error'
3
3
  require 'twitter/oembed'
4
4
  require 'twitter/rest/request'
5
+ require 'twitter/rest/upload_utils'
5
6
  require 'twitter/rest/utils'
6
7
  require 'twitter/tweet'
7
8
  require 'twitter/utils'
@@ -9,6 +10,7 @@ require 'twitter/utils'
9
10
  module Twitter
10
11
  module REST
11
12
  module Tweets
13
+ include Twitter::REST::UploadUtils
12
14
  include Twitter::REST::Utils
13
15
  include Twitter::Utils
14
16
  MAX_TWEETS_PER_REQUEST = 100
@@ -323,37 +325,6 @@ module Twitter
323
325
 
324
326
  private
325
327
 
326
- # Uploads images and videos. Videos require multiple requests and uploads in chunks of 5 Megabytes.
327
- # The only supported video format is mp4.
328
- #
329
- # @see https://dev.twitter.com/rest/public/uploading-media
330
- def upload(media) # rubocop:disable MethodLength, AbcSize
331
- if File.basename(media) !~ /\.mp4$/
332
- Twitter::REST::Request.new(self, :multipart_post, 'https://upload.twitter.com/1.1/media/upload.json', key: :media, file: media).perform
333
- else
334
- init = Twitter::REST::Request.new(self, :post, 'https://upload.twitter.com/1.1/media/upload.json',
335
- command: 'INIT',
336
- media_type: 'video/mp4',
337
- total_bytes: media.size).perform
338
-
339
- until media.eof?
340
- chunk = media.read(5_000_000)
341
- seg ||= -1
342
- Twitter::REST::Request.new(self, :multipart_post, 'https://upload.twitter.com/1.1/media/upload.json',
343
- command: 'APPEND',
344
- media_id: init[:media_id],
345
- segment_index: seg += 1,
346
- key: :media,
347
- file: StringIO.new(chunk)).perform
348
- end
349
-
350
- media.close
351
-
352
- Twitter::REST::Request.new(self, :post, 'https://upload.twitter.com/1.1/media/upload.json',
353
- command: 'FINALIZE', media_id: init[:media_id]).perform
354
- end
355
- end
356
-
357
328
  def array_wrap(object)
358
329
  if object.respond_to?(:to_ary)
359
330
  object.to_ary || [object]
@@ -0,0 +1,68 @@
1
+ require 'twitter/rest/request'
2
+
3
+ module Twitter
4
+ module REST
5
+ module UploadUtils
6
+ private
7
+
8
+ # Uploads images and videos. Videos require multiple requests and uploads in chunks of 5 Megabytes.
9
+ # The only supported video format is mp4.
10
+ #
11
+ # @see https://developer.twitter.com/en/docs/media/upload-media/uploading-media/media-best-practices
12
+ def upload(media, media_category_prefix: 'tweet')
13
+ return chunk_upload(media, 'video/mp4', "#{media_category_prefix}_video") if File.extname(media) == '.mp4'
14
+ return chunk_upload(media, 'image/gif', "#{media_category_prefix}_gif") if File.extname(media) == '.gif' && File.size(media) > 5_000_000
15
+
16
+ Twitter::REST::Request.new(self, :multipart_post, 'https://upload.twitter.com/1.1/media/upload.json', key: :media, file: media).perform
17
+ end
18
+
19
+ # @raise [Twitter::Error::TimeoutError] Error raised when the upload is longer than the value specified in Twitter::Client#timeouts[:upload].
20
+ # @raise [Twitter::Error::MediaError] Error raised when Twitter return an error about a media which is not mapped by the gem.
21
+ # @raise [Twitter::Error::MediaInternalError] Error raised when Twitter returns an InternalError error.
22
+ # @raise [Twitter::Error::InvalidMedia] Error raised when Twitter returns an InvalidMedia error.
23
+ # @raise [Twitter::Error::UnsupportedMedia] Error raised when Twitter returns an UnsupportedMedia error.
24
+ # @see https://developer.twitter.com/en/docs/media/upload-media/uploading-media/chunked-media-upload
25
+ def chunk_upload(media, media_type, media_category)
26
+ Timeout.timeout(timeouts&.fetch(:upload, nil), Twitter::Error::TimeoutError) do
27
+ init = Twitter::REST::Request.new(self, :post, 'https://upload.twitter.com/1.1/media/upload.json',
28
+ command: 'INIT',
29
+ media_type: media_type,
30
+ media_category: media_category,
31
+ total_bytes: media.size).perform
32
+ append_media(media, init[:media_id])
33
+ media.close
34
+ finalize_media(init[:media_id])
35
+ end
36
+ end
37
+
38
+ # @see https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-append
39
+ def append_media(media, media_id)
40
+ until media.eof?
41
+ chunk = media.read(5_000_000)
42
+ seg ||= -1
43
+ Twitter::REST::Request.new(self, :multipart_post, 'https://upload.twitter.com/1.1/media/upload.json',
44
+ command: 'APPEND',
45
+ media_id: media_id,
46
+ segment_index: seg += 1,
47
+ key: :media,
48
+ file: StringIO.new(chunk)).perform
49
+ end
50
+ end
51
+
52
+ # @see https://developer.twitter.com/en/docs/media/upload-media/api-reference/post-media-upload-finalize
53
+ # @see https://developer.twitter.com/en/docs/media/upload-media/api-reference/get-media-upload-status
54
+ def finalize_media(media_id)
55
+ response = Twitter::REST::Request.new(self, :post, 'https://upload.twitter.com/1.1/media/upload.json',
56
+ command: 'FINALIZE', media_id: media_id).perform
57
+ loop do
58
+ return response if !response[:processing_info] || %w[failed succeeded].include?(response[:processing_info][:state])
59
+
60
+ sleep(response[:processing_info][:check_after_secs])
61
+ response = Twitter::REST::Request.new(self, :get, 'https://upload.twitter.com/1.1/media/upload.json',
62
+ command: 'STATUS', media_id: media_id).perform
63
+ end
64
+ response
65
+ end
66
+ end
67
+ end
68
+ end
@@ -46,8 +46,8 @@ module Twitter
46
46
  # @param request_method [Symbol]
47
47
  # @param path [String]
48
48
  # @param options [Hash]
49
- def perform_request(request_method, path, options = {})
50
- Twitter::REST::Request.new(self, request_method, path, options).perform
49
+ def perform_request(request_method, path, options = {}, params = nil)
50
+ Twitter::REST::Request.new(self, request_method, path, options, params).perform
51
51
  end
52
52
 
53
53
  # @param path [String]
@@ -68,8 +68,8 @@ module Twitter
68
68
  # @param path [String]
69
69
  # @param options [Hash]
70
70
  # @param klass [Class]
71
- def perform_request_with_object(request_method, path, options, klass)
72
- response = perform_request(request_method, path, options)
71
+ def perform_request_with_object(request_method, path, options, klass, params = nil)
72
+ response = perform_request(request_method, path, options, params)
73
73
  klass.new(response)
74
74
  end
75
75
 
@@ -102,9 +102,15 @@ module Twitter
102
102
  # @param collection_name [Symbol]
103
103
  # @param klass [Class]
104
104
  def perform_get_with_cursor(path, options, collection_name, klass = nil)
105
- merge_default_cursor!(options)
105
+ limit = options.delete(:limit)
106
+ if options[:no_default_cursor]
107
+ options.delete(:no_default_cursor)
108
+ else
109
+ merge_default_cursor!(options)
110
+ end
111
+
106
112
  request = Twitter::REST::Request.new(self, :get, path, options)
107
- Twitter::Cursor.new(collection_name.to_sym, klass, request)
113
+ Twitter::Cursor.new(collection_name.to_sym, klass, request, limit)
108
114
  end
109
115
 
110
116
  # @param request_method [Symbol]
@@ -151,6 +157,17 @@ module Twitter
151
157
  end
152
158
  end
153
159
 
160
+ # @param request_method [Symbol]
161
+ # @param path [String]
162
+ # @param ids [Array]
163
+ # @return nil
164
+ def perform_requests(request_method, path, ids)
165
+ ids.each do |id|
166
+ perform_request(request_method, path, id: id)
167
+ end
168
+ nil
169
+ end
170
+
154
171
  # @param collection_name [Symbol]
155
172
  # @param klass [Class]
156
173
  # @param path [String]
@@ -229,7 +246,7 @@ module Twitter
229
246
  hash[:screen_name] = screen_names.join(',') unless screen_names.empty?
230
247
  end
231
248
 
232
- def collect_users(users) # rubocop:disable MethodLength
249
+ def collect_users(users) # rubocop:disable Metrics/MethodLength
233
250
  user_ids = []
234
251
  screen_names = []
235
252
  users.each do |user|