x 0.1.0 → 0.2.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: 480eb23ebeb098bcc5c2d4924279d2d4608f8cf2a8e5eb7897edf45cc0311bc4
4
- data.tar.gz: c97f4694a91b3d3ad9f60d55001d48080329254d28db75aff74d646264fe38f2
3
+ metadata.gz: c40d2b65cf1479d65e6f434d6d9c98cb32ad7aa62b739fd1207b491b7e276e4d
4
+ data.tar.gz: 6dee4de59342deb6bbc8a5897f7a96e18ae6b7a856083ae84e1ee26580ce7482
5
5
  SHA512:
6
- metadata.gz: 86adf1889c2e820353c4455a08c443480682d4827addd074a7e1fe3e60937e70b638118d31802c8cc57d457865c917d2f1a83d739448659519f42c9cbede74ed
7
- data.tar.gz: 714dcea3deff757a2d15548ed1fdbb17749ca5b901e9463a5b3a223120b05974ab792d1c3230406dc07984358a6d9e7c4d781402aa025fa5e257d967ee5ecbcf
6
+ metadata.gz: d64f01c2f011e53621214597fd1a9ac80e777ce22db2eb5d48c6b2f760743efa2fa3aa6848e6549b3fc25290c80456873262ddf92dbffaff2d134040cb87555f
7
+ data.tar.gz: 8fc7d2aca33c67534ac775cc97f6afce95ef035716c5799e3571fb7b3a0218fd5b4646b41d8c66f61e3bba1d58f22b6c56c67ca273199b99dd7c55829827215a
data/.rubocop.yml CHANGED
@@ -15,8 +15,8 @@ Style/StringLiteralsInInterpolation:
15
15
  Enabled: true
16
16
  EnforcedStyle: double_quotes
17
17
 
18
- Layout/LineLength:
19
- Max: 120
20
-
21
18
  Style/FrozenStringLiteralComment:
22
19
  Enabled: false
20
+
21
+ Metrics/ParameterLists:
22
+ CountKeywordArgs: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2023-08-02
4
+
5
+ - Allow configuration of base URL (4bc0531)
6
+ - Improve error handling (14dc0cd)
7
+
3
8
  ## [0.1.0] - 2023-08-02
4
9
 
5
10
  - Initial release
data/Gemfile CHANGED
@@ -9,4 +9,5 @@ gem "rubocop", ">= 1.21"
9
9
  gem "rubocop-minitest", ">= 0.31"
10
10
  gem "rubocop-performance", ">= 1.18"
11
11
  gem "rubocop-rake", ">= 0.6"
12
+ gem "simplecov", ">= 0.22"
12
13
  gem "webmock", ">= 3.18.1"
data/README.md CHANGED
@@ -15,17 +15,20 @@ If bundler is not being used to manage dependencies, install the gem by executin
15
15
  ## Usage
16
16
 
17
17
  ```ruby
18
- api_key = "YOUR_X_API_KEY"
19
- api_key_secret = "YOUR_X_API_KEY_SECRET"
20
- access_token = "YOUR_X_ACCESS_TOKEN"
21
- access_token_secret = "YOUR_X_ACCESS_TOKEN_SECRET"
18
+ x_api_key = "YOUR_X_API_KEY"
19
+ x_api_key_secret = "YOUR_X_API_KEY_SECRET"
20
+ x_access_token = "YOUR_X_ACCESS_TOKEN"
21
+ x_access_token_secret = "YOUR_X_ACCESS_TOKEN_SECRET"
22
22
 
23
- x_client = X::Client.new(api_key: api_key, api_key_secret: api_key_secret, access_token: access_token, access_token_secret: access_token_secret)
23
+ x_client = X::Client.new(api_key: x_api_key,
24
+ api_key_secret: x_api_key_secret,
25
+ access_token: x_access_token,
26
+ access_token_secret: x_access_token_secret)
24
27
 
25
28
  begin
26
29
  response = x_client.get("users/me")
27
30
  puts JSON.pretty_generate(response)
28
- rescue StandardError => e
31
+ rescue X::Error => e
29
32
  puts "Error: #{e.message}"
30
33
  end
31
34
  ```
data/lib/x/client.rb CHANGED
@@ -1,94 +1,193 @@
1
- require "oauth"
2
1
  require "json"
3
2
  require "net/http"
3
+ require "oauth"
4
4
 
5
5
  module X
6
- # Main client that handles HTTP authentication and requests
6
+ # HTTP client that handles authentication and requests
7
7
  class Client
