api_adaptor 0.0.2 → 1.0.0

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.
@@ -1,92 +1,106 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Null logger class. This is essentially the same as sending data down the
4
- # `/dev/null` black hole.
5
- #
6
- # @example Basic Usage
7
- #
8
- # logger = NullLogger.new
9
- # Rails.logger = logger
10
- #
11
- #
12
- # @example Basic Pattern Usage
13
- # class SomeService
14
- # def initialize(options = {})
15
- # @logger = options[:logger] || NullLogger.new
16
- # end
17
- #
18
- # def perform
19
- # @logger.debug -> { "do some work here" }
20
- # # .. ..
21
- # @logger.info -> { "finished working" }
22
- # end
23
- # end
24
- #
25
- # service = SomeService.new(logger: Logger.new(STDOUT))
26
- # service.perform
27
- #
28
- # silent = SomeService.new(logger: NullLogger.new
29
- # silent.perform
30
- #
31
3
  module ApiAdaptor
4
+ # Null logger that discards all log messages.
5
+ #
6
+ # This logger implements the Logger interface but does nothing with the messages,
7
+ # sending them to the metaphorical /dev/null. Useful for testing or when logging
8
+ # is not desired.
9
+ #
10
+ # @example Basic usage
11
+ # logger = NullLogger.new
12
+ # logger.info("This message is discarded")
13
+ #
14
+ # @example Service pattern with optional logging
15
+ # class SomeService
16
+ # def initialize(options = {})
17
+ # @logger = options[:logger] || NullLogger.new
18
+ # end
19
+ #
20
+ # def perform
21
+ # @logger.debug { "do some work here" }
22
+ # # Work happens...
23
+ # @logger.info { "finished working" }
24
+ # end
25
+ # end
26
+ #
27
+ # # With logging
28
+ # service = SomeService.new(logger: Logger.new($stdout))
29
+ # service.perform
30
+ #
31
+ # # Silent (no logging)
32
+ # silent = SomeService.new # Uses NullLogger by default
33
+ # silent.perform
32
34
  class NullLogger
33
- # @param _args Anything that we want to ignore
35
+ # Logs an unknown severity message (discarded)
36
+ #
37
+ # @param _args [Array] Message arguments (ignored)
34
38
  # @return [nil]
35
39
  def unknown(*_args)
36
40
  nil
37
41
  end
38
42
 
39
- # @param _args Anything that we want to ignore
43
+ # Logs a fatal message (discarded)
44
+ #
45
+ # @param _args [Array] Message arguments (ignored)
40
46
  # @return [nil]
41
47
  def fatal(*_args)
42
48
  nil
43
49
  end
44
50
 
45
- # @return [FALSE]
51
+ # @return [Boolean] false (fatal logging is never enabled)
46
52
  def fatal?
47
53
  false
48
54
  end
49
55
 
50
- # @param _args Anything that we want to ignore
56
+ # Logs an error message (discarded)
57
+ #
58
+ # @param _args [Array] Message arguments (ignored)
51
59
  # @return [nil]
52
60
  def error(*_args)
53
61
  nil
54
62
  end
55
63
 
56
- # @return [FALSE]
64
+ # @return [Boolean] false (error logging is never enabled)
57
65
  def error?
58
66
  false
59
67
  end
60
68
 
61
- # @param _args Anything that we want to ignore
69
+ # Logs a warning message (discarded)
70
+ #
71
+ # @param _args [Array] Message arguments (ignored)
62
72
  # @return [nil]
63
73
  def warn(*_args)
64
74
  nil
65
75
  end
66
76
 
67
- # @return [FALSE]
77
+ # @return [Boolean] false (warn logging is never enabled)
68
78
  def warn?
69
79
  false
70
80
  end
71
81
 
72
- # @param _args Anything that we want to ignore
82
+ # Logs an info message (discarded)
83
+ #
84
+ # @param _args [Array] Message arguments (ignored)
73
85
  # @return [nil]
74
86
  def info(*_args)
75
87
  nil
76
88
  end
77
89
 
78
- # @return [FALSE]
90
+ # @return [Boolean] false (info logging is never enabled)
79
91
  def info?
80
92
  false
81
93
  end
82
94
 
83
- # @param _args Anything that we want to ignore
95
+ # Logs a debug message (discarded)
96
+ #
97
+ # @param _args [Array] Message arguments (ignored)
84
98
  # @return [nil]
85
99
  def debug(*_args)
86
100
  nil
87
101
  end
88
102
 
89
- # @return [FALSE]
103
+ # @return [Boolean] false (debug logging is never enabled)
90
104
  def debug?
