scorn 0.4.1 → 0.6.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.
Files changed (6) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +32 -0
  4. data/lib/scorn.rb +123 -6
  5. data/scorn.gemspec +0 -1
  6. metadata +1 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: db81b43557f802f076a4b23664f3b5700eb1a0700390b89965db7f587af64a1f
4
- data.tar.gz: c1f31b4118e4a7d2e97cf22fecbaed95837fc20b6afd821cbfc2b629a528b566
3
+ metadata.gz: 5ba08d0daf842995b186b35c7617fb9f76e80789939cb17e2f42bf492e09aa4f
4
+ data.tar.gz: 36bbefc8c53658eed3aee3685088cf57884f52395fe9e3afc22ee33db841c427
5
5
  SHA512:
6
- metadata.gz: 74f488814ae0260f156dbd56cf482eea5ef495671d2b0ab9c62fce6ee87d7380b79c7d1b047b9441e6f6e633c61265d2e431e4464a7d7da46cfa51a1abd5057c
7
- data.tar.gz: 5017773301fc3459c46da16ae3b971b4fd0bf56b62912d4011deaef5c8911e21eba52a80907a5f413275b2bab3337c6f4a76335de50f3ada4b0f3fad61efec6a
6
+ metadata.gz: a7e169aa56ddb9acba9855a8c98ddaf9b6d43f9f2863726c5bb0c30a77702cbdfb61763912ee3661c0bd0435a4189f98efb6b48fe64c246efbcbdd13eb472da3
7
+ data.tar.gz: 1965a0ff5a7c74ba854609929ad38185fb7007e58a9a60fd39de17a774564010a71dae1ffa68eaa1f65a341ce4a4b10f34504add53952cbee6b885a786892f68
data/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
+ ## scorn 0.6.0 released 2026-10-15
6
+
7
+ * Use json:d to post JSON (body not form)
8
+ * Introduce Scorn::Debugger
9
+
10
+
11
+ ## scorn 0.5.0 released 2026-10-10
12
+
13
+ * Accept `x_foobar_header: 'hello'`
14
+ * `r._response._request._headers`
15
+ * Implement :etag/:if_none_match opt
16
+
17
+
5
18
  ## scorn 0.4.1 released 2026-09-02
6
19
 
7
20
  * Tidy dependencies
data/README.md CHANGED
@@ -9,6 +9,7 @@ A stupid HTTP client library.
9
9
  ```ruby
10
10
  r = Scorn.get('https://example.com/data.json')
11
11
 
12
+ p r # => { "data" => [ "foo", "bar" ] }
12
13
  p r._response._c # => 200
13
14
  ```
14
15
 
@@ -36,6 +37,37 @@ p r.class # => Hash
36
37
  p r['form'] # => { 'n' => '-1', 'source' => 'src', 'target' => 'tgt' }