8
- BASE_URL = "https://api.twitter.com/2/".freeze
9
- HTTP_METHODS = {
10
- get: Net::HTTP::Get,
11
- post: Net::HTTP::Post,
12
- put: Net::HTTP::Put,
13
- delete: Net::HTTP::Delete
14
- }.freeze
15
-
16
- def initialize(bearer_token: nil, api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil)
17
- @use_bearer_token = !bearer_token.nil?
18
-
19
- if @use_bearer_token
20
- @bearer_token = bearer_token
21
- else
22
- unless api_key && api_key_secret && access_token && access_token_secret
23
- raise ArgumentError, "Missing OAuth credentials."
24
- end
25
-
26
- @consumer = OAuth::Consumer.new(api_key, api_key_secret, site: BASE_URL)
27
- @access_token = OAuth::Token.new(access_token, access_token_secret)
28
- end
8
+ DEFAULT_BASE_URL = "https://api.twitter.com/2/".freeze
9
+ DEFAULT_USER_AGENT = "X-Client/#{VERSION} Ruby/#{RUBY_VERSION}".freeze
10
+
11
+ def initialize(bearer_token: nil, api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil,
12
+ base_url: DEFAULT_BASE_URL, user_agent: DEFAULT_USER_AGENT)
13
+ @http_request = HttpRequest.new(bearer_token: bearer_token,
14
+ api_key: api_key,
15
+ api_key_secret: api_key_secret,
16
+ access_token: access_token,
17
+ access_token_secret: access_token_secret,
18
+ base_url: base_url,
19
+ user_agent: user_agent)
29
20
  end
30
21
 
31
22
  def get(endpoint)
32
- response = send_request(:get, endpoint)
33
- handle_response(response)
23
+ handle_response { @http_request.get(endpoint) }
34
24
  end
35
25
 
36
26
  def post(endpoint, body = nil)
37
- response = send_request(:post, endpoint, body)
38
- handle_response(response)
27
+ handle_response { @http_request.post(endpoint, body) }
39
28
  end
40
29
 
41
30
  def put(endpoint, body = nil)
42
- response = send_request(:put, endpoint, body)
43
- handle_response(response)
31
+ handle_response { @http_request.put(endpoint, body) }
44
32
  end
45
33
 
46
34
  def delete(endpoint)
47
- response = send_request(:delete, endpoint)
48
- handle_response(response)
35
+ handle_response { @http_request.delete(endpoint) }
49
36
  end
50
37
 
51
38
  private
52
39
 
53
- def send_request(http_method, endpoint, body = nil)
54
- url = URI.parse(BASE_URL + endpoint)
55
- http = Net::HTTP.new(url.host, url.port)
56
- http.use_ssl = true
40
+ def handle_response
41
+ response = yield
42
+ ErrorHandler.new(response).handle
43
+ end
57
44
 
58
- request = create_request(http_method, url, body)
59
- add_authorization(request)
45
+ # HTTP client requester
46
+ class HttpRequest
47
+ HTTP_METHODS = {
48
+ get: Net::HTTP::Get,
49
+ post: Net::HTTP::Post,
50
+ put: Net::HTTP::Put,
51
+ delete: Net::HTTP::Delete
52
+ }.freeze
53
+
54
+ def initialize(bearer_token: nil, api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil,
55
+ base_url: nil, user_agent: nil)
56
+ @base_url = base_url
57
+ @use_bearer_token = !bearer_token.nil?
58
+ @user_agent = user_agent || Client::DEFAULT_USER_AGENT
59
+
60
+ if @use_bearer_token
61
+ @bearer_token = bearer_token
62
+ else
63
+ initialize_oauth(api_key, api_key_secret, access_token, access_token_secret)
64
+ end
65
+ end
60
66
 
61
- http.request(request)
62
- end
67
+ def get(endpoint)
68
+ send_request(:get, endpoint)
69
+ end
63
70
 
64
- def create_request(http_method, url, body)
65
- http_method_class = HTTP_METHODS[http_method]
71
+ def post(endpoint, body = nil)
72
+ send_request(:post, endpoint, body)
73
+ end
66
74
 
67
- raise ArgumentError, "Unsupported HTTP method: #{http_method}" unless http_method_class
75
+ def put(endpoint, body = nil)
76
+ send_request(:put, endpoint, body)
77
+ end
68
78
 
69
- request = http_method_class.new(url)
70
- request.body = body if body && http_method != :get
71
- request
72
- end
79
+ def delete(endpoint)
80
+ send_request(:delete, endpoint)
81
+ end
82
+
83
+ private
84
+
85
+ def initialize_oauth(api_key, api_key_secret, access_token, access_token_secret)
86
+ unless api_key && api_key_secret && access_token && access_token_secret
87
+ raise ArgumentError, "Missing OAuth credentials."
88
+ end
89
+
90
+ @consumer = OAuth::Consumer.new(api_key, api_key_secret, site: @base_url)
91
+ @access_token = OAuth::Token.new(access_token, access_token_secret)
92
+ end
93
+
94
+ def send_request(http_method, endpoint, body = nil)
95
+ url = URI.parse(@base_url + endpoint)
96
+ http = Net::HTTP.new(url.host, url.port)
97
+ http.use_ssl = true
98
+
99
+ request = create_request(http_method, url, body)
100
+ add_authorization(request)
101
+ add_user_agent(request)
102
+
103
+ http.request(request)
104
+ end
73
105
 