91
105
  false
92
106
  end
@@ -4,66 +4,103 @@ require "json"
4
4
  require "forwardable"
5
5
 
6
6
  module ApiAdaptor
7
- # This wraps an HTTP response with a JSON body.
7
+ # Wraps an HTTP response with a JSON body and provides convenient access methods.
8
8
  #
9
- # Responses can be configured to use relative URLs for `web_url` properties.
10
- # API endpoints should return absolute URLs so that they make sense outside of the
11
- # domain context. However on systems within an API we want to present relative URLs.
12
- # By specifying a base URI, this will convert all matching web_urls into relative URLs
13
- # This is useful on non-canonical frontends, such as those in staging environments.
9
+ # Response objects parse JSON and provide hash-like access to the response body.
10
+ # They also handle cache control headers and can convert absolute URLs to relative URLs.
14
11
  #
15
- # Example:
12
+ # @example Basic usage
13
+ # response = client.get_json("https://api.example.com/users")
14
+ # users = response["results"]
15
+ # cache_duration = response.cache_control.max_age
16
16
  #
17
- # r = Response.new(response, web_urls_relative_to: "https://www.gov.uk")
18
- # r['results'][0]['web_url']
19
- # => "/bank-holidays"
17
+ # @example With relative URLs
18
+ # response = Response.new(http_response, web_urls_relative_to: "https://www.example.com")
19
+ # response['results'][0]['web_url'] # => "/foo" instead of "https://www.example.com/foo"
20
+ #
21
+ # @example Checking cache headers
22
+ # if response.cache_control.public? && response.cache_control.max_age > 300
23
+ # # Cache this response
24
+ # end
20
25
  class Response
21
26
  extend Forwardable
22
27
  include Enumerable
23
28
 
29
+ # Parses and provides access to HTTP Cache-Control header directives.
30
+ #
31
+ # @example
32
+ # cc = CacheControl.new("public, max-age=3600, must-revalidate")
33
+ # cc.public? # => true
34
+ # cc.max_age # => 3600
35
+ # cc.must_revalidate? # => true
24
36
  class CacheControl < Hash
25
- PATTERN = /([-a-z]+)(?:\s*=\s*([^,\s]+))?,?+/i.freeze
37
+ # Regex pattern for parsing Cache-Control directives
38
+ PATTERN = /([-a-z]+)(?:\s*=\s*([^,\s]+))?,?+/i
26
39
 
40
+ # Initializes a new CacheControl object by parsing a header value
41
+ #
42
+ # @param value [String, nil] Cache-Control header value
27
43
  def initialize(value = nil)
28
44
  super()
29
45
  parse(value)
30
46
  end
31
47
 
48
+ # @return [Boolean] true if cache is public
32
49
  def public?
33
50
  self["public"]
34
51
  end
35
52
 
53
+ # @return [Boolean] true if cache is private
36
54
  def private?
37
55
  self["private"]
38
56
  end
39
57
 
58
+ # @return [Boolean] true if no-cache directive is present
40
59
  def no_cache?
41
60
  self["no-cache"]
42
61
  end
43
62
 
63
+ # @return [Boolean] true if no-store directive is present
44
64
  def no_store?
45
65
  self["no-store"]
46
66
  end
47
67
 
68
+ # @return [Boolean] true if must-revalidate directive is present
48
69
  def must_revalidate?
49
70
  self["must-revalidate"]
50
71
  end
51
72
 
73
+ # @return [Boolean] true if proxy-revalidate directive is present
52
74
  def proxy_revalidate?
53
75
  self["proxy-revalidate"]
54
76
  end
55
77
 
78
+ # Returns the max-age directive value
79
+ #
80
+ # @return [Integer, nil] Maximum age in seconds, or nil if not present
56
81
  def max_age
57
82
  self["max-age"].to_i if key?("max-age")
58
83
  end
59
84
 
85
+ # Returns the r-maxage directive value
86
+ #
87
+ # Note: r-maxage is a non-standard Cache-Control directive and is not part
88
+ # of RFC 7234. This method exists for compatibility with custom cache implementations.
89
+ #
90
+ # @return [Integer, nil] Reverse maximum age in seconds, or nil if not present
60
91
  def reverse_max_age
61
92
  self["r-maxage"].to_i if key?("r-maxage")
62
93
  end
63
94
  alias r_maxage reverse_max_age
64
95
 
96
+ # Returns the s-maxage (shared max age) directive value
97
+ #
98
+ # The s-maxage directive is like max-age but only applies to shared caches
99
+ # (e.g., CDNs, proxies). It overrides max-age for shared caches.
100
+ #
101
+ # @return [Integer, nil] Shared maximum age in seconds, or nil if not present
65
102
  def shared_max_age
