http.rb 1.2.0 → 1.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 448e7dce1f8dc9a0b23a659b351d10e241ccf1e3cf853663046ec4d7e6e0c8fe
4
- data.tar.gz: 75123ecd90c4f5333b8c9a6d62435ab20b46623a5871eb8464a224a92c900ed1
3
+ metadata.gz: 06d8c18c5bfecb35e07a63571d9f4a874da28ae9c20a368781e2ecb01f3ebf7f
4
+ data.tar.gz: f2f58d2f70bb3aeaa7b03d7a2ad3c234fe23536a0e53a8dec312b22da8f0f695
5
5
  SHA512:
6
- metadata.gz: 7fadfa5b0e33af922edc397877c6fecdff9fd0e5fdb2c8bbb17b9aaeb33f7cddc61553129e1ae6dafc7ee7367a4e1e0a71fa380119020dafbb50e66873f2333c
7
- data.tar.gz: d5b35c43b60b13b3e34b1b56a5d02189266401ccb3a9d63b9d87ead07330f96efb80e2577391eea8ed0ec2a734050d7b22aa0fb21e50cfe334b53ca700ede7a6
6
+ metadata.gz: 989858d90f01ab5b85678baf5241ff61a1e620a3c338442dd8ba512d11c5de81fa6cdb92abb39431e88d4e4372c9df4cd11883d06d111bc7a7e3bec91c1c8b55
7
+ data.tar.gz: ace86800c9010db5b30b6d6814196ba0d689781757076f0fab6b1b0c0b235d2557e350180010b20e6e0face72d55eb2a2c8c9933ebaed51b9ec2c49a3f4b3fc7
data/CHANGELOG CHANGED
@@ -1,6 +1,35 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 20260609
3
+ ## 20260610
4
+
5
+ 1.3.0: Rename VERBS to METHODS; VERBS retained as a deprecated alias.
6
+
7
+ RFC 9110 calls these "methods", not "verbs". HTTP::VERBS becomes HTTP::METHODS and HTTP::RETRY::VERBS becomes HTTP::RETRY::METHODS, with the old names kept as deprecated aliases (via deprecate_constant) so nothing breaks. The call surface — the named methods, the options hash, HTTP.request, request_with_body/request_without_body — is unchanged. The aliases are scheduled for removal in 2.0.0; that removal is the breaking change the major bump is reserved for.
8
+
9
+ 1. /lib/HTTP/verbs.rb/lib/HTTP/METHODS.rb/: the file is now named for the METHODS module it defines, matching RETRY.rb and VERSION.rb.
10
+ 2. ~ HTTP/METHODS.rb: /VERBS/METHODS/ for the WITHOUT_BODY/WITH_BODY container; + VERBS deprecated alias; /verb/request_method/ in the block variable.
11
+ 3. ~ HTTP.rb: require 'HTTP/METHODS' in place of require 'HTTP/verbs'.
12
+ 4. ~ HTTP/RETRY.rb: /VERBS/METHODS/ for the default retry method list; + VERBS deprecated alias. retry_config now accepts the :retry_methods option (preferred) and still honours :retry_verbs as a backwards-compatible alias; its internal config key is /verbs/methods/.
13
+ 5. ~ HTTP/request.rb: the 307/308 redirect handler now references METHODS::WITH_BODY, and the retry guard reads config[:methods]; /verb/request_method/.
14
+ 6. ~ test/HTTP/RETRY_test.rb: + retry_methods opt-in and retry_methods-over-retry_verbs precedence cases; the option-leak test now covers both :retry_methods and :retry_verbs.
15
+ 7. + test/HTTP/METHODS_test.rb
16
+ 8. ~ README.md: retry_methods documented as the option key, with retry_verbs noted as still accepted; the retry default is shown as HTTP::RETRY::METHODS.
17
+ 9. ~ HTTP::VERSION: /1.2.1/1.3.0/
18
+ 10. ~ CHANGELOG: + 1.3.0 entry
19
+
20
+ ## 20260610
21
+
22
+ 1.2.1: Split the request builders into their own files.
23
+
24
+ request_with_body and request_without_body, added in 1.2.0, each move into their own file, matching the one-method-per-file layout used throughout lib/. Purely internal reorganisation — the methods, their signatures, and their behaviour are unchanged — so it is a patch, not a minor.
25
+
26
+ 1. + lib/HTTP/request_without_body.rb: HTTP.request_without_body, extracted from verbs.rb; requires net/http and the Hash#x_www_form_urlencode extension.
27
+ 2. + lib/HTTP/request_with_body.rb: HTTP.request_with_body, extracted from verbs.rb; requires net/http and json.
28
+ 3. ~ HTTP/verbs.rb: the request builders are extracted; the file now requires the two new files and keeps the VERBS constant and the named-method generation.
29
+ 4. ~ HTTP::VERSION: /1.2.0/1.2.1/
30
+ 5. ~ CHANGELOG: + 1.2.1 entry
31
+
32
+ ## 20260610
4
33
 
