x 0.14.1 → 0.15.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: d12a0c6ddac3958b996b0bb5fc2a0a9893d0c85fa456d9d3a95bb5a9b4a39291
4
- data.tar.gz: 771bc16ba8c1b1492268be7e83a9a69a3291d45863dd309fbdc083969a58d5da
3
+ metadata.gz: 546b7411941d44ee91c52d183794a25c0335df8bc86606a6eb6464a2e45222b6
4
+ data.tar.gz: 17d533d29594706aef376c5970269ea634d5c8eb67216b6b4b9b78c3d3861db1
5
5
  SHA512:
6
- metadata.gz: c0c951541a6a4fdcb6eb7cf68135ecc8177a91af6beb1b823cd80495c54fd7bff8571b1644ff66553e65a4f386fb4e1ef5acc0984621d2f68e1c8960505d1708
7
- data.tar.gz: 6de5b4c4c7350dad50704727c1947705c590adc0729acb91c63b219b35b5ea51feba448d799b74ec4c0102fdac0896b91eafe79e9ecfa42ebae40efa55873ae5
6
+ metadata.gz: fd1be4aa484b43ac51d0a6ef04375b9de9c5f3f132ae3d8b33f108838710ef4ea5303ddf8fc89c334639383da5986d5ca16b90be3ae6e9f3b26cc929de091276
7
+ data.tar.gz: a0c0335a499edc77b0c6c260f16d35a0ffcc9de485b546e0f8477c2eb92426796d7c2588b006926718bcc8b989c06c0c15c20cde177582ede998cfbcf20b1910
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ ## [0.15.0] - 2025-02-06
2
+ * Change media upload to use the API v2 endpoints (eca2b88)
3
+
1
4
  ## [0.14.1] - 2023-12-20
2
5
  * Fix infinite loop when an upload fails (5dfc604)
3
6
 