74
- def add_authorization(request)
75
- if @use_bearer_token
76
- request["Authorization"] = "Bearer #{@bearer_token}"
77
- else
78
- @consumer.sign!(request, @access_token)
106
+ def create_request(http_method, url, body)
107
+ http_method_class = HTTP_METHODS[http_method]
108
+
109
+ raise ArgumentError, "Unsupported HTTP method: #{http_method}" unless http_method_class
110
+
111
+ request = http_method_class.new(url)
112
+ request.body = body if body && http_method != :get
113
+ request
114
+ end
115
+
116
+ def add_authorization(request)
117
+ if @use_bearer_token
118
+ request["Authorization"] = "Bearer #{@bearer_token}"
119
+ else
120
+ @consumer.sign!(request, @access_token)
121
+ end
122
+ end
123
+
124
+ def add_user_agent(request)
125
+ request["User-Agent"] = @user_agent if @user_agent
79
126
  end
80
127
  end
81
128
 
82
- def handle_response(response)
83
- case response
84
- when Net::HTTPSuccess
85
- JSON.parse(response.body)
86
- when Net::HTTPUnauthorized
129
+ # HTTP client error handler
130
+ class ErrorHandler
131
+ HTTP_STATUS_HANDLERS = {
132
+ Net::HTTPOK => :handle_success_response,
133
+ Net::HTTPBadRequest => :handle_bad_request_response,
134
+ Net::HTTPForbidden => :handle_forbidden_response,
135
+ Net::HTTPUnauthorized => :handle_unauthorized_response,
136
+ Net::HTTPNotFound => :handle_not_found_response,
137
+ Net::HTTPTooManyRequests => :handle_too_many_requests_response,
138
+ Net::HTTPInternalServerError => :handle_server_error_response,
139
+ Net::HTTPServiceUnavailable => :handle_service_unavailable_response
140
+ }.freeze
141
+
142
+ def initialize(response)
143
+ @response = response
144
+ end
145
+
146
+ def handle
147
+ handler_method = HTTP_STATUS_HANDLERS[@response.class]
148
+ if handler_method
149
+ send(handler_method)
150
+ else
151
+ handle_unexpected_response
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ def handle_success_response
158
+ JSON.parse(@response.body)
159
+ end
160
+
161
+ def handle_bad_request_response
162
+ raise X::BadRequestError, "Bad request: #{@response.code} #{@response.message}"
163
+ end
164
+
165
+ def handle_forbidden_response
166
+ raise X::ForbiddenError, "Forbidden: #{@response.code} #{@response.message}"
167
+ end
168
+
169
+ def handle_unauthorized_response
87
170
  raise X::AuthenticationError, "Authentication failed. Please check your credentials."
88
- when Net::HTTPServerError
89
- raise X::ServerError, "An internal server error occurred."
90
- else
91
- raise X::Error, "Unexpected response: #{response.code} #{response.message}"
171
+ end
172
+
173
+ def handle_not_found_response
174
+ raise X::NotFoundError, "Not found: #{@response.code} #{@response.message}"
175
+ end
176
+
177
+ def handle_too_many_requests_response
178
+ raise X::TooManyRequestsError, "Too many requests: #{@response.code} #{@response.message}"
179
+ end
180
+
181
+ def handle_server_error_response
182
+ raise X::ServerError, "Internal server error: #{@response.code} #{@response.message}"
183
+ end
184
+
185
+ def handle_service_unavailable_response
186
+ raise X::ServiceUnavailableError, "Service unavailable: #{@response.code} #{@response.message}"
187
+ end
188
+
189
+ def handle_unexpected_response
190
+ raise X::Error, "Unexpected response: #{@response.code}"
92
191
  end
93
192
  end
94
193
  end
data/lib/x/errors.rb ADDED
@@ -0,0 +1,11 @@
1
+ module X
2
+ class Error < ::StandardError; end
3
+ class ClientError < Error; end
4
+ class AuthenticationError < ClientError; end
5
+ class BadRequestError < ClientError; end
6
+ class ForbiddenError < ClientError; end
7
+ class NotFoundError < ClientError; end
8
+ class TooManyRequestsError < ClientError; end
9
+ class ServerError < Error; end
10
+ class ServiceUnavailableError < ServerError; end
11
+ end
data/lib/x/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module X
2
- VERSION = "0.1.0".freeze
2
+ VERSION = "0.2.0".freeze
3
3
  end
data/lib/x.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  require_relative "x/version"
2
+ require_relative "x/errors"
2
3
  require_relative "x/client"
3
- require_relative "x/error"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: x
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Berlin
@@ -39,7 +39,7 @@ files:
39
39
  - Rakefile
40
40
  - lib/x.rb
41
41
  - lib/x/client.rb
42
- - lib/x/error.rb
42
+ - lib/x/errors.rb
43
43
  - lib/x/version.rb
44
44
  - sig/x.rbs
45
45
  homepage: https://github.com/sferik/x-ruby
data/lib/x/error.rb DELETED
@@ -1,5 +0,0 @@
1
- module X
2
- class Error < StandardError; end
3
- class AuthenticationError < Error; end
4
- class ServerError < Error; end
5
- end