5
34
  1.2.0: Extract request_without_body and request_with_body.
6
35
 
data/README.md CHANGED
@@ -110,10 +110,10 @@ HTTP.get('http://example.com', {}, {}, {retries: 3})
110
110
 
111
111
  When enabled, transient network exceptions and retry-worthy HTTP status codes (429, 502, 503, 504) are retried with exponential backoff and jitter. If the response carries a `Retry-After` header, it is honoured in place of the calculated delay.
112
112
 
113
- Only idempotent verbs (`get`, `head`, `options`, `put`, `delete`, `trace`) are retried by default. POST and PATCH are not — retrying a non-idempotent write can create duplicate resources against APIs that don't deduplicate. Opt in per-call via `retry_verbs:`.
113
+ Only idempotent methods (`get`, `head`, `options`, `put`, `delete`, `trace`) are retried by default. POST and PATCH are not — retrying a non-idempotent write can create duplicate resources against APIs that don't deduplicate. Opt in per-call via `retry_methods:` (the older `retry_verbs:` is still accepted).
114
114
 
115
115
  ```ruby
116
- HTTP.post('http://example.com', {a: 1}, {}, {retries: 3, retry_verbs: %i{get post}})
116
+ HTTP.post('http://example.com', {a: 1}, {}, {retries: 3, retry_methods: %i{get post}})
117
117
  ```
118
118
 
119
119
  Configurable options:
@@ -124,7 +124,7 @@ options = {
124
124
  retry_delay: 1.0, # base delay (seconds) for exponential backoff
125
125
  retry_status_codes: [429, 502, 503, 504], # HTTP status codes to retry
126
126
  retry_exceptions: HTTP::RETRY::EXCEPTIONS, # exceptions to retry
127
- retry_verbs: HTTP::RETRY::VERBS # verbs that retry by default
127
+ retry_methods: HTTP::RETRY::METHODS # methods that retry by default (retry_verbs still accepted)
128
128
  }