37
38
  ```
38
39
 
40
+ ```ruby
41
+ r = Scorn.post(
42
+ 'https://httpbin.org/post',
43
+ json: { who: 'Doomberg', what: 'buy gold', when: -1 },
44
+ debug: Scorn::Debugger.new)
45
+
46
+ p r['json'] # => { 'who' => 'Doomberg', 'what' => 'buy gold', 'when' => -1 }
47
+
48
+ p r._response._c # => 200
49
+ p r['url'] # => 'https://httpbin.org/post'
50
+ p r['headers']['Accept'] # => 'application/json'
51
+ ```
52
+
53
+ ### Options:
54
+
55
+ * `accept:` for the `Accept` header
56
+ * `uauthorization:` or `auth:` for the `Authorization` header
57
+ * `content_type:` for the `Content-Type` header
58
+ * `etag:` or `if_none_match:` for the `If-None-Match` header
59
+ * `user_agent:` for the `Agent` header
60
+ * `ssl_verify: false`, `ssl_verify: :none`, `verify: false`, as expected
61
+
62
+ ```ruby
63
+ r = Scorn.get(
64
+ 'https://httpbin.org/get',
65
+ auth: 'Bearer toto-nada-xplus',
66
+ json: true)
67
+
68
+ p r['headers']['Authorization'] # => 'Bearer toto-nada-xplus'
69
+ ```
70
+
39
71
 
40
72
  ## LICENSE
41
73
 
data/lib/scorn.rb CHANGED
@@ -1,8 +1,9 @@
1
1
 
2
+ # scorn.rb
3
+
2
4
  require 'json'
3
5
  require 'time'
4
6
  require 'base64'
5
- require 'webrick' # for the http code to message mapping
6
7
  require 'ostruct'
7
8
  require 'openssl'
8
9
  require 'net/http'
@@ -10,7 +11,7 @@ require 'net/http'
10
11
 
11
12
  module Scorn
12
13
 
13
- VERSION = '0.4.1'.freeze
14
+ VERSION = '0.6.0'.freeze
14
15
 
15
16
  class << self
16
17
 
@@ -96,7 +97,22 @@ module Scorn
96
97
  req = make_req(:post, uri, gather_headers(:post, opts))
97
98
 
98
99
  data = opts[:data]
99
- req.set_form_data(data) if data
100
+ json = opts[:json]
101
+
102
+ if data
103
+
104
+ req.set_form_data(data)
105
+
106
+ elsif json.is_a?(Hash) || json.is_a?(Array)
107
+
108
+ req['Content-Type'] = 'application/json'
109
+ req.body = JSON.dump(json)
110
+
111
+ elsif json.is_a?(String)
112
+
113
+ req['Content-Type'] = 'application/json'
114
+ req.body = json
115
+ end
100
116
 
101
117
  req
102
118
  end
@@ -118,8 +134,13 @@ module Scorn
118
134
  opts[:content_type] || 'application/x-www-form-urlencoded' \
119
135
  if verb == :post
120
136
 
137
+ h['If-None-Match'] =
138
+ opts[:etag] || opts[:if_none_match] \
139
+ if verb == :get || verb == :head
140
+
121
141
  opts.each do |k, v|
122
- h[k] = v if k.start_with?(/X-/)
142
+ k = k.to_s.gsub('_', '-')
143
+ h[k] = v if k.start_with?(/x-/i)
123
144
  end
124
145
 
125
146
  h.compact!
@@ -142,6 +163,10 @@ module Scorn
142
163
 
143
164
  headers.each { |k, v| req[k] = v }
144
165
 
166
+ class << req
167
+ def _headers; each_header.inject({}) { |h, (k, v)| h[k] = v; h }; end
168
+ end
169
+
145
170
  req
146
171
  end
147
172
 
@@ -163,7 +188,7 @@ module Scorn
163
188
  res._uri = uri
164
189
  res._request = req
165
190
  res._c = res.code.to_i
166
- res._sta = WEBrick::HTTPStatus.reason_phrase(res.code) rescue ''
191
+ res._sta = STATUS_CODES[res._c] || ''
167
192
  res._elapsed = monow - t0
168
193
  #res._proxy = detail_proxy(http)
169
194
 
@@ -203,11 +228,103 @@ module Scorn
203
228
  else
204
229
  s
205
230
  end
206
- class << r; attr_accessor :_response; end
231
+ class << r; attr_accessor :_response, :_debug; end
207
232
  r._response = res
233
+ r._debug = @opts[:debug]
208
234
 
209
235
  r
210
236
  end
237
+
238
+ # https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv
239
+ #
240
+ STATUS_CODES = {
241
+
242
+ 100 => 'Continue', 101 => 'Switching Protocols', 102 => 'Processing',
243
+
244
+ 200 => 'OK', 201 => 'Created', 202 => 'Accepted',
245
+ 203 => 'Non-Authoritative Information',
246
+ 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content',
247
+ 207 => 'Multi-Status', 208 => 'Already Reported', 226 => 'IM Used',
248
+
249
+ 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found',
250
+ 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy',
251
+ 307 => 'Temporary Redirect', 308 => 'Permanent Redirect',
252
+
253
+ 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required',
254
+ 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed',
255
+ 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required',
256
+ 408 => 'Request Timeout', 409 => 'Conflict', 410 => 'Gone',
257
+ 411 => 'Length Required', 412 => 'Precondition Failed',
258
+ 413 => 'Payload Too Large', 414 => 'URI Too Long',
259
+ 415 => 'Unsupported Media Type', 416 => 'Range Not Satisfiable',
260
+ 417 => 'Expectation Failed', 422 => 'Unprocessable Entity',
261
+ 423 => 'Locked', 424 => 'Failed Dependency', 426 => 'Upgrade Required',
262
+ 428 => 'Precondition Required', 429 => 'Too Many Requests',
263
+ 431 => 'Request Header Fields Too Large',
264
+
265
+ 500 => 'Internal Server Error', 501 => 'Not Implemented',
266
+ 502 => 'Bad Gateway', 503 => 'Service Unavailable',
267
+ 504 => 'Gateway Timeout', 505 => 'HTTP Version Not Supported',
268
+ 506 => 'Variant Also Negotiates', 507 => 'Insufficient Storage',
269
+ 508 => 'Loop Detected', 510 => 'Not Extended',
270
+ 511 => 'Network Authentication Required'
271
+
272
+ }.freeze
273
+ end
274
+
275
+ class Debugger
276
+
277
+ attr_reader :a
278
+
279
+ def initialize; @a = []; end
280
+
281
+ def <<(l); @a << l; end
282
+
283
+ def to_s; @a.join(); end
284
+
285
+ def request_lines
286
+
287
+ @a.find { |s| s.match?(/\A"(GET|POST|HEAD|PUT|DELETE) /) }.split('\r\n')
288
+ end
289
+
290
+ def request_headera
291
+
292
+ request_lines
293
+ .inject([]) { |a, l|
294
+ m = l.match(/\A([-a-zA-Z0-9]+): (.+)\z/)
295
+ a << [ m[1], m[2] ] if m
296
+ a }
297
+ end
298
+
299
+ def response_header_lines
300
+
301
+ @a
302
+ .take_while { |l|
303
+ l != '-> "\\r\\n"\n' }
304
+ .inject([]) { |a, l|
305
+ m = l.match(/\A-> "(.+)\\r\\n"\n\z/)
306
+ a << m[1] if m
307
+ a }
308
+ end
309
+
310
+ def response_headera
311
+
312
+ response_header_lines
313
+ .inject([]) { |a, l|
314
+ m = l.match(/\A([A-Z][-a-zA-Z0-9]*):\s*(.+)\z/)
315
+ a << [ m[1], m[2] ] if m
316
+ a }
317
+ end
318
+
319
+ def request_headerh
320
+
321
+ request_headera.inject({}) { |h, (k, v)| h[k] = v; h }
322
+ end
323
+
324
+ def response_headerh
325
+
326
+ response_headera.inject({}) { |h, (k, v)| h[k] = v; h }
327
+ end
211
328
  end
212
329
  end
213
330
 
data/scorn.gemspec CHANGED
@@ -38,7 +38,6 @@ A stupid HTTP client library. Consider with scorn.
38
38
  ]
39
39
 
40
40
  s.add_runtime_dependency 'base64'
41
- s.add_runtime_dependency 'webrick'
42
41
  s.add_runtime_dependency 'ostruct'
43
42
 
44
43
  s.add_development_dependency 'probatio', '~> 1.6'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scorn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Mettraux
@@ -23,20 +23,6 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
- - !ruby/object:Gem::Dependency
27
- name: webrick
28
- requirement: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: '0'
33
- type: :runtime
34
- prerelease: false
35
- version_requirements: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: '0'
40
26
  - !ruby/object:Gem::Dependency
41
27
  name: ostruct
42
28
  requirement: !ruby/object:Gem::Requirement