66
- self["s-maxage"].to_i if key?("r-maxage")
103
+ self["s-maxage"].to_i if key?("s-maxage")
67
104
  end
68
105
  alias s_maxage shared_max_age
69
106
 
@@ -93,26 +130,67 @@ module ApiAdaptor
93
130
  end
94
131
  end
95
132
 
133
+ # @!method [](key)
134
+ # Access parsed JSON response by key
135
+ # @param key [String, Symbol] The key to access
136
+ # @return [Object] The value at the key
137
+ #
138
+ # @!method <=>(other)
139
+ # Compare responses
140
+ # @param other [Response] Another response
141
+ # @return [Integer] Comparison result
142
+ #
143
+ # @!method each(&block)
144
+ # Iterate over parsed response hash
145
+ # @yield [key, value] Each key-value pair
146
+ #
147
+ # @!method dig(*keys)
148
+ # Dig into nested hash structure
149
+ # @param keys [Array] Keys to traverse
150
+ # @return [Object, nil] The value at the path or nil
96
151
  def_delegators :to_hash, :[], :"<=>", :each, :dig
97
152
 
153
+ # Initializes a new Response object
154
+ #
155
+ # @param http_response [RestClient::Response] The raw HTTP response
156
+ # @param options [Hash] Configuration options
157
+ # @option options [String] :web_urls_relative_to Base URL for converting absolute URLs to relative
158
+ #
159
+ # @example
160
+ # response = Response.new(http_response)
161
+ #
162
+ # @example With relative URLs
163
+ # response = Response.new(http_response, web_urls_relative_to: "https://www.example.com")
98
164
  def initialize(http_response, options = {})
99
165
  @http_response = http_response
100
166
  @web_urls_relative_to = options[:web_urls_relative_to] ? URI.parse(options[:web_urls_relative_to]) : nil
101
167
  end
102
168
 
169
+ # Returns the raw response body string
170
+ #
171
+ # @return [String] Raw response body
103
172
  def raw_response_body
104
173
  @http_response.body
105
174
  end
106
175
 
176
+ # Returns the HTTP status code
177
+ #
178
+ # @return [Integer] HTTP status code
107
179
  def code
108
180
  # Return an integer code for consistency with HTTPErrorResponse
109
181
  @http_response.code
110
182
  end
111
183
 
184
+ # Returns the response headers
185
+ #
186
+ # @return [Hash] HTTP headers
112
187
  def headers
113
188
  @http_response.headers
114
189
  end
115
190
 
191
+ # Calculates when the response expires based on cache headers
192
+ #
193
+ # @return [Time, nil] Expiration time or nil if not cacheable
116
194
  def expires_at
117
195
  if headers[:date] && cache_control.max_age
118
196
  response_date = Time.parse(headers[:date])
@@ -122,6 +200,9 @@ module ApiAdaptor
122
200
  end
123
201
  end
124
202
 
203
+ # Calculates how many seconds until the response expires
204
+ #
205
+ # @return [Integer, nil] Seconds until expiration or nil if not cacheable
125
206
  def expires_in
126
207
  return unless headers[:date]
127
208
 
@@ -134,22 +215,37 @@ module ApiAdaptor
134
215
  end
135
216
  end
136
217
 
218
+ # Returns parsed Cache-Control header
219
+ #
220
+ # @return [CacheControl] Parsed cache control directives
137
221
  def cache_control
138
222
  @cache_control ||= CacheControl.new(headers[:cache_control])
139
223
  end
140
224
 
225
+ # Returns the parsed JSON response as a hash
226
+ #
227
+ # @return [Hash] Parsed JSON response
141
228
  def to_hash
142
229
  parsed_content
143
230
  end
144
231
 
232
+ # Returns the parsed and transformed JSON content
233
+ #
234
+ # @return [Hash, Array] Parsed JSON with transformed web_urls
145
235
  def parsed_content
146
236
  @parsed_content ||= transform_parsed(JSON.parse(@http_response.body))
147
237
  end
148
238
 
239
+ # Always returns true (response is present)
240
+ #
241
+ # @return [Boolean] true
149
242
  def present?
150
243
  true
151
244
  end
152
245
 
246
+ # Always returns false (response is not blank)
247
+ #
248
+ # @return [Boolean] false
153
249
  def blank?
154
250
  false
155
251
  end
