Skip to content

Commit b770c54

Browse files
committed
Add YARD/yardstick and require 100% documentation coverage
1 parent 93f993e commit b770c54

35 files changed

Lines changed: 830 additions & 9 deletions

.github/workflows/docs.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: docs
2+
on: [push, pull_request]
3+
jobs:
4+
build:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- uses: actions/checkout@v4
8+
- uses: ruby/setup-ruby@v1
9+
with:
10+
ruby-version: "3.4"
11+
bundler-cache: true
12+
- run: bundle exec rake yardstick

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ gem "simplecov", ">= 0.22"
1919
gem "standard", ">= 1.35.1"
2020
gem "steep", ">= 1.10"
2121
gem "webmock", ">= 3.18.1"
22+
gem "yard", ">= 0.9"
23+
gem "yardstick", ">= 0.9"

Rakefile

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,25 @@ task :mutant do
2929
sh "bundle exec mutant run"
3030
end
3131

32+
require "yard"
33+
34+
YARD::Rake::YardocTask.new(:yard) do |t|
35+
t.files = ["lib/**/*.rb"]
36+
t.options = ["--no-private"]
37+
end
38+
39+
require "yardstick/rake/measurement"
40+
require "yardstick/rake/verify"
41+
42+
Yardstick::Rake::Measurement.new(:yardstick_measure) do |measurement|
43+
measurement.output = "doc/coverage.txt"
44+
end
45+
46+
Yardstick::Rake::Verify.new(:yardstick) do |verify|
47+
verify.threshold = 100
48+
end
49+
3250
desc "Run linters"
3351
task lint: %i[rubocop standard]
3452

35-
task default: %i[test lint mutant steep]
53+
task default: %i[test lint mutant steep yardstick]

lib/x/authenticator.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,19 @@
1+
# A Ruby client for the X API
12
module X
3+
# Base class for authentication
4+
# @api public
25
class Authenticator
6+
# The HTTP header name for authentication
37
AUTHENTICATION_HEADER = "Authorization".freeze
48

9+
# Generate the authentication header for a request
10+
#
11+
# @api public
12+
# @param _request [Net::HTTPRequest] the HTTP request
13+
# @return [Hash{String => String}] the authentication header
14+
# @example Generate an empty authentication header
15+
# authenticator = X::Authenticator.new
16+
# authenticator.header(request)
517
def header(_request)
618
{AUTHENTICATION_HEADER => ""}
719
end

lib/x/bearer_token_authenticator.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
require_relative "authenticator"
22

33
module X
4+
# Authenticator for Bearer token authentication
5+
# @api public
46
class BearerTokenAuthenticator < Authenticator
7+
# The bearer token for authentication
8+
# @api public
9+
# @return [String] the bearer token
10+
# @example Get the bearer token
11+
# authenticator.bearer_token
512
attr_accessor :bearer_token
613

14+
# Initialize a new BearerTokenAuthenticator
15+
#
16+
# @api public
17+
# @param bearer_token [String] the bearer token for authentication
18+
# @return [BearerTokenAuthenticator] a new instance
19+
# @example Create a new bearer token authenticator
20+
# authenticator = X::BearerTokenAuthenticator.new(bearer_token: "token")
721
def initialize(bearer_token:) # rubocop:disable Lint/MissingSuper
822
@bearer_token = bearer_token
923
end
1024

25+
# Generate the authentication header for a request
26+
#
27+
# @api public
28+
# @param _request [Net::HTTPRequest] the HTTP request
29+
# @return [Hash{String => String}] the authentication header with bearer token
30+
# @example Generate a bearer authentication header
31+
# authenticator.header(request)
1132
def header(_request)
1233
{AUTHENTICATION_HEADER => "Bearer #{bearer_token}"}
1334
end

lib/x/client.rb

Lines changed: 189 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,106 @@
77
require_relative "response_parser"
88

99
module X
10+
# A client for interacting with the X API
11+
# @api public
1012
class Client
1113
extend Forwardable
1214

15+
# Default base URL for the X API
1316
DEFAULT_BASE_URL = "https://api.twitter.com/2/".freeze
17+
# Default class for parsing JSON arrays
1418
DEFAULT_ARRAY_CLASS = Array
19+
# Default class for parsing JSON objects
1520
DEFAULT_OBJECT_CLASS = Hash
1621