data/README.md CHANGED
@@ -1,8 +1,9 @@
1
- [![Tests](https://github.com/sferik/x-ruby/actions/workflows/test.yml/badge.svg)](https://github.com/sferik/x-ruby/actions/workflows/test.yml)
2
- [![Linter](https://github.com/sferik/x-ruby/actions/workflows/lint.yml/badge.svg)](https://github.com/sferik/x-ruby/actions/workflows/lint.yml)
3
- [![Mutant](https://github.com/sferik/x-ruby/actions/workflows/mutant.yml/badge.svg)](https://github.com/sferik/x-ruby/actions/workflows/mutant.yml)
4
- [![Typer Checker](https://github.com/sferik/x-ruby/actions/workflows/steep.yml/badge.svg)](https://github.com/sferik/x-ruby/actions/workflows/steep.yml)
5
- [![Gem Version](https://badge.fury.io/rb/x.svg)](https://rubygems.org/gems/x)
1
+ [![tests](https://github.com/sferik/x-ruby/actions/workflows/test.yml/badge.svg)](https://github.com/sferik/x-ruby/actions/workflows/test.yml)
2
+ [![mutation tests](https://github.com/sferik/x-ruby/actions/workflows/mutant.yml/badge.svg)](https://github.com/sferik/x-ruby/actions/workflows/mutant.yml)
3
+ [![linter](https://github.com/sferik/x-ruby/actions/workflows/lint.yml/badge.svg)](https://github.com/sferik/x-ruby/actions/workflows/lint.yml)
4
+ [![typer checker](https://github.com/sferik/x-ruby/actions/workflows/steep.yml/badge.svg)](https://github.com/sferik/x-ruby/actions/workflows/steep.yml)
5
+ [![maintainability](https://api.codeclimate.com/v1/badges/40bbddf2c9170742ca9e/maintainability)](https://codeclimate.com/github/sferik/x-ruby/maintainability)
6
+ [![gem version](https://badge.fury.io/rb/x.svg)](https://rubygems.org/gems/x)
6
7
 
7
8
  # A [Ruby](https://www.ruby-lang.org) interface to the [X API](https://developer.x.com)
8
9
 
data/lib/x/client.rb CHANGED
@@ -33,35 +33,30 @@ module X
33
33
  default_array_class: DEFAULT_ARRAY_CLASS,
34
34
  default_object_class: DEFAULT_OBJECT_CLASS,
35
35
  max_redirects: RedirectHandler::DEFAULT_MAX_REDIRECTS)
36
-
37
36
  initialize_oauth(api_key, api_key_secret, access_token, access_token_secret, bearer_token)
38
37
  initialize_authenticator
39
38
  @base_url = base_url
40
39
  initialize_default_classes(default_array_class, default_object_class)
41
- @connection = Connection.new(open_timeout: open_timeout, read_timeout: read_timeout,
42
- write_timeout: write_timeout, debug_output: debug_output, proxy_url: proxy_url)
40
+ @connection = Connection.new(open_timeout:, read_timeout:, write_timeout:, debug_output:, proxy_url:)
43
41
  @request_builder = RequestBuilder.new
44
- @redirect_handler = RedirectHandler.new(connection: @connection, request_builder: @request_builder,
45
- max_redirects: max_redirects)
42
+ @redirect_handler = RedirectHandler.new(connection: @connection, request_builder: @request_builder, max_redirects:)
46
43
  @response_parser = ResponseParser.new
47
44
  end
48
45
 
49
46
  def get(endpoint, headers: {}, array_class: default_array_class, object_class: default_object_class)
50
- execute_request(:get, endpoint, headers: headers, array_class: array_class, object_class: object_class)
47
+ execute_request(:get, endpoint, headers:, array_class:, object_class:)
51
48
  end
52
49
 
53
50
  def post(endpoint, body = nil, headers: {}, array_class: default_array_class, object_class: default_object_class)
54
- execute_request(:post, endpoint, body: body, headers: headers, array_class: array_class,
55
- object_class: object_class)
51
+ execute_request(:post, endpoint, body:, headers:, array_class:, object_class:)
56
52
  end
57
53
 
58
54
  def put(endpoint, body = nil, headers: {}, array_class: default_array_class, object_class: default_object_class)
59
- execute_request(:put, endpoint, body: body, headers: headers, array_class: array_class,
60
- object_class: object_class)
55
+ execute_request(:put, endpoint, body:, headers:, array_class:, object_class:)
61
56
  end
62
57
 
63
58
  def delete(endpoint, headers: {}, array_class: default_array_class, object_class: default_object_class)
64
- execute_request(:delete, endpoint, headers: headers, array_class: array_class, object_class: object_class)
59
+ execute_request(:delete, endpoint, headers:, array_class:, object_class:)
65
60
  end
66
61
 
67
62
  def api_key=(api_key)
@@ -106,10 +101,9 @@ module X
106
101
 
107
102
  def initialize_authenticator
108
103
  @authenticator = if api_key && api_key_secret && access_token && access_token_secret
109
- OAuthAuthenticator.new(api_key: api_key, api_key_secret: api_key_secret, access_token: access_token,
110
- access_token_secret: access_token_secret)
104
+ OAuthAuthenticator.new(api_key:, api_key_secret:, access_token:, access_token_secret:)
111
105
  elsif bearer_token
112
- BearerTokenAuthenticator.new(bearer_token: bearer_token)
106
+ BearerTokenAuthenticator.new(bearer_token:)
113
107
  elsif @authenticator.nil?
114
108
  Authenticator.new
115
109
  else
@@ -119,12 +113,10 @@ module X
119
113
 
120
114
  def execute_request(http_method, endpoint, body: nil, headers: {}, array_class: default_array_class, object_class: default_object_class)
121
115
  uri = URI.join(base_url, endpoint)
122
- request = @request_builder.build(http_method: http_method, uri: uri, body: body, headers: headers,
123
- authenticator: @authenticator)
124
- response = @connection.perform(request: request)
125
- response = @redirect_handler.handle(response: response, request: request, base_url: base_url,
126
- authenticator: @authenticator)
127
- @response_parser.parse(response: response, array_class: array_class, object_class: object_class)
116
+ request = @request_builder.build(http_method:, uri:, body:, headers:, authenticator: @authenticator)
117
+ response = @connection.perform(request:)
118
+ response = @redirect_handler.handle(response:, request:, base_url:, authenticator: @authenticator)
119
+ @response_parser.parse(response:, array_class:, object_class:)
128
120
  end
129
121
  end
130
122
  end
@@ -35,7 +35,7 @@ module X
35
35
  end
36
36
 
37
37
  def json?(response)
38
- JSON_CONTENT_TYPE_REGEXP.match?(response["content-type"])
38
+ JSON_CONTENT_TYPE_REGEXP === response["content-type"]
39
39
  end
40
40
  end
41
41
  end
@@ -9,7 +9,7 @@ module X
9
9
 
10
10
  def rate_limits
11
11
  @rate_limits ||= RateLimit::TYPES.filter_map do |type|
12
- RateLimit.new(type: type, response: response) if response["x-#{type}-remaining"].eql?("0")
12
+ RateLimit.new(type:, response:) if response["x-#{type}-remaining"].eql?("0")
13
13
  end
14
14
  end
15
15
 
@@ -14,33 +14,29 @@ module X
14
14
  GIF_MIME_TYPE, JPEG_MIME_TYPE, MP4_MIME_TYPE, PNG_MIME_TYPE, SUBRIP_MIME_TYPE, WEBP_MIME_TYPE = MIME_TYPES
15
15
  MIME_TYPE_MAP = {"gif" => GIF_MIME_TYPE, "jpg" => JPEG_MIME_TYPE, "jpeg" => JPEG_MIME_TYPE, "mp4" => MP4_MIME_TYPE,
16
16
  "png" => PNG_MIME_TYPE, "srt" => SUBRIP_MIME_TYPE, "webp" => WEBP_MIME_TYPE}.freeze
17
+ PROCESSING_INFO_STATES = %w[failed succeeded].freeze
17
18
 
18
19
  def upload(client:, file_path:, media_category:, media_type: infer_media_type(file_path, media_category),
19
20
  boundary: SecureRandom.hex)
20
- validate!(file_path: file_path, media_category: media_category)
21
- upload_client = client.dup.tap { |c| c.base_url = "https://upload.twitter.com/1.1/" }
22
- upload_body = construct_upload_body(file_path: file_path, media_type: media_type, boundary: boundary)
21
+ validate!(file_path:, media_category:)
22
+ upload_body = construct_upload_body(file_path:, media_type:, boundary:)
23
23
  headers = {"Content-Type" => "multipart/form-data, boundary=#{boundary}"}
24
- upload_client.post("media/upload.json?media_category=#{media_category}", upload_body, headers: headers)
24
+ client.post("media/upload?media_category=#{media_category}", upload_body, headers:)
25
25
  end
26
26
 
27
27
  def chunked_upload(client:, file_path:, media_category:, media_type: infer_media_type(file_path,
28
- media_category), boundary: SecureRandom.hex, chunk_size_mb: 8)
29
- validate!(file_path: file_path, media_category: media_category)
30
- upload_client = client.dup.tap { |c| c.base_url = "https://upload.twitter.com/1.1/" }
31
- media = init(upload_client: upload_client, file_path: file_path, media_type: media_type,
32
- media_category: media_category)
28
+ media_category), boundary: SecureRandom.hex, chunk_size_mb: 1)
29
+ validate!(file_path:, media_category:)
30
+ media = init(client:, file_path:, media_type:, media_category:)
33
31
  chunk_size = chunk_size_mb * BYTES_PER_MB
34
- append(upload_client: upload_client, file_paths: split(file_path, chunk_size), media: media,
35
- media_type: media_type, boundary: boundary)
36
- upload_client.post("media/upload.json?command=FINALIZE&media_id=#{media["media_id"]}")
32
+ append(client:, file_paths: split(file_path, chunk_size), media:, media_type:, boundary:)
33
+ client.post("media/upload?command=FINALIZE&media_key=#{media["media_key"]}")&.fetch("data")
37
34
  end
38
35
 
39
36
  def await_processing(client:, media:)
40
- upload_client = client.dup.tap { |c| c.base_url = "https://upload.twitter.com/1.1/" }
41
37
  loop do
42
- status = upload_client.get("media/upload.json?command=STATUS&media_id=#{media["media_id"]}")
43
- return status if !status["processing_info"] || %w[failed succeeded].include?(status["processing_info"]["state"])
38
+ status = client.get("media/upload?command=STATUS&media_key=#{media["media_key"]}")&.fetch("data")
39
+ return status if !status["processing_info"] || PROCESSING_INFO_STATES.include?(status["processing_info"]["state"])
44
40
 
45
41
  sleep status["processing_info"]["check_after_secs"].to_i
46
42
  end
@@ -67,39 +63,38 @@ module X
67
63
 
68
64
  def split(file_path, chunk_size)
69
65
  file_number = -1
66
+ file_paths = [] # @type var file_paths: Array[String]
70
67
 
71
- [].tap do |file_paths|
72
- File.open(file_path, "rb") do |f|
73
- while (chunk = f.read(chunk_size))
74
- file_paths << "#{Dir.mktmpdir}/x#{format("%03d", file_number += 1)}".tap do |path|
75
- File.binwrite(path, chunk)
76
- end
77
- end
68
+ File.open(file_path, "rb") do |f|
69
+ while (chunk = f.read(chunk_size))
70
+ path = "#{Dir.mktmpdir}/x#{format("%03d", file_number += 1)}"
71
+ File.binwrite(path, chunk)
72
+ file_paths << path
78
73
  end
79
74
  end
75
+ file_paths
80
76
  end
81
77
 
82
- def init(upload_client:, file_path:, media_type:, media_category:)
78
+ def init(client:, file_path:, media_type:, media_category:)
83
79
  total_bytes = File.size(file_path)
84
80
  query = "command=INIT&media_type=#{media_type}&media_category=#{media_category}&total_bytes=#{total_bytes}"
85
- upload_client.post("media/upload.json?#{query}")
81
+ client.post("media/upload?#{query}")&.fetch("data")
86
82
  end
87
83
 
88
- def append(upload_client:, file_paths:, media:, media_type:, boundary: SecureRandom.hex)
84
+ def append(client:, file_paths:, media:, media_type:, boundary: SecureRandom.hex)
89
85
  threads = file_paths.map.with_index do |file_path, index|
90
86
  Thread.new do
91
- upload_body = construct_upload_body(file_path: file_path, media_type: media_type, boundary: boundary)
92
- query = "command=APPEND&media_id=#{media["media_id"]}&segment_index=#{index}"
87
+ upload_body = construct_upload_body(file_path:, media_type:, boundary:)
88
+ query = "command=APPEND&media_key=#{media["media_key"]}&segment_index=#{index}"
93
89
  headers = {"Content-Type" => "multipart/form-data, boundary=#{boundary}"}
94
- upload_chunk(upload_client: upload_client, query: query, upload_body: upload_body, file_path: file_path,
95
- headers: headers)
90
+ upload_chunk(client:, query:, upload_body:, file_path:, headers:)
96
91
  end
97
92
  end
98
93
  threads.each(&:join)
99
94
  end
100
95
 
101
- def upload_chunk(upload_client:, query:, upload_body:, file_path:, headers: {})
102
- upload_client.post("media/upload.json?#{query}", upload_body, headers: headers)
96
+ def upload_chunk(client:, query:, upload_body:, file_path:, headers: {})
97
+ client.post("media/upload?#{query}", upload_body, headers:)
103
98
  rescue NetworkError, ServerError
104
99
  retries ||= 0
105
100
  ((retries += 1) < MAX_RETRIES) ? retry : raise
@@ -1,10 +1,10 @@
1
1
  require "base64"
2
+ require "cgi"
2
3
  require "json"
3
4
  require "openssl"
4
5
  require "securerandom"
5
6
  require "uri"
6
7
  require_relative "authenticator"
7
- require_relative "cgi"
8
8
 
9
9
  module X
10
10
  class OAuthAuthenticator < Authenticator
@@ -39,7 +39,7 @@ module X
39
39
  end
40
40
 
41
41
  def uri_without_query(uri)
42
- uri.to_s.chomp("?#{uri.query}")
42
+ "#{uri.scheme}://#{uri.host}#{uri.path}"
43
43
  end
44
44
 
45
45
  def build_oauth_header(method, url, query_params)
@@ -71,7 +71,7 @@ module X
71
71
  end
72
72
 
73
73
  def signature_base_string(method, url, params)
74
- "#{method}&#{CGI.escape(url)}&#{CGI.escape(CGI.escape_params(params.sort))}"
74
+ "#{method}&#{CGI.escapeURIComponent(url)}&#{CGI.escapeURIComponent(URI.encode_www_form(params.sort))}"
75
75
  end
76
76
 
77
77
  def signing_key
@@ -79,7 +79,7 @@ module X
79
79
  end
80
80
 
81
81
  def format_oauth_header(params)
82
- "OAuth #{params.sort.map { |k, v| "#{k}=\"#{CGI.escape(v)}\"" }.join(", ")}"
82
+ "OAuth #{params.sort.map { |k, v| "#{k}=\"#{CGI.escapeURIComponent(v)}\"" }.join(", ")}"
83
83
  end
84
84
  end
85
85
  end
@@ -28,7 +28,7 @@ module X
28
28
  new_request = build_request(request, new_uri, Integer(response.code), authenticator)
29
29
  new_response = connection.perform(request: new_request)
30
30
 
31
- handle(response: new_response, request: new_request, base_url: base_url, redirect_count: redirect_count + 1)
31
+ handle(response: new_response, request: new_request, base_url:, redirect_count: redirect_count + 1)
32
32
  else
33
33
  response
34
34
  end
@@ -42,7 +42,7 @@ module X
42
42
  URI.join(base_url, location)
43
43
  end
44
44
 
45
- def build_request(request, new_uri, response_code, authenticator)
45
+ def build_request(request, uri, response_code, authenticator)
46
46
  http_method, body = case response_code
47
47
  in 307 | 308
48
48
  [request.method.downcase.to_sym, request.body]
@@ -50,7 +50,7 @@ module X
50
50
  [:get, nil]
51
51
  end
52
52
 
53
- request_builder.build(http_method: http_method, uri: new_uri, body: body, authenticator: authenticator)
53
+ request_builder.build(http_method:, uri:, body:, authenticator:)
54
54
  end
55
55
  end
56
56
  end
@@ -1,7 +1,6 @@
1
1
  require "net/http"
2
2
  require "uri"
3
3
  require_relative "authenticator"
4
- require_relative "cgi"
5
4
  require_relative "version"
6
5
 
7
6
  module X
@@ -18,9 +17,9 @@ module X
18
17
  }.freeze
19
18
 
20
19
  def build(http_method:, uri:, body: nil, headers: {}, authenticator: Authenticator.new)
21
- request = create_request(http_method: http_method, uri: uri, body: body)
22
- add_headers(request: request, headers: headers)
23
- add_authentication(request: request, authenticator: authenticator)
20
+ request = create_request(http_method:, uri:, body:)
21
+ add_headers(request:, headers:)
22
+ add_authentication(request:, authenticator:)
24
23
  request
25
24
  end
26
25
 
@@ -51,7 +50,7 @@ module X
51
50
  end
52
51
 
53
52
  def escape_query_params(uri)
54
- URI(uri).tap { |u| u.query = CGI.escape_params(URI.decode_www_form(u.query)) if u.query }
53
+ URI(uri).tap { |u| u.query = URI.encode_www_form(URI.decode_www_form(u.query)) if u.query }
55
54
  end
56
55
  end
57
56
  end
@@ -34,28 +34,27 @@ module X
34
34
  503 => ServiceUnavailable,
35
35
  504 => GatewayTimeout
36
36
  }.freeze
37
- JSON_CONTENT_TYPE_REGEXP = %r{application/json}
38
37
 
39
38
  def parse(response:, array_class: nil, object_class: nil)
40
39
  raise error(response) unless response.is_a?(Net::HTTPSuccess)
41
40
 
42
- return unless json?(response)
41
+ return if response.instance_of?(Net::HTTPNoContent)
43
42
 
44
- JSON.parse(response.body, array_class: array_class, object_class: object_class)
43
+ begin
44
+ JSON.parse(response.body, array_class:, object_class:)
45
+ rescue JSON::ParserError
46
+ nil
47
+ end
45
48
  end
46
49
 
47
50
  private
48
51
 
49
52
  def error(response)
50
- error_class(response).new(response: response)
53
+ error_class(response).new(response:)
51
54
  end
52
55
 
53
56
  def error_class(response)
54
57
  ERROR_MAP[Integer(response.code)] || HTTPError
55
58
  end
56
-
57
- def json?(response)
58
- JSON_CONTENT_TYPE_REGEXP.match?(response["content-type"])
59
- end
60
59
  end
61
60
  end
data/lib/x/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  require "rubygems/version"
2
2
 
3
3
  module X
4
- VERSION = Gem::Version.create("0.14.1")
4
+ VERSION = Gem::Version.create("0.15.0")
5
5
  end
data/sig/x.rbs CHANGED
@@ -125,6 +125,8 @@ module X
125
125
  DEFAULT_DEBUG_OUTPUT: IO
126
126
  NETWORK_ERRORS: Array[(singleton(Errno::ECONNREFUSED) | singleton(Errno::ECONNRESET) | singleton(Net::OpenTimeout) | singleton(Net::ReadTimeout) | singleton(OpenSSL::SSL::SSLError))]
127
127
 
128
+ @proxy_url: URI::Generic | String
129
+
128
130
  extend Forwardable
129
131
 
130
132
  attr_accessor open_timeout : Float | Integer
@@ -151,7 +153,7 @@ module X
151
153
  RATE_LIMIT_TYPE: String
152
154
  APP_LIMIT_TYPE: String
153
155
  USER_LIMIT_TYPE: String
154
- TYPES: [String, String, String]
156
+ TYPES: Array[String]
155
157
 
156
158
  attr_accessor type: String
157
159
  attr_accessor response: Net::HTTPResponse
@@ -167,7 +169,7 @@ module X
167
169
  DEFAULT_HEADERS: Hash[String, String]
168
170
 
169
171
  def initialize: (?content_type: String, ?user_agent: String) -> void
170
- def build: (authenticator: Authenticator, http_method: Symbol, uri: URI::Generic, ?body: String?, ?headers: Hash[String, String]) -> (Net::HTTPRequest)
172
+ def build: (http_method: Symbol, uri: URI::Generic, ?body: String?, ?headers: Hash[String, String], ?authenticator: Authenticator) -> (Net::HTTPRequest)
171
173
 
172
174
  private
173
175
  def create_request: (http_method: Symbol, uri: URI::Generic, body: String?) -> (Net::HTTPRequest)
@@ -183,7 +185,7 @@ module X
183
185
  attr_reader connection: Connection
184
186
  attr_reader request_builder: RequestBuilder
185
187
  attr_reader max_redirects: Integer
186
- def initialize: (connection: Connection, request_builder: RequestBuilder, ?max_redirects: Integer) -> void
188
+ def initialize: (?connection: Connection, ?request_builder: RequestBuilder, ?max_redirects: Integer) -> void
187
189
  def handle: (response: Net::HTTPResponse, request: Net::HTTPRequest, base_url: String, ?authenticator: Authenticator, ?redirect_count: Integer) -> Net::HTTPResponse
188
190
 
189
191
  private
@@ -193,60 +195,51 @@ module X
193
195
  end
194
196
 
195
197
  class ResponseParser
196
- ERROR_MAP: Hash[Integer, singleton(Unauthorized) | singleton(BadRequest) | singleton(Forbidden) | singleton(InternalServerError) | singleton(NotFound) | singleton(PayloadTooLarge) | singleton(ServiceUnavailable) | singleton(TooManyRequests)]
197
- JSON_CONTENT_TYPE_REGEXP: Regexp
198
+ ERROR_MAP: Hash[Integer, singleton(BadGateway) | singleton(BadRequest) | singleton(ConnectionException) | singleton(Forbidden) | singleton(GatewayTimeout) | singleton(Gone) | singleton(InternalServerError) | singleton(NotAcceptable) | singleton(NotFound) | singleton(PayloadTooLarge) | singleton(ServiceUnavailable) | singleton(TooManyRequests) | singleton(Unauthorized) | singleton(UnprocessableEntity)]
198
199
 
199
200
  def parse: (response: Net::HTTPResponse, ?array_class: Class?, ?object_class: Class?) -> untyped
200
201
 
201
202
  private
202
- def error: (Net::HTTPResponse response) -> (Unauthorized | BadRequest | Forbidden | InternalServerError | NotFound | PayloadTooLarge | ServiceUnavailable | TooManyRequests)
203
- def error_class: (Net::HTTPResponse response) -> (singleton(Unauthorized) | singleton(BadRequest) | singleton(Forbidden) | singleton(InternalServerError) | singleton(NotFound) | singleton(PayloadTooLarge) | singleton(ServiceUnavailable) | singleton(TooManyRequests))
203
+ def error: (Net::HTTPResponse response) -> HTTPError
204
+ def error_class: (Net::HTTPResponse response) -> (singleton(BadGateway) | singleton(BadRequest) | singleton(ConnectionException) | singleton(Forbidden) | singleton(GatewayTimeout) | singleton(Gone) | singleton(InternalServerError) | singleton(NotAcceptable) | singleton(NotFound) | singleton(PayloadTooLarge) | singleton(ServiceUnavailable) | singleton(TooManyRequests) | singleton(Unauthorized) | singleton(UnprocessableEntity))
204
205
  def json?: (Net::HTTPResponse response) -> bool
205
206
  end
206
207
 
207
208
  class Client
208
209
  DEFAULT_BASE_URL: String
209
- DEFAULT_ARRAY_CLASS: Class
210
- DEFAULT_OBJECT_CLASS: Class
211
-
210
+ DEFAULT_ARRAY_CLASS: singleton(Array)
211
+ DEFAULT_OBJECT_CLASS: singleton(Hash)
212
212
  extend Forwardable
213
- @authenticator: Authenticator
213
+ @authenticator: Authenticator | BearerTokenAuthenticator | OAuthAuthenticator
214
214
  @connection: Connection
215
215
  @request_builder: RequestBuilder
216
216
  @redirect_handler: RedirectHandler
217
217
  @response_parser: ResponseParser
218
218
 
219
- attr_accessor access_token: String
220
- attr_accessor access_token_secret: String
221
- attr_accessor api_key: String
222
- attr_accessor api_key_secret: String
223
- attr_accessor bearer_token: String
224
219
  attr_accessor base_url: String
225
- attr_accessor open_timeout: Float | Integer
226
- attr_accessor read_timeout: Float | Integer
227
- attr_accessor write_timeout: Float | Integer
228
- attr_accessor proxy_url: String
229
- attr_accessor debug_output: IO
230
- attr_accessor default_array_class: Class
231
- attr_accessor default_object_class: Class
232
- attr_accessor max_redirects: Integer
233
- attr_accessor authenticator: Authenticator
234
- attr_accessor connection: Connection
235
- attr_accessor request_builder: RequestBuilder
236
- attr_accessor redirect_handler: RedirectHandler
237
- attr_accessor response_parser: ResponseParser
238
-
239
- def initialize: (?api_key: String?, ?api_key_secret: String?, ?access_token: String?, ?access_token_secret: String?, ?bearer_token: String?, ?base_url: String, ?open_timeout: Float | Integer, ?read_timeout: Float | Integer, ?write_timeout: Float | Integer, ?proxy_url: URI::Generic? | String?, ?debug_output: IO, ?default_array_class: Class, ?default_object_class: Class, ?max_redirects: Integer) -> void
220
+ attr_accessor default_array_class: singleton(Array)
221
+ attr_accessor default_object_class: singleton(Hash)
222
+ attr_reader api_key: String?
223
+ attr_reader api_key_secret: String?
224
+ attr_reader access_token: String?
225
+ attr_reader access_token_secret: String?
226
+ attr_reader bearer_token: String?
227
+ def initialize: (?api_key: nil, ?api_key_secret: nil, ?access_token: nil, ?access_token_secret: nil, ?bearer_token: nil, ?base_url: String, ?open_timeout: Integer, ?read_timeout: Integer, ?write_timeout: Integer, ?debug_output: untyped, ?proxy_url: nil, ?default_array_class: singleton(Array), ?default_object_class: singleton(Hash), ?max_redirects: Integer) -> void
240
228
  def get: (String endpoint, ?headers: Hash[String, String], ?array_class: Class, ?object_class: Class) -> untyped
241
229
  def post: (String endpoint, ?String? body, ?headers: Hash[String, String], ?array_class: Class, ?object_class: Class) -> untyped
242
230
  def put: (String endpoint, ?String? body, ?headers: Hash[String, String], ?array_class: Class, ?object_class: Class) -> untyped
243
231
  def delete: (String endpoint, ?headers: Hash[String, String], ?array_class: Class, ?object_class: Class) -> untyped
232
+ def api_key=: (String api_key) -> void
233
+ def api_key_secret=: (String api_key_secret) -> void
234
+ def access_token=: (String access_token) -> void
235
+ def access_token_secret=: (String access_token_secret) -> void
236
+ def bearer_token=: (String bearer_token) -> void
244
237
 
245
238
  private
246
239
  def initialize_oauth: (String? api_key, String? api_key_secret, String? access_token, String? access_token_secret, String? bearer_token) -> void
247
- def initialize_default_classes: (Class? default_array_class, Class? default_object_class) -> void
248
- def initialize_authenticator: -> Authenticator
249
- def execute_request: (Symbol http_method, String endpoint, ?body: String?, ?headers: Hash[String, String], ?array_class: Class?, ?object_class: Class?) -> untyped
240
+ def initialize_default_classes: (singleton(Array) default_array_class, singleton(Hash) default_object_class) -> singleton(Hash)
241
+ def initialize_authenticator: -> (Authenticator | BearerTokenAuthenticator | OAuthAuthenticator)
242
+ def execute_request: (:delete | :get | :post | :put http_method, String endpoint, ?body: String?, ?headers: Hash[String, String], ?array_class: Class, ?object_class: Class) -> nil
250
243
  end
251
244
 
252
245
  module MediaUploader
@@ -269,6 +262,7 @@ module X
269
262
  SUBRIP_MIME_TYPE: String
270
263
  WEBP_MIME_TYPE: String
271
264
  MIME_TYPE_MAP: Hash[String, String]
265
+ PROCESSING_INFO_STATES: Array[String]
272
266
  extend MediaUploader
273
267
 
274
268
  def upload: (client: Client, file_path: String, media_category: String, ?media_type: String, ?boundary: String) -> untyped
@@ -279,16 +273,11 @@ module X
279
273
  def validate!: (file_path: String, media_category: String) -> nil
280
274
  def infer_media_type: (String file_path, String media_category) -> String
281
275
  def split: (String file_path, Integer chunk_size) -> Array[String]
282
- def init: (upload_client: Client, file_path: String, media_type: String, media_category: String) -> untyped
283
- def append: (upload_client: Client, file_paths: Array[String], media: untyped, media_type: String, ?boundary: String) -> Array[String]
284
- def upload_chunk: (upload_client: Client, query: String, upload_body: String, file_path: String, ?headers: Hash[String, String]) -> Integer?
276
+ def init: (client: Client, file_path: String, media_type: String, media_category: String) -> untyped
277
+ def append: (client: Client, file_paths: Array[String], media: untyped, media_type: String, ?boundary: String) -> Array[String]
278
+ def upload_chunk: (client: Client, query: String, upload_body: String, file_path: String, ?headers: Hash[String, String]) -> Integer?
285
279
  def cleanup_file: (String file_path) -> Integer?
286
- def finalize: (upload_client: Client, media: untyped) -> untyped
280
+ def finalize: (client: Client, media: untyped) -> untyped
287
281
  def construct_upload_body: (file_path: String, media_type: String, ?boundary: String) -> String
288
282
  end
289
-
290
- class CGI
291
- def self.escape: (String value) -> String
292
- def self.escape_params: (Hash[String, String] | Array[[String, String]] params) -> String
293
- end
294
283
  end
metadata CHANGED
@@ -1,16 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: x
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.1
4
+ version: 0.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Berlin
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2023-12-20 00:00:00.000000000 Z
10
+ date: 2025-02-06 00:00:00.000000000 Z
12
11
  dependencies: []
13
- description:
14
12
  email:
15
13
  - sferik@gmail.com
16
14
  executables: []
@@ -25,7 +23,6 @@ files:
25
23
  - lib/x.rb
26
24
  - lib/x/authenticator.rb
27
25
  - lib/x/bearer_token_authenticator.rb
28
- - lib/x/cgi.rb
29
26
  - lib/x/client.rb
30
27
  - lib/x/connection.rb
31
28
  - lib/x/errors/bad_gateway.rb
@@ -67,7 +64,6 @@ metadata:
67
64
  changelog_uri: https://github.com/sferik/x-ruby/blob/master/CHANGELOG.md
68
65
  bug_tracker_uri: https://github.com/sferik/x-ruby/issues
69
66
  documentation_uri: https://rubydoc.info/gems/x/
70
- post_install_message:
71
67
  rdoc_options: []
72
68
  require_paths:
73
69
  - lib
@@ -75,15 +71,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
75
71
  requirements:
76
72
  - - ">="
77
73
  - !ruby/object:Gem::Version
78
- version: '3.0'
74
+ version: '3.2'
79
75
  required_rubygems_version: !ruby/object:Gem::Requirement
80
76
  requirements:
81
77
  - - ">="
82
78
  - !ruby/object:Gem::Version
83
79
  version: '0'
84
80
  requirements: []
85
- rubygems_version: 3.5.1
86
- signing_key:
81
+ rubygems_version: 3.6.3
87
82
  specification_version: 4
88
83
  summary: A Ruby interface to the X API.
89
84
  test_files: []
data/lib/x/cgi.rb DELETED
@@ -1,14 +0,0 @@
1
- require "cgi"
2
-
3
- module X
4
- class CGI
5
- # TODO: Replace CGI.escape with CGI.escapeURIComponent when support for Ruby 3.1 is dropped
6
- def self.escape(value)
7
- ::CGI.escape(value).gsub("+", "%20")
8
- end
9
-
10
- def self.escape_params(params)
11
- params.map { |k, v| "#{k}=#{escape(v)}" }.join("&")
12
- end
13
- end
14
- end