@@ -1,15 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ApiAdaptor
4
+ # Environment variable configuration for User-Agent metadata
5
+ #
6
+ # These variables are used to construct the User-Agent header for HTTP requests,
7
+ # allowing API providers to identify and contact clients if needed.
8
+ #
9
+ # @example Setting environment variables
10
+ # ENV["APP_NAME"] = "MyApiClient"
11
+ # ENV["APP_VERSION"] = "2.1.0"
12
+ # ENV["APP_CONTACT"] = "dev@example.com"
13
+ #
14
+ # @example User-Agent header format
15
+ # # "MyApiClient/2.1.0 (dev@example.com)"
16
+ #
17
+ # @see JSONClient.default_request_headers
4
18
  module Variables
19
+ # Returns the application name from environment variable
20
+ #
21
+ # @return [String] Application name (default: "Ruby ApiAdaptor App")
22
+ #
23
+ # @example
24
+ # ENV["APP_NAME"] = "WikidataClient"
25
+ # Variables.app_name # => "WikidataClient"
5
26
  def self.app_name
6
27
  ENV["APP_NAME"] || "Ruby ApiAdaptor App"
7
28
  end
8
29
 
30
+ # Returns the application version from environment variable
31
+ #
32
+ # @return [String] Application version (default: "Version not stated")
33
+ #
34
+ # @example
35
+ # ENV["APP_VERSION"] = "3.2.1"
36
+ # Variables.app_version # => "3.2.1"
9
37
  def self.app_version
10
38
  ENV["APP_VERSION"] || "Version not stated"
11
39
  end
12
40
 
41
+ # Returns the application contact from environment variable
42
+ #
43
+ # Should be an email address or URL where API providers can reach you.
44
+ #
45
+ # @return [String] Contact information (default: "Contact not stated")
46
+ #
47
+ # @example
48
+ # ENV["APP_CONTACT"] = "api@example.com"
49
+ # Variables.app_contact # => "api@example.com"
13
50
  def self.app_contact
14
51
  ENV["APP_CONTACT"] || "Contact not stated"
15
52
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ApiAdaptor
4
- VERSION = "0.0.2"
4
+ VERSION = "1.0.0"
5
5
  end
data/lib/api_adaptor.rb CHANGED
@@ -3,7 +3,37 @@
3
3
  require_relative "api_adaptor/version"
4
4
  require_relative "api_adaptor/base"
5
5
 
6
+ # ApiAdaptor provides a framework for building JSON API clients with minimal boilerplate.
7
+ #
8
+ # It handles common patterns like request/response parsing, authentication, redirect handling,
9
+ # pagination, and error management, allowing you to focus on your API's specific logic.
10
+ #
11
+ # @example Building a simple API client
12
+ # class MyApiClient < ApiAdaptor::Base
13
+ # def initialize
14
+ # super("https://api.example.com")
15
+ # end
16
+ #
17
+ # def get_user(id)
18
+ # get_json("/users/#{id}")
19
+ # end
20
+ #
21
+ # def create_user(data)
22
+ # post_json("/users", data)
23
+ # end
24
+ # end
25
+ #
26
+ # client = MyApiClient.new
27
+ # user = client.get_user(123)
28
+ #
29
+ # @example Using JSONClient directly
30
+ # client = ApiAdaptor::JSONClient.new(bearer_token: "abc123")
31
+ # response = client.get_json("https://api.example.com/data")
32
+ # puts response["items"]
33
+ #
34
+ # @see Base Base class for building API clients
35
+ # @see JSONClient Low-level HTTP client with JSON support
6
36
  module ApiAdaptor
37
+ # Base error class for all ApiAdaptor exceptions
7
38
  class Error < StandardError; end
8
- # Your code goes here...
9
39
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api_adaptor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Huw Diprose
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-01-14 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: addressable
@@ -24,6 +23,40 @@ dependencies:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
25
  version: '2.8'
26
+ - !ruby/object:Gem::Dependency
27
+ name: base64
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.3'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.3'
40
+ - !ruby/object:Gem::Dependency
41
+ name: bigdecimal
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '3.3'
47
+ - - "<"
48
+ - !ruby/object:Gem::Version
49
+ version: '5.0'
50
+ type: :runtime
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: '3.3'
57
+ - - "<"
58
+ - !ruby/object:Gem::Version
59
+ version: '5.0'
27
60
  - !ruby/object:Gem::Dependency
28
61
  name: link_header
29
62
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +71,26 @@ dependencies:
38
71
  - - "~>"
39
72
  - !ruby/object:Gem::Version
40
73
  version: 0.0.8