17-
attr_accessor :base_url, :default_array_class, :default_object_class
18-
attr_reader :api_key, :api_key_secret, :access_token, :access_token_secret, :bearer_token
22+
# The base URL for API requests
23+
# @api public
24+
# @return [String] the base URL for API requests
25+
# @example Get or set the base URL
26+
# client.base_url = "https://api.x.com/2/"
27+
attr_accessor :base_url
28+
29+
# The default class for parsing JSON arrays
30+
# @api public
31+
# @return [Class] the default class for parsing JSON arrays
32+
# @example Get or set the default array class
33+
# client.default_array_class = Array
34+
attr_accessor :default_array_class
35+
36+
# The default class for parsing JSON objects
37+
# @api public
38+
# @return [Class] the default class for parsing JSON objects
39+
# @example Get or set the default object class
40+
# client.default_object_class = Hash
41+
attr_accessor :default_object_class
42+
43+
# The API key for OAuth authentication
44+
# @api public
45+
# @return [String, nil] the API key for OAuth authentication
46+
# @example Get the API key
47+
# client.api_key
48+
attr_reader :api_key
49+
50+
# The API key secret for OAuth authentication
51+
# @api public
52+
# @return [String, nil] the API key secret for OAuth authentication
53+
# @example Get the API key secret
54+
# client.api_key_secret
55+
attr_reader :api_key_secret
56+
57+
# The access token for OAuth authentication
58+
# @api public
59+
# @return [String, nil] the access token for OAuth authentication
60+
# @example Get the access token
61+
# client.access_token
62+
attr_reader :access_token
63+
64+
# The access token secret for OAuth authentication
65+
# @api public
66+
# @return [String, nil] the access token secret for OAuth authentication
67+
# @example Get the access token secret
68+
# client.access_token_secret
69+
attr_reader :access_token_secret
70+
71+
# The bearer token for authentication
72+
# @api public
73+
# @return [String, nil] the bearer token for authentication
74+
# @example Get the bearer token
75+
# client.bearer_token
76+
attr_reader :bearer_token
1977

2078
def_delegators :@connection, :open_timeout, :read_timeout, :write_timeout, :proxy_url, :debug_output
2179
def_delegators :@connection, :open_timeout=, :read_timeout=, :write_timeout=, :proxy_url=, :debug_output=
2280
def_delegators :@redirect_handler, :max_redirects
2381
def_delegators :@redirect_handler, :max_redirects=
2482