129
129
  ```
130
130
 
@@ -0,0 +1,29 @@
1
+ # HTTP/METHODS.rb
2
+ # HTTP::METHODS
3
+
4
+ require_relative './request_without_body'
5
+ require_relative './request_with_body'
6
+
7
+ module HTTP
8
+ module METHODS
9
+ WITHOUT_BODY = %i{get delete head options trace}
10
+ WITH_BODY = %i{post put patch}
11
+ end
12
+
13
+ VERBS = METHODS # Deprecated alias for METHODS; to be removed in 2.0.0.
14
+ deprecate_constant :VERBS
15
+
16
+ METHODS::WITHOUT_BODY.each do |request_method|
17
+ define_method(request_method) do |uri, args = {}, headers = {}, options = {}, &block|
18
+ request_without_body(request_method, uri, args, headers, options, &block)
19
+ end
20
+ module_function request_method
21
+ end
22
+
23
+ METHODS::WITH_BODY.each do |request_method|
24
+ define_method(request_method) do |uri, data = {}, headers = {}, options = {}, &block|
25
+ request_with_body(request_method, uri, data, headers, options, &block)
26
+ end
27
+ module_function request_method
28
+ end
29
+ end
data/lib/HTTP/RETRY.rb CHANGED
@@ -19,7 +19,9 @@ module HTTP
19
19
  EOFError
20
20
  ].freeze
21
21
  STATUS_CODES = [429, 502, 503, 504].freeze
22
- VERBS = %i{get head options put delete trace}.freeze
22
+ METHODS = %i{get head options put delete trace}.freeze
23
+ VERBS = METHODS # Deprecated alias for METHODS; to be removed in 2.0.0.
24
+ deprecate_constant :VERBS
23
25
 
24
26
  def self.sleep(seconds)
25
27
  Kernel.sleep(seconds)
@@ -32,7 +34,7 @@ module HTTP
32
34
  delay: options.delete(:retry_delay) || 1.0,
33
35
  status_codes: options.delete(:retry_status_codes) || RETRY::STATUS_CODES,
34
36
  exceptions: options.delete(:retry_exceptions) || RETRY::EXCEPTIONS,
35
- verbs: options.delete(:retry_verbs) || RETRY::VERBS
37
+ methods: [options.delete(:retry_methods), options.delete(:retry_verbs)].compact.first || RETRY::METHODS
36
38
  }
37
39
  end
38
40
  module_function :retry_config
data/lib/HTTP/VERSION.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # HTTP::VERSION
3
3
 
4
4
  module HTTP
5
- VERSION = '1.2.0'
5
+ VERSION = '1.3.0'
6
6
  end
data/lib/HTTP/request.rb CHANGED
@@ -29,9 +29,9 @@ module HTTP
29
29
  elsif uri.user
30
30
  request_object.basic_auth(uri.user, uri.password)
31
31
  end
32
- verb = request_object.method.downcase.to_sym
32
+ request_method = request_object.method.downcase.to_sym
33
33
  response = (
34
- if config[:retries] > 0 && config[:verbs].include?(verb)
34
+ if config[:retries] > 0 && config[:methods].include?(request_method)
35
35
  with_retries(http, request_object, config)
36
36
  else
37
37
  http.request(request_object)
@@ -45,8 +45,8 @@ module HTTP
45
45
  end
46
46
  redirect_uri = uri.merge(response['location'])
47
47
  if response.code =~ /^30[78]$/
48
- data = VERBS::WITH_BODY.include?(verb) ? request_object.body : {}
49
- response = send(verb, redirect_uri.to_s, data, headers, options, &block)
48
+ data = METHODS::WITH_BODY.include?(request_method) ? request_object.body : {}
49
+ response = send(request_method, redirect_uri.to_s, data, headers, options, &block)
50
50
  else
51
51
  response = get(redirect_uri.to_s, {}, {}, options, &block)
52
52
  end
@@ -0,0 +1,25 @@
1
+ # HTTP/request_with_body.rb
2
+ # HTTP.request_with_body
3
+
4
+ require 'json'
5
+ require 'net/http'
6
+
7
+ require_relative './request'
8
+
9
+ module HTTP
10
+ def request_with_body(method, uri, data = {}, headers = {}, options = {}, &block)
11
+ uri = uri.is_a?(URI) ? uri : URI.parse(uri)
12
+ request_object = Net::HTTP.const_get(method.to_s.capitalize).new(uri.request_uri)
13
+ content_type = headers.find{|k, v| k.downcase == 'content-type'}&.last.to_s
14
+ if data.is_a?(String)
15
+ request_object.body = data
16
+ elsif content_type.start_with?('application/json')
17
+ request_object.body = JSON.dump(data)
18
+ else
19
+ request_object.form_data = data
20
+ end
21
+ request(uri, request_object, headers, options, &block)
22
+ end
23
+
24
+ module_function :request_with_body
25
+ end
@@ -0,0 +1,21 @@
1
+ # HTTP/request_without_body.rb
2
+ # HTTP.request_without_body
3
+
4
+ require 'net/http'
5
+
6
+ require_relative '../Hash/x_www_form_urlencode'
7
+ require_relative './request'
8
+
9
+ module HTTP
10
+ def request_without_body(method, uri, args = {}, headers = {}, options = {}, &block)
11
+ uri = uri.is_a?(URI) ? uri : URI.parse(uri)
12
+ request_uri = uri.request_uri
13
+ unless args.empty?
14
+ request_uri += '?' + args.x_www_form_urlencode
15
+ end
16
+ request_object = Net::HTTP.const_get(method.to_s.capitalize).new(request_uri)
17
+ request(uri, request_object, headers, options, &block)
18
+ end
19
+
20
+ module_function :request_without_body
21
+ end
data/lib/HTTP.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  # HTTP.rb
2
2
  # HTTP
3
3
 
4
- require 'HTTP/verbs'
4
+ require 'HTTP/METHODS'
@@ -0,0 +1,39 @@
1
+ # test/HTTP/METHODS_test.rb
2
+
3
+ require_relative '../helper'
4
+
5
+ def without_deprecation_warnings
6
+ original = Warning[:deprecated]
7
+ Warning[:deprecated] = false
8
+ yield
9
+ ensure
10
+ Warning[:deprecated] = original
11
+ end
12
+
13
+ describe "HTTP::METHODS" do
14
+ it "groups the methods sent without a body" do
15
+ _(HTTP::METHODS::WITHOUT_BODY).must_equal(%i{get delete head options trace})
16
+ end
17
+
18
+ it "groups the methods sent with a body" do
19
+ _(HTTP::METHODS::WITH_BODY).must_equal(%i{post put patch})
20
+ end
21
+
22
+ it "remains reachable under the deprecated VERBS name" do
23
+ without_deprecation_warnings do
24
+ _(HTTP::VERBS).must_equal(HTTP::METHODS)
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "HTTP::RETRY::METHODS" do
30
+ it "lists the idempotent methods retried by default" do
31
+ _(HTTP::RETRY::METHODS).must_equal(%i{get head options put delete trace})
32
+ end
33
+
34
+ it "remains reachable under the deprecated VERBS name" do
35
+ without_deprecation_warnings do
36
+ _(HTTP::RETRY::VERBS).must_equal(HTTP::RETRY::METHODS)
37
+ end
38
+ end
39
+ end
@@ -177,6 +177,7 @@ describe "retry behaviour" do
177
177
  retry_delay: 0.1,
178
178
  retry_status_codes: [500],
179
179
  retry_exceptions: [Errno::ECONNRESET],
180
+ retry_methods: %i{get},
180
181
  retry_verbs: %i{get}
181
182
  })
182
183
  end
@@ -184,6 +185,7 @@ describe "retry behaviour" do
184
185
  _(received_opts).wont_include(:retry_delay)
185
186
  _(received_opts).wont_include(:retry_status_codes)
186
187
  _(received_opts).wont_include(:retry_exceptions)
188
+ _(received_opts).wont_include(:retry_methods)
187
189
  _(received_opts).wont_include(:retry_verbs)
188
190
  end
189
191
  end
@@ -240,6 +242,24 @@ describe "retry behaviour" do
240
242
  assert_requested(:post, uri, times: 2)
241
243
  end
242
244
  end
245
+
246
+ it "retries POST when opted in via retry_methods" do
247
+ HTTP::RETRY.stub(:sleep, nil) do
248
+ stub_request(:post, uri).to_return({status: 503}, {status: 200, body: ''})
249
+ response = HTTP.post(uri, {}, {}, {retries: 3, retry_methods: %i{get post}})
250
+ _(response.success?).must_equal(true)
251
+ assert_requested(:post, uri, times: 2)
252
+ end
253
+ end
254
+
255
+ it "prefers retry_methods over retry_verbs when both are given" do
256
+ HTTP::RETRY.stub(:sleep, nil) do
257
+ stub_request(:post, uri).to_return({status: 503}, {status: 200, body: ''})
258
+ response = HTTP.post(uri, {}, {}, {retries: 3, retry_methods: %i{get post}, retry_verbs: %i{get}})
259
+ _(response.success?).must_equal(true)
260
+ assert_requested(:post, uri, times: 2)
261
+ end
262
+ end
243
263
  end
244
264
  end
245
265
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: http.rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran
@@ -94,16 +94,19 @@ files:
94
94
  - Rakefile
95
95
  - http.rb.gemspec
96
96
  - lib/HTTP.rb
97
+ - lib/HTTP/METHODS.rb
97
98
  - lib/HTTP/RETRY.rb
98
99
  - lib/HTTP/VERSION.rb
99
100
  - lib/HTTP/request.rb
100
- - lib/HTTP/verbs.rb
101
+ - lib/HTTP/request_with_body.rb
102
+ - lib/HTTP/request_without_body.rb
101
103
  - lib/Hash/x_www_form_urlencode.rb
102
104
  - lib/Net/HTTP/set_options.rb
103
105
  - lib/Net/HTTPRequest/set_headers.rb
104
106
  - lib/Net/HTTPResponse/StatusPredicates.rb
105
107
  - lib/String/url_encode.rb
106
108
  - lib/URI/Generic/use_sslQ.rb
109
+ - test/HTTP/METHODS_test.rb
107
110
  - test/HTTP/RETRY_test.rb
108
111
  - test/HTTP/basic_auth_test.rb
109
112
  - test/HTTP/delete_test.rb
data/lib/HTTP/verbs.rb DELETED
@@ -1,55 +0,0 @@
1
- # HTTP/verbs.rb
2
- # HTTP.verb
3
-
4
- require 'json'
5
- require 'net/http'
6
-
7
- require_relative '../Hash/x_www_form_urlencode'
8
- require_relative './request'
9
-
10
- module HTTP
11
- module VERBS
12
- WITHOUT_BODY = %i{get delete head options trace}
13
- WITH_BODY = %i{post put patch}
14
- end
15
-
16
- def request_without_body(method, uri, args = {}, headers = {}, options = {}, &block)
17
- uri = uri.is_a?(URI) ? uri : URI.parse(uri)
18
- request_uri = uri.request_uri
19
- unless args.empty?
20
- request_uri += '?' + args.x_www_form_urlencode
21
- end
22
- request_object = Net::HTTP.const_get(method.to_s.capitalize).new(request_uri)
23
- request(uri, request_object, headers, options, &block)
24
- end
25
- module_function :request_without_body
26
-
27
- def request_with_body(method, uri, data = {}, headers = {}, options = {}, &block)
28
- uri = uri.is_a?(URI) ? uri : URI.parse(uri)
29
- request_object = Net::HTTP.const_get(method.to_s.capitalize).new(uri.request_uri)
30
- content_type = headers.find{|k, v| k.downcase == 'content-type'}&.last.to_s
31
- if data.is_a?(String)
32
- request_object.body = data
33
- elsif content_type.start_with?('application/json')
34
- request_object.body = JSON.dump(data)
35
- else
36
- request_object.form_data = data
37
- end
38
- request(uri, request_object, headers, options, &block)
39
- end
40
- module_function :request_with_body
41
-
42
- VERBS::WITHOUT_BODY.each do |verb|
43
- define_method(verb) do |uri, args = {}, headers = {}, options = {}, &block|
44
- request_without_body(verb, uri, args, headers, options, &block)
45
- end
46
- module_function verb
47
- end
48
-
49
- VERBS::WITH_BODY.each do |verb|
50
- define_method(verb) do |uri, data = {}, headers = {}, options = {}, &block|
51
- request_with_body(verb, uri, data, headers, options, &block)
52
- end
53
- module_function verb
54
- end
55
- end