74
+ - !ruby/object:Gem::Dependency
75
+ name: logger
76
+ requirement: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '1.6'
81
+ - - "<"
82
+ - !ruby/object:Gem::Version
83
+ version: '2.0'
84
+ type: :runtime
85
+ prerelease: false
86
+ version_requirements: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '1.6'
91
+ - - "<"
92
+ - !ruby/object:Gem::Version
93
+ version: '2.0'
41
94
  - !ruby/object:Gem::Dependency
42
95
  name: rest-client
43
96
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +119,20 @@ dependencies:
66
119
  - - "~>"
67
120
  - !ruby/object:Gem::Version
68
121
  version: '13.0'
122
+ - !ruby/object:Gem::Dependency
123
+ name: redcarpet
124
+ requirement: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - "~>"
127
+ - !ruby/object:Gem::Version
128
+ version: '3.6'
129
+ type: :development
130
+ prerelease: false
131
+ version_requirements: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: '3.6'
69
136
  - !ruby/object:Gem::Dependency
70
137
  name: rspec
71
138
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +161,34 @@ dependencies:
94
161
  - - "~>"
95
162
  - !ruby/object:Gem::Version
96
163
  version: '1.21'
164
+ - !ruby/object:Gem::Dependency
165
+ name: rubocop-yard
166
+ requirement: !ruby/object:Gem::Requirement
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: '0'
171
+ type: :development
172
+ prerelease: false
173
+ version_requirements: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ - !ruby/object:Gem::Dependency
179
+ name: simplecov
180
+ requirement: !ruby/object:Gem::Requirement
181
+ requirements:
182
+ - - "~>"
183
+ - !ruby/object:Gem::Version
184
+ version: '0.22'
185
+ type: :development
186
+ prerelease: false
187
+ version_requirements: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - "~>"
190
+ - !ruby/object:Gem::Version
191
+ version: '0.22'
97
192
  - !ruby/object:Gem::Dependency
98
193
  name: timecop
99
194
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +217,20 @@ dependencies:
122
217
  - - "~>"
123
218
  - !ruby/object:Gem::Version
124
219
  version: '3.18'
220
+ - !ruby/object:Gem::Dependency
221
+ name: yard
222
+ requirement: !ruby/object:Gem::Requirement
223
+ requirements:
224
+ - - "~>"
225
+ - !ruby/object:Gem::Version
226
+ version: '0.9'
227
+ type: :development
228
+ prerelease: false
229
+ version_requirements: !ruby/object:Gem::Requirement
230
+ requirements:
231
+ - - "~>"
232
+ - !ruby/object:Gem::Version
233
+ version: '0.9'
125
234
  description: A basic adaptor to send HTTP requests and parse the responses. Intended
126
235
  to bootstrap the quick writing of Adaptors for specific APIs, without having to
127
236
  write the same old JSON request and processing time and time again.
@@ -134,13 +243,16 @@ files:
134
243
  - ".env.example"
135
244
  - ".rspec"
136
245
  - ".rubocop.yml"
246
+ - ".yardopts"
137
247
  - CHANGELOG.md
248
+ - CLAUDE.md
138
249
  - CODE_OF_CONDUCT.md
139
250
  - Gemfile
140
251
  - Gemfile.lock
141
252
  - LICENSE.txt
142
253
  - README.md
143
254
  - Rakefile
255
+ - fixtures/v1/integration/foo.json
144
256
  - lib/api_adaptor.rb
145
257
  - lib/api_adaptor/base.rb
146
258
  - lib/api_adaptor/exceptions.rb
@@ -160,7 +272,6 @@ metadata:
160
272
  homepage_uri: https://github.com/huwd/api_adaptor
161
273
  source_code_uri: https://github.com/huwd/api_adaptor
162
274
  changelog_uri: https://github.com/huwd/api_adaptor/blob/main/CHANGELOG.md
163
- post_install_message:
164
275
  rdoc_options: []
165
276
  require_paths:
166
277
  - lib
@@ -168,15 +279,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
168
279
  requirements:
169
280
  - - ">="
170
281
  - !ruby/object:Gem::Version
171
- version: 2.6.0
282
+ version: 3.2.0
172
283
  required_rubygems_version: !ruby/object:Gem::Requirement
173
284
  requirements:
174
285
  - - ">="
175
286
  - !ruby/object:Gem::Version
176
287
  version: '0'
177
288
  requirements: []
178
- rubygems_version: 3.4.13
179
- signing_key:
289
+ rubygems_version: 4.0.3
180
290
  specification_version: 4
181
291
  summary: A basic adaptor to send HTTP requests and parse the responses.
182
292
  test_files: []