83+
# Initialize a new X API client
84+
#
85+
# @api public
86+
# @param api_key [String, nil] the API key for OAuth authentication
87+
# @param api_key_secret [String, nil] the API key secret for OAuth authentication
88+
# @param access_token [String, nil] the access token for OAuth authentication
89+
# @param access_token_secret [String, nil] the access token secret for OAuth authentication
90+
# @param bearer_token [String, nil] the bearer token for authentication
91+
# @param base_url [String] the base URL for API requests
92+
# @param open_timeout [Integer] the timeout for opening connections in seconds
93+
# @param read_timeout [Integer] the timeout for reading responses in seconds
94+
# @param write_timeout [Integer] the timeout for writing requests in seconds
95+
# @param debug_output [IO] the IO object for debug output
96+
# @param proxy_url [String, nil] the proxy URL for requests
97+
# @param default_array_class [Class] the default class for parsing JSON arrays
98+
# @param default_object_class [Class] the default class for parsing JSON objects
99+
# @param max_redirects [Integer] the maximum number of redirects to follow
100+
# @return [Client] a new client instance
101+
# @example Create a client with bearer token authentication
102+
# client = X::Client.new(bearer_token: "token")
103+
# @example Create a client with OAuth authentication
104+
# client = X::Client.new(
105+
# api_key: "key",
106+
# api_key_secret: "secret",
107+
# access_token: "token",
108+
# access_token_secret: "token_secret"
109+
# )
25110
def initialize(api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil,
26111
bearer_token: nil,
27112
base_url: DEFAULT_BASE_URL,
@@ -43,49 +128,134 @@ def initialize(api_key: nil, api_key_secret: nil, access_token: nil, access_toke
43128
@response_parser = ResponseParser.new
44129
end
45130

131+
# Make a GET request to the API
132+
#
133+
# @api public
134+
# @param endpoint [String] the API endpoint
135+
# @param headers [Hash] additional headers for the request
136+
# @param array_class [Class] the class for parsing JSON arrays
137+
# @param object_class [Class] the class for parsing JSON objects
138+
# @return [Hash, Array, nil] the parsed response body
139+
# @example Get user information
140+
# client.get("users/me")
46141
def get(endpoint, headers: {}, array_class: default_array_class, object_class: default_object_class)
47142
execute_request(:get, endpoint, headers:, array_class:, object_class:)
48143
end
49144

145+
# Make a POST request to the API
146+
#
147+
# @api public
148+
# @param endpoint [String] the API endpoint
149+
# @param body [String, nil] the request body
150+
# @param headers [Hash] additional headers for the request
151+
# @param array_class [Class] the class for parsing JSON arrays
152+
# @param object_class [Class] the class for parsing JSON objects
153+
# @return [Hash, Array, nil] the parsed response body
154+
# @example Create a tweet
155+
# client.post("tweets", '{"text": "Hello, world!"}')
50156
def post(endpoint, body = nil, headers: {}, array_class: default_array_class, object_class: default_object_class)
51157
execute_request(:post, endpoint, body:, headers:, array_class:, object_class:)
52158
end
53159

160+
# Make a PUT request to the API
161+
#
162+
# @api public
163+
# @param endpoint [String] the API endpoint
164+
# @param body [String, nil] the request body
165+
# @param headers [Hash] additional headers for the request
166+
# @param array_class [Class] the class for parsing JSON arrays
167+
# @param object_class [Class] the class for parsing JSON objects
168+
# @return [Hash, Array, nil] the parsed response body
169+
# @example Update a resource
170+
# client.put("resource/123", '{"key": "value"}')
54171
def put(endpoint, body = nil, headers: {}, array_class: default_array_class, object_class: default_object_class)
55172
execute_request(:put, endpoint, body:, headers:, array_class:, object_class:)
56173
end
57174

175+
# Make a DELETE request to the API
176+
#
177+
# @api public
178+
# @param endpoint [String] the API endpoint
179+
# @param headers [Hash] additional headers for the request
180+
# @param array_class [Class] the class for parsing JSON arrays
181+
# @param object_class [Class] the class for parsing JSON objects
182+
# @return [Hash, Array, nil] the parsed response body
183+
# @example Delete a tweet
184+
# client.delete("tweets/123")
58185
def delete(endpoint, headers: {}, array_class: default_array_class, object_class: default_object_class)
59186
execute_request(:delete, endpoint, headers:, array_class:, object_class:)
60187
end
61188

189+
# Set the API key for OAuth authentication
190+
#
191+
# @api public
192+
# @param api_key [String] the API key
193+
# @return [void]
194+
# @example Set the API key
195+
# client.api_key = "new_key"
62196
def api_key=(api_key)
63197
@api_key = api_key
64198
initialize_authenticator
65199
end
66200

201+
# Set the API key secret for OAuth authentication
202+
#
203+
# @api public
204+
# @param api_key_secret [String] the API key secret
205+
# @return [void]
206+
# @example Set the API key secret
207+
# client.api_key_secret = "new_secret"
67208
def api_key_secret=(api_key_secret)
68209
@api_key_secret = api_key_secret
69210
initialize_authenticator
70211
end
71212

213+
# Set the access token for OAuth authentication
214+
#
215+
# @api public
216+
# @param access_token [String] the access token
217+
# @return [void]
218+
# @example Set the access token
219+
# client.access_token = "new_token"
72220
def access_token=(access_token)
73221
@access_token = access_token
74222
initialize_authenticator
75223
end
76224

225+
# Set the access token secret for OAuth authentication
226+
#
227+
# @api public
228+
# @param access_token_secret [String] the access token secret
229+
# @return [void]
230+
# @example Set the access token secret
231+
# client.access_token_secret = "new_secret"
77232
def access_token_secret=(access_token_secret)
78233
@access_token_secret = access_token_secret
79234
initialize_authenticator
80235
end
81236

237+
# Set the bearer token for authentication
238+
#
239+
# @api public
240+
# @param bearer_token [String] the bearer token
241+
# @return [void]
242+
# @example Set the bearer token
243+
# client.bearer_token = "new_token"
82244
def bearer_token=(bearer_token)
83245
@bearer_token = bearer_token
84246
initialize_authenticator
85247
end
86248

87249
private
88250

251+
# Initialize OAuth credentials
252+
# @api private
253+
# @param api_key [String, nil] the API key
254+
# @param api_key_secret [String, nil] the API key secret
255+
# @param access_token [String, nil] the access token
256+
# @param access_token_secret [String, nil] the access token secret
257+
# @param bearer_token [String, nil] the bearer token
258+
# @return [void]
89259
def initialize_oauth(api_key, api_key_secret, access_token, access_token_secret, bearer_token)
90260
@api_key = api_key
91261
@api_key_secret = api_key_secret
@@ -94,11 +264,19 @@ def initialize_oauth(api_key, api_key_secret, access_token, access_token_secret,
94264
@bearer_token = bearer_token
95265
end
96266

267+
# Initialize default classes for JSON parsing
268+
# @api private
269+
# @param default_array_class [Class] the default array class
270+
# @param default_object_class [Class] the default object class
271+
# @return [void]
97272
def initialize_default_classes(default_array_class, default_object_class)
98273
@default_array_class = default_array_class
99274
@default_object_class = default_object_class
100275
end
101276

277+
# Initialize the authenticator based on available credentials
278+
# @api private
279+
# @return [void]
102280
def initialize_authenticator
103281
@authenticator = if api_key && api_key_secret && access_token && access_token_secret
104282
OAuthAuthenticator.new(api_key:, api_key_secret:, access_token:, access_token_secret:)
@@ -111,6 +289,15 @@ def initialize_authenticator
111289
end
112290
end
113291

292+
# Execute an HTTP request
293+
# @api private
294+
# @param http_method [Symbol] the HTTP method
295+
# @param endpoint [String] the API endpoint
296+
# @param body [String, nil] the request body
297+
# @param headers [Hash] additional headers
298+
# @param array_class [Class] the class for parsing JSON arrays
299+
# @param object_class [Class] the class for parsing JSON objects
300+
# @return [Hash, Array, nil] the parsed response body
114301
def execute_request(http_method, endpoint, body: nil, headers: {}, array_class: default_array_class, object_class: default_object_class)
115302
uri = URI.join(base_url, endpoint)
116303
request = @request_builder.build(http_method:, uri:, body:, headers:, authenticator: @authenticator)

0 commit comments

Comments
 (0)