http.rb 1.2.1 → 1.3.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: 8e8141a5ebd470216fbd373f5a21fa5b96981be7dc2d6ddb7d8131b220b91ef7
4
- data.tar.gz: 20a7507d9c92457c23f38970f9d04877c527115d298a07a5c3fe0015c88ff627
3
+ metadata.gz: 9c256751d9a000319b5c01326141fab71034662d85cf2393e47170ef73ff3272
4
+ data.tar.gz: e707ce4ff95cab59c75f66a2d29100483da32ad9c3df3e384455be00a202bf2b
5
5
  SHA512:
6
- metadata.gz: 97514595af94da5845f2189b81751838c1c753ee69afaf31cd43d08ab80cfbd623b7da8214e61375a011210ef9dbe330f318e3981b0b5c350f63b12f4526c2a7
7
- data.tar.gz: a8828932d5d72ab5348f44b2f0bff95f14a13653682161963fdb3d518f3734805798abf518bd7fe849f70e838225a9229b989147c94a3808e533e1f5d50836cd
6
+ metadata.gz: f9b47e3129188137bb30018f85cee74b8d2d8f1daa42a00b0aabf68133286416432f53f107ffe5c8370e6184875f29cef3ffdc48fa113a3bdce8ca2a03cbbb8f
7
+ data.tar.gz: 63edff9c0ec8b79d348de5a6a49737a7683fd74958dc2d33ce99433fc170010600e2477957ae355c6426fc8c725aa6b4a6fee2134136292eb2d7be942e70c94a
data/CHANGELOG CHANGED
@@ -1,6 +1,36 @@
1
1
  # CHANGELOG
2
2
 
3
- ## 20260609
3
+ ## 20260713
4
+
5
+ 1.3.1: Use Thoran Namespace for URL-encoding extensions.
6
+
7
+ The String#url_encode and Hash#x_www_form_urlencode extensions reopened the core classes directly. Both now live in named modules under the Thoran:: namespace — Thoran::String::Urlencode and Thoran::Hash::XWwwFormUrlencode — mixed into String and Hash.
8
+
9
+ 1. + Thoran::String::Urlencode#urlencode
10
+ 2. - String#url_encode
11
+ 3. + Thoran::Hash::XWwwFormUrlencode#x_www_form_urlencode
12
+ 4. ~ Hash#x_www_form_urlencode: Requires the Thoran namespaced equivalent.
13
+ 5. ~ HTTP::VERSION: /1.3.0/1.3.1/
14
+ 6. ~ CHANGELOG: + 1.3.1 entry
15
+
16
+ ## 20260610
17
+
18
+ 1.3.0: Rename VERBS to METHODS; VERBS retained as a deprecated alias.
19
+
20
+ 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.
21
+
22
+ 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.
23
+ 2. ~ HTTP/METHODS.rb: /VERBS/METHODS/ for the WITHOUT_BODY/WITH_BODY container; + VERBS deprecated alias; /verb/request_method/ in the block variable.
24
+ 3. ~ HTTP.rb: require 'HTTP/METHODS' in place of require 'HTTP/verbs'.
25
+ 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/.
26
+ 5. ~ HTTP/request.rb: the 307/308 redirect handler now references METHODS::WITH_BODY, and the retry guard reads config[:methods]; /verb/request_method/.
27
+ 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.
28
+ 7. + test/HTTP/METHODS_test.rb
29
+ 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.
30
+ 9. ~ HTTP::VERSION: /1.2.1/1.3.0/
31
+ 10. ~ CHANGELOG: + 1.3.0 entry
32
+
33
+ ## 20260610
4
34
 
5
35
  1.2.1: Split the request builders into their own files.
6
36
 
@@ -12,7 +42,7 @@ request_with_body and request_without_body, added in 1.2.0, each move into their
12
42
  4. ~ HTTP::VERSION: /1.2.0/1.2.1/
13
43
  5. ~ CHANGELOG: + 1.2.1 entry
14
44
 
15
- ## 20260609
45
+ ## 20260610
16
46
 
17
47
  1.2.0: Extract request_without_body and request_with_body.
18
48
 
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.1'
5
+ VERSION = '1.3.1'
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
@@ -1,10 +1,7 @@
1
1
  # Hash/x_www_form_urlencode.rb
2
2
  # Hash#x_www_form_urlencode
3
3
 
4
- require_relative '../String/url_encode'
4
+ # 20260713
5
+ # 0.2.0
5
6
 
6
- class Hash
7
- def x_www_form_urlencode(joiner = '&')
8
- inject([]){|a,e| a << "#{e.first.to_s.url_encode.gsub(/ /, '+')}=#{e.last.to_s.url_encode.gsub(/ /, '+')}" unless e.last.nil?; a}.join(joiner)
9
- end
10
- end
7
+ require 'Thoran/Hash/XWwwFormUrlencode/x_www_form_urlencode'
@@ -0,0 +1,27 @@
1
+ # Thoran/Hash/XWwwFormUrlencode/x_www_form_urlencode.rb
2
+ # Thoran::Hash::XWwwFormUrlencode#x_www_form_urlencode
3
+
4
+ # 20260713
5
+ # 0.3.0
6
+
7
+ # Changes since 0.2:
8
+ # -/0: (The class name and the snake case name really are consistent now.)
9
+ # 1. /XWwwFormUrlEncode/XWwwFormUrlencode/
10
+ # 2. /url_encode/urlencode/
11
+
12
+ require 'Thoran/String/Urlencode/urlencode'
13
+
14
+ module Thoran
15
+ module Hash
16
+ module XWwwFormUrlencode
17
+
18
+ def x_www_form_urlencode(joiner = '&')
19
+ inject([]){|a,e| a << "#{e.first.to_s.urlencode}=#{e.last.to_s.urlencode}" unless e.last.nil?; a}.join(joiner)
20
+ end
21
+ alias_method :x_www_form_url_encode, :x_www_form_urlencode
22
+
23
+ end
24
+ end
25
+ end
26
+
27
+ Hash.send(:include, Thoran::Hash::XWwwFormUrlencode)
@@ -0,0 +1,29 @@
1
+ # Thoran/String/Urlencode/urlencode.rb
2
+ # Thoran::String::Urlencode#urlencode
3
+
4
+ # 20260713
5
+ # 0.4.0
6
+
7
+ # Acknowledgements: I've simply ripped off and refashioned the code from the CGI module!...
8
+
9
+ # Changes since 0.3:
10
+ # -/0: More consistent naming.
11
+ # 1. /UrlEncode/Urlencode/
12
+ # 2. /url_encode/urlencode/
13
+
14
+ module Thoran
15
+ module String
16
+ module Urlencode
17
+
18
+ def urlencode
19
+ self.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
20
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
21
+ end.tr(' ', '+')
22
+ end
23
+ alias_method :url_encode, :urlencode
24
+
25
+ end
26
+ end
27
+ end
28
+
29
+ String.send(:include, Thoran::String::Urlencode)
data/lib/http.rb ADDED
@@ -0,0 +1,4 @@
1
+ # HTTP.rb
2
+ # HTTP
3
+
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.1
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - thoran
@@ -93,19 +93,21 @@ files:
93
93
  - README.md
94
94
  - Rakefile
95
95
  - http.rb.gemspec
96
- - lib/HTTP.rb
96
+ - lib/HTTP/METHODS.rb
97
97
  - lib/HTTP/RETRY.rb
98
98
  - lib/HTTP/VERSION.rb
99
99
  - lib/HTTP/request.rb
100
100
  - lib/HTTP/request_with_body.rb
101
101
  - lib/HTTP/request_without_body.rb
102
- - lib/HTTP/verbs.rb
103
102
  - lib/Hash/x_www_form_urlencode.rb
104
103
  - lib/Net/HTTP/set_options.rb
105
104
  - lib/Net/HTTPRequest/set_headers.rb
106
105
  - lib/Net/HTTPResponse/StatusPredicates.rb
107
- - lib/String/url_encode.rb
106
+ - lib/Thoran/Hash/XWwwFormUrlencode/x_www_form_urlencode.rb
107
+ - lib/Thoran/String/Urlencode/urlencode.rb
108
108
  - lib/URI/Generic/use_sslQ.rb
109
+ - lib/http.rb
110
+ - test/HTTP/METHODS_test.rb
109
111
  - test/HTTP/RETRY_test.rb
110
112
  - test/HTTP/basic_auth_test.rb
111
113
  - test/HTTP/delete_test.rb
@@ -138,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
140
  - !ruby/object:Gem::Version
139
141
  version: '0'
140
142
  requirements: []
141
- rubygems_version: 4.0.13
143
+ rubygems_version: 4.0.16
142
144
  specification_version: 4
143
145
  summary: HTTP made easy.
144
146
  test_files: []
data/lib/HTTP/verbs.rb DELETED
@@ -1,26 +0,0 @@
1
- # HTTP/verbs.rb
2
- # HTTP.verb
3
-
4
- require_relative './request_without_body'
5
- require_relative './request_with_body'
6
-
7
- module HTTP
8
- module VERBS
9
- WITHOUT_BODY = %i{get delete head options trace}
10
- WITH_BODY = %i{post put patch}
11
- end
12
-
13
- VERBS::WITHOUT_BODY.each do |verb|
14
- define_method(verb) do |uri, args = {}, headers = {}, options = {}, &block|
15
- request_without_body(verb, uri, args, headers, options, &block)
16
- end
17
- module_function verb
18
- end
19
-
20
- VERBS::WITH_BODY.each do |verb|
21
- define_method(verb) do |uri, data = {}, headers = {}, options = {}, &block|
22
- request_with_body(verb, uri, data, headers, options, &block)
23
- end
24
- module_function verb
25
- end
26
- end
data/lib/HTTP.rb DELETED
@@ -1,4 +0,0 @@
1
- # HTTP.rb
2
- # HTTP
3
-
4
- require 'HTTP/verbs'
@@ -1,24 +0,0 @@
1
- # String/url_encode.rb
2
- # String#url_encode
3
-
4
- # 20130310
5
- # 0.2.1
6
-
7
- # Description: When I went looking for a means to url encode and decode strings, I wanted something which used extensions to the String class. Of course there are other means to do url encoding and decoding including using the CGI class, but I liked the approach of having this 'built-in' to strings, rather than the string being passed as a parameter as does CGI. Hence this String extender...
8
-
9
- # Acknowledgements: I've simply ripped off and refashioned the code from the CGI module!...
10
-
11
- # Changes since 0.1:
12
- # 1. The url_encode and url_decode methods now are strictly demarcated.
13
- # 0/1
14
- # 2. A small reformat.
15
-
16
- class String
17
-
18
- def url_encode
19
- self.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
20
- '%' + $1.unpack('H2' * $1.size).join('%').upcase
21
- end.tr(' ', '+')
22
- end
23
-
24
- end