x 0.14.1 → 0.15.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +6 -5
- data/lib/x/client.rb +12 -20
- data/lib/x/errors/http_error.rb +1 -1
- data/lib/x/errors/too_many_requests.rb +1 -1
- data/lib/x/media_uploader.rb +26 -31
- data/lib/x/oauth_authenticator.rb +4 -4
- data/lib/x/redirect_handler.rb +7 -8
- data/lib/x/request_builder.rb +6 -5
- data/lib/x/response_parser.rb +7 -8
- data/lib/x/version.rb +1 -1
- data/sig/x.rbs +32 -43
- metadata +4 -9
- data/lib/x/cgi.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 96d08de21266cc093e64eaa91a2f9835ec3fcfe1652790a7ef134e7bf621d6e5
|
4
|
+
data.tar.gz: 0061b62ae16521cb959580b3ce56509998c61050139ea880aa5d61991a3f92f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 904988eb897a4b598e7ac8ac8dbf9eeafaefe5b5b1c7ed7d16ddce440cc7fdd5d59cbbec75062d3f943e6a4417844709f7f04e49f3eb9a4b4ca5c3894eee483f
|
7
|
+
data.tar.gz: 4605412f136aa0828403ac198b7e6a5b7ffe449b7d6ccad993ec19b848155f0ed6266d1b96fe80d8837b3e51105647da3ccaf5beb24bfe5b41abf78e8ce3fd35
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
|
2
|
+
## [0.15.0] - 2025-03-24
|
3
|
+
* Fix bug in MediaUploader#await_processing (136dff8)
|
4
|
+
* Refactor RedirectHandler#build_request (fd379c3)
|
5
|
+
* Escape space in query string as %20, not + (2d2df75)
|
6
|
+
* Don't escape commas in query parameters (e7d9056)
|
7
|
+
|
8
|
+
## [0.15.0] - 2025-02-06
|
9
|
+
* Change media upload to use the API v2 endpoints (eca2b88)
|
10
|
+
|
1
11
|
## [0.14.1] - 2023-12-20
|
2
12
|
* Fix infinite loop when an upload fails (5dfc604)
|
3
13
|
|
data/README.md
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
[](https://github.com/sferik/x-ruby/actions/workflows/test.yml)
|
2
|
+
[](https://github.com/sferik/x-ruby/actions/workflows/mutant.yml)
|
3
|
+
[](https://github.com/sferik/x-ruby/actions/workflows/lint.yml)
|
4
|
+
[](https://github.com/sferik/x-ruby/actions/workflows/steep.yml)
|
5
|
+
[](https://codeclimate.com/github/sferik/x-ruby/maintainability)
|
6
|
+
[](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
|
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
|
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
|
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
|
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
|
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
|
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:
|
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
|
123
|
-
|
124
|
-
response = @
|
125
|
-
|
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
|
data/lib/x/errors/http_error.rb
CHANGED
@@ -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
|
12
|
+
RateLimit.new(type:, response:) if response["x-#{type}-remaining"].eql?("0")
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
data/lib/x/media_uploader.rb
CHANGED
@@ -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
|
21
|
-
|
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
|
-
|
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:
|
29
|
-
validate!(file_path
|
30
|
-
|
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(
|
35
|
-
|
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 =
|
43
|
-
return status if !status["processing_info"] ||
|
38
|
+
status = client.get("media/upload?command=STATUS&media_id=#{media["id"]}")&.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
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
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(
|
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
|
-
|
81
|
+
client.post("media/upload?#{query}")&.fetch("data")
|
86
82
|
end
|
87
83
|
|
88
|
-
def append(
|
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
|
92
|
-
query = "command=APPEND&
|
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(
|
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(
|
102
|
-
|
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.
|
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.
|
74
|
+
"#{method}&#{CGI.escapeURIComponent(url)}&#{CGI.escapeURIComponent(URI.encode_www_form(params.sort).gsub("+", "%20"))}"
|
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.
|
82
|
+
"OAuth #{params.sort.map { |k, v| "#{k}=\"#{CGI.escapeURIComponent(v)}\"" }.join(", ")}"
|
83
83
|
end
|
84
84
|
end
|
85
85
|
end
|
data/lib/x/redirect_handler.rb
CHANGED
@@ -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
|
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,15 +42,14 @@ module X
|
|
42
42
|
URI.join(base_url, location)
|
43
43
|
end
|
44
44
|
|
45
|
-
def build_request(request,
|
46
|
-
http_method
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
[:get, nil]
|
45
|
+
def build_request(request, uri, response_code, authenticator)
|
46
|
+
http_method = :get
|
47
|
+
if [307, 308].include?(response_code)
|
48
|
+
http_method = request.method.downcase.to_sym
|
49
|
+
body = request.body
|
51
50
|
end
|
52
51
|
|
53
|
-
request_builder.build(http_method
|
52
|
+
request_builder.build(http_method:, uri:, body:, authenticator:)
|
54
53
|
end
|
55
54
|
end
|
56
55
|
end
|
data/lib/x/request_builder.rb
CHANGED
@@ -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
|
22
|
-
add_headers(request
|
23
|
-
add_authentication(request
|
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,9 @@ module X
|
|
51
50
|
end
|
52
51
|
|
53
52
|
def escape_query_params(uri)
|
54
|
-
URI(uri).tap
|
53
|
+
URI(uri).tap do |u|
|
54
|
+
u.query = URI.encode_www_form(URI.decode_www_form(u.query)).gsub("%2C", ",") if u.query
|
55
|
+
end
|
55
56
|
end
|
56
57
|
end
|
57
58
|
end
|
data/lib/x/response_parser.rb
CHANGED
@@ -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
|
41
|
+
return if response.instance_of?(Net::HTTPNoContent)
|
43
42
|
|
44
|
-
|
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:
|
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
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
|
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: (
|
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(
|
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) ->
|
203
|
-
def error_class: (Net::HTTPResponse response) -> (singleton(
|
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:
|
210
|
-
DEFAULT_OBJECT_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
|
226
|
-
attr_accessor
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
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: (
|
248
|
-
def initialize_authenticator: -> Authenticator
|
249
|
-
def execute_request: (
|
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: (
|
283
|
-
def append: (
|
284
|
-
def upload_chunk: (
|
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: (
|
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.
|
4
|
+
version: 0.15.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Erik Berlin
|
8
|
-
autorequire:
|
9
8
|
bindir: exe
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-03-24 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.
|
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
|
86
|
-
signing_key:
|
81
|
+
rubygems_version: 3.6.5
|
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
|