x 0.9.0 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ff134774b6d6b28f3f72c740c74b1f44b11ce94cff63fbfe4eabc0414f600c3
4
- data.tar.gz: 9f3b9abd3ca2628979676b5f634781b960adf6afe8a77559cf6f3a96ee738098
3
+ metadata.gz: e2906018cf3e05342abe1bc6c938f3d1b3cf9d1d84c3813a96a11b693fbe0960
4
+ data.tar.gz: f84f47e1a9ea06b101712ae1f9fd7198d71b2cbc7487a654b8442dd13e3244e0
5
5
  SHA512:
6
- metadata.gz: cd471d0b216161f5e68832e83b5024a8a89cdda1c815f49543f8361b225776ad46f84690eddf9a9b884cf0ab540924ecae94ebd145f26ac07e44ffdf7c4ff1f2
7
- data.tar.gz: 3670666ca95e154a253be800af600cdcaf44195b9dcd3d001e1404d85f27dd5939ce09bd25588cfbe81cefef92ce22bfcd26e105dcb70d813bc4efa221a4360d
6
+ metadata.gz: 2c98c3ea7e22d0b023a328207c9599dca0f74b54d5ed05eea04378c5c2ed5a2803799b7dcafa54d041d3b7dac8b3e08c8559d4cacb03b2419796368fa153401e
7
+ data.tar.gz: 36c0faa98a5f6657a88ce770924e2669f91fcea2985b38f1c82dc0766d0c5a5cf6a4b0342d4684f5b679b41dc76f5fa94b791a6468b6f0514c28f6d70864b41a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.9.1] - 2023-10-06
4
+
5
+ - Allow successful empty responses (06bf7db)
6
+ - Update default User-Agent string (296b36a)
7
+ - Move query parameter escaping into RequestBuilder (56d6bd2)
8
+
3
9
  ## [0.9.0] - 2023-09-26
4
10
 
5
11
  - Add support for HTTP proxies (3740f4f)
data/README.md CHANGED
@@ -90,6 +90,14 @@ Building and maintaining an open-source project like this takes a considerable a
90
90
 
91
91
  [Click here to sponsor this project.](https://github.com/sponsors/sferik)
92
92
 
93
+ ## Sponsors
94
+
95
+ Many thanks to our sponsors (listed in order of when they sponsored this project):
96
+
97
+ <a href="https://betterstack.com"><img src="https://raw.githubusercontent.com/sferik/x-ruby/main/sponsor_logos/better_stack.svg" alt="Better Stack" width="200" align="middle"></a>
98
+ <img src="https://raw.githubusercontent.com/sferik/x-ruby/main/sponsor_logos/spacer.png" width="20" align="middle">
99
+ <a href="https://sentry.io"><img src="https://raw.githubusercontent.com/sferik/x-ruby/main/sponsor_logos/sentry.svg" alt="Sentry" width="200" align="middle"></a>
100
+
93
101
  ## Development
94
102
 
95
103
  1. Checkout and repo:
@@ -72,22 +72,18 @@ module X
72
72
  end
73
73
 
74
74
  def signature_base_string(method, url, params)
75
- "#{method}&#{encode(url)}&#{encode(encode_params(params))}"
76
- end
77
-
78
- def encode_params(params)
79
- params.sort.map { |k, v| "#{k}=#{encode(v.to_s)}" }.join("&")
75
+ "#{method}&#{escape(url)}&#{escape(URI.encode_www_form(params.sort))}"
80
76
  end
81
77
 
82
78
  def signing_key
83
- "#{encode(api_key_secret)}&#{encode(access_token_secret)}"
79
+ "#{escape(api_key_secret)}&#{escape(access_token_secret)}"
84
80
  end
85
81
 
86
82
  def format_oauth_header(params)
87
- "OAuth #{params.sort.map { |k, v| "#{k}=\"#{encode(v.to_s)}\"" }.join(", ")}"
83
+ "OAuth #{params.sort.map { |k, v| "#{k}=\"#{escape(v.to_s)}\"" }.join(", ")}"
88
84
  end
89
85
 
90
- def encode(value)
86
+ def escape(value)
91
87
  # TODO: Replace CGI.escape with CGI.escapeURIComponent when support for Ruby 3.1 is dropped
92
88
  CGI.escape(value.to_s).gsub("+", "%20")
93
89
  end
@@ -12,7 +12,7 @@ module X
12
12
  delete: Net::HTTP::Delete
13
13
  }.freeze
14
14
  DEFAULT_CONTENT_TYPE = "application/json; charset=utf-8".freeze
15
- DEFAULT_USER_AGENT = "X-Client/#{VERSION} Ruby/#{RUBY_VERSION}".freeze
15
+ DEFAULT_USER_AGENT = "X-Client/#{VERSION} #{RUBY_ENGINE}/#{RUBY_VERSION} (#{RUBY_PLATFORM})".freeze
16
16
  AUTHORIZATION_HEADER = "Authorization".freeze
17
17
  CONTENT_TYPE_HEADER = "Content-Type".freeze
18
18
  USER_AGENT_HEADER = "User-Agent".freeze
@@ -24,8 +24,8 @@ module X
24
24
  @user_agent = user_agent
25
25
  end
26
26
 
27
- def build(authenticator, http_method, url, body: nil)
28
- request = create_request(http_method, url, body)
27
+ def build(authenticator, http_method, uri, body: nil)
28
+ request = create_request(http_method, uri, body)
29
29
  add_authorization(request, authenticator)
30
30
  add_content_type(request)
31
31
  add_user_agent(request)
@@ -34,12 +34,13 @@ module X
34
34
 
35
35
  private
36
36
 
37
- def create_request(http_method, url, body)
37
+ def create_request(http_method, uri, body)
38
38
  http_method_class = HTTP_METHODS[http_method]
39
39
 
40
40
  raise ArgumentError, "Unsupported HTTP method: #{http_method}" unless http_method_class
41
41
 
42
- request = http_method_class.new(url)
42
+ escaped_uri = escape_query_params(uri)
43
+ request = http_method_class.new(escaped_uri)
43
44
  request.body = body if body && http_method != :get
44
45
  request
45
46
  end
@@ -55,5 +56,9 @@ module X
55
56
  def add_user_agent(request)
56
57
  request.add_field(USER_AGENT_HEADER, user_agent) if user_agent
57
58
  end
59
+
60
+ def escape_query_params(uri)
61
+ URI(uri).tap { |u| u.query = URI.encode_www_form(URI.decode_www_form(uri.query)) if uri.query }
62
+ end
58
63
  end
59
64
  end
@@ -32,19 +32,23 @@ module X
32
32
  end
33
33
 
34
34
  def handle(response)
35
- if successful_json_response?(response)
36
- return JSON.parse(response.body, array_class: array_class, object_class: object_class)
35
+ if success?(response)
36
+ JSON.parse(response.body, array_class: array_class, object_class: object_class) if json?(response)
37
+ else
38
+ error_class = ERROR_CLASSES[response.code.to_i] || Error
39
+ error_message = "#{response.code} #{response.message}"
40
+ raise error_class.new(error_message, response: response)
37
41
  end
38
-
39
- error_class = ERROR_CLASSES[response.code.to_i] || Error
40
- error_message = "#{response.code} #{response.message}"
41
- raise error_class.new(error_message, response: response)
42
42
  end
43
43
 
44
44
  private
45
45
 
46
- def successful_json_response?(response)
47
- response.is_a?(Net::HTTPSuccess) && response.body && JSON_CONTENT_TYPE_REGEXP.match?(response["content-type"])
46
+ def success?(response)
47
+ response.is_a?(Net::HTTPSuccess)
48
+ end
49
+
50
+ def json?(response)
51
+ response.body && JSON_CONTENT_TYPE_REGEXP.match?(response["content-type"])
48
52
  end
49
53
  end
50
54
  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.9.0")
4
+ VERSION = Gem::Version.create("0.9.1")
5
5
  end
data/sig/x.rbs CHANGED
@@ -28,10 +28,9 @@ module X
28
28
  def generate_signature: (String method, String url, Hash[String, String] params) -> String
29
29
  def hmac_signature: (String base_string) -> String
30
30
  def signature_base_string: (String method, String url, Hash[String, String] params) -> String
31
- def encode_params: (Hash[String, String] params) -> String
32
31
  def signing_key: -> String
33
32
  def format_oauth_header: (Hash[String, String] params) -> String
34
- def encode: (String value) -> String
33
+ def escape: (String value) -> String
35
34
  end
36
35
 
37
36
  class Error < StandardError
@@ -121,13 +120,14 @@ module X
121
120
  attr_accessor content_type: String
122
121
  attr_accessor user_agent: String
123
122
  def initialize: (?content_type: String, ?user_agent: String) -> void
124
- def build: (BearerTokenAuthenticator | OauthAuthenticator authenticator, Symbol http_method, URI::Generic url, ?body: String?) -> (Net::HTTPRequest)
123
+ def build: (BearerTokenAuthenticator | OauthAuthenticator authenticator, Symbol http_method, URI::Generic uri, ?body: String?) -> (Net::HTTPRequest)
125
124
 
126
125
  private
127
- def create_request: (Symbol http_method, URI::Generic url, String? body) -> (Net::HTTPRequest)
126
+ def create_request: (Symbol http_method, URI::Generic uri, String? body) -> (Net::HTTPRequest)
128
127
  def add_authorization: (Net::HTTPRequest request, BearerTokenAuthenticator | OauthAuthenticator authenticator) -> void
129
128
  def add_content_type: (Net::HTTPRequest request) -> void
130
129
  def add_user_agent: (Net::HTTPRequest request) -> void
130
+ def escape_query_params: (URI::Generic uri) -> URI::Generic
131
131
  end
132
132
 
133
133
  class RedirectHandler
@@ -155,10 +155,11 @@ module X
155
155
  attr_accessor array_class: Class
156
156
  attr_accessor object_class: Class
157
157
  def initialize: (?array_class: Class, ?object_class: Class) -> void
158
- def handle: (Net::HTTPResponse response) -> Hash[String, untyped]
158
+ def handle: (Net::HTTPResponse response) -> Hash[String, untyped]?
159
159
 
160
160
  private
161
- def successful_json_response?: (Net::HTTPResponse response) -> bool
161
+ def success?: (Net::HTTPResponse response) -> bool
162
+ def json?: (Net::HTTPResponse response) -> bool
162
163
  end
163
164
 
164
165
  class Client
@@ -171,13 +172,13 @@ module X
171
172
 
172
173
  attr_reader base_uri: URI::Generic
173
174
  def initialize: (?bearer_token: String?, ?api_key: String?, ?api_key_secret: String?, ?access_token: String?, ?access_token_secret: String?, ?base_url: URI::Generic | String, ?content_type: String, ?user_agent: String, ?open_timeout: Float | Integer, ?read_timeout: Float | Integer, ?write_timeout: Float | Integer, ?proxy_url: URI::Generic? | String?, ?debug_output: IO?, ?array_class: Class, ?object_class: Class, ?max_redirects: Integer) -> void
174
- def get: (String endpoint) -> Hash[String, untyped]
175
- def post: (String endpoint, ?nil body) -> Hash[String, untyped]
176
- def put: (String endpoint, ?nil body) -> Hash[String, untyped]
177
- def delete: (String endpoint) -> Hash[String, untyped]
175
+ def get: (String endpoint) -> Hash[String, untyped]?
176
+ def post: (String endpoint, ?nil body) -> Hash[String, untyped]?
177
+ def put: (String endpoint, ?nil body) -> Hash[String, untyped]?
178
+ def delete: (String endpoint) -> Hash[String, untyped]?
178
179
 
179
180
  private
180
181
  def initialize_authenticator: (String? bearer_token, String? api_key, String? api_key_secret, String? access_token, String? access_token_secret) -> (BearerTokenAuthenticator | OauthAuthenticator)
181
- def send_request: (Symbol http_method, String endpoint, ?nil body) -> Hash[String, untyped]
182
+ def send_request: (Symbol http_method, String endpoint, ?nil body) -> Hash[String, untyped]?
182
183
  end
183
184
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: x
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Berlin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-09-26 00:00:00.000000000 Z
11
+ date: 2023-10-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -69,7 +69,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
69
  - !ruby/object:Gem::Version
70
70
  version: '0'
71
71
  requirements: []
72
- rubygems_version: 3.4.19
72
+ rubygems_version: 3.4.20
73
73
  signing_key:
74
74
  specification_version: 4
75
75
  summary: A Ruby interface to the X API.