code-ruby 5.0.12 → 5.1.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: 210faa246f2abe16f58a157a6713259359b3977d0ad84c5442b3bb0e8c97ec08
4
- data.tar.gz: 7866c67b465097ee9a7af1280672b7ef7a7a84cfddcb75d4a27efc37be5a6200
3
+ metadata.gz: eec903cbdd5d6368b89062c2b714515f30296bc14da6c56955cfb56883d1c8ec
4
+ data.tar.gz: efff6f91bce16b5bd93295f694be06337a7edbbfd3c882108ffc0f4b1381be17
5
5
  SHA512:
6
- metadata.gz: 9b8ef0e86c48ee64640c4e5240f66ee31f04b84a21eb1fd06f77b33d48c91124310d7a09c4f8dc1923f35ea815852e68fd2451b4b7402700c79c3168e8a3c43c
7
- data.tar.gz: f9a9fc037f7bf34dbd7cc236f05b3f96b46b0e35cf9e76d302c6024a7f31e417b88ee5efa2412e422ae6eeee4f0e97af24b8cd0fee8b85a3d8a710567f6f4fde
6
+ metadata.gz: aaa741df2a2cedebe5db8b763cb0aaac30f5829175baa76a90b4207bbfdb31f2b3eb87cf397eb475ef50e881db24c054e199677f09928f56c359712d5ee83101
7
+ data.tar.gz: 59c8234dfb28efcb3672fbfb23673bfa5f3748edb92618fc0b5bda474ad700cca898da79f705ce750ac22b47539605544bb5c15d9e53287d872f97e77ffd5d32
data/VERSION CHANGED
@@ -1 +1 @@
1
- 5.0.12
1
+ 5.1.0
@@ -107,6 +107,25 @@ class Code
107
107
  years: ::ActiveSupport::Duration::SECONDS_PER_YEAR
108
108
  }.freeze
109
109
  FUNCTIONS = {
110
+ "fail" => {
111
+ name: "fail",
112
+ description: "raises an explicit evaluation error.",
113
+ examples: [
114
+ "fail(\"missing configuration\") rescue nothing",
115
+ "fail(\"invalid response\") rescue :failed",
116
+ "fail(\"unavailable\") rescue \"fallback\""
117
+ ]
118
+ },
119
+ "assert" => {
120
+ name: "assert",
121
+ description:
122
+ "raises the supplied error unless the condition is truthy.",
123
+ examples: [
124
+ "assert(true, \"required\")",
125
+ "assert(1 == 1, \"unexpected\")",
126
+ "assert(false, \"required\") rescue nothing"
127
+ ]
128
+ },
110
129
  "Base64" => {
111
130
  name: "Base64",
112
131
  description:
@@ -493,6 +512,12 @@ class Code
493
512
  end
494
513
 
495
514
  case code_operator.to_s
515
+ when "fail"
516
+ sig(args) { String }
517
+ code_fail(code_value)
518
+ when "assert"
519
+ sig(args) { [Object, String] }
520
+ code_assert(code_value, code_arguments.code_second)
496
521
  when "Boolean"
497
522
  sig(args) { Object.repeat }
498
523
  if code_arguments.any?
@@ -800,6 +825,16 @@ class Code
800
825
  ::Kernel.sleep(duration)
801
826
  Nothing.new
802
827
  end
828
+
829
+ def code_fail(message)
830
+ raise ::Code::Error, message.to_s
831
+ end
832
+
833
+ def code_assert(value, message)
834
+ raise ::Code::Error, message.to_s unless value.to_code.truthy?
835
+
836
+ value.to_code
837
+ end
803
838
  end
804
839
  end
805
840
  end
@@ -94,7 +94,8 @@ class Code
94
94
  },
95
95
  "fetch" => {
96
96
  name: "fetch",
97
- description: "sends an http request using the given method name.",
97
+ description:
98
+ "sends an http request with optional json, redirects, max_retries, retry_delays, retry_unsafe and max_bytes controls.",
98
99
  examples: [
99
100
  "Http.fetch(:get, \"http://httpbin.org/status/200\").success?",
100
101
  "Http.fetch(:post, \"http://httpbin.org/status/200\", body: :hello).request.body",
@@ -121,7 +122,13 @@ class Code
121
122
  timeout: (Integer | Decimal).maybe,
122
123
  open_timeout: (Integer | Decimal).maybe,
123
124
  read_timeout: (Integer | Decimal).maybe,
124
- write_timeout: (Integer | Decimal).maybe
125
+ write_timeout: (Integer | Decimal).maybe,
126
+ redirects: Integer.maybe,
127
+ max_retries: Integer.maybe,
128
+ retry_delays: List.maybe,
129
+ retry_unsafe: Boolean.maybe,
130
+ max_bytes: Integer.maybe,
131
+ json: Object.maybe
125
132
  }
126
133
  ].freeze
127
134
 
@@ -273,16 +280,106 @@ class Code
273
280
  code_fetch("patch", ...)
274
281
  end
275
282
 
276
- def self.code_fetch(*arguments, redirects: 10)
283
+ def self.code_fetch(*arguments, redirects: nil)
284
+ options = arguments.third.to_code
285
+ options = Dictionary.new if options.nothing?
286
+ delays = options.code_get("retry_delays")
287
+ delays = delays.nothing? ? [] : delays.as_json
288
+ unless delays.size <= 5 &&
289
+ delays.all? do |delay|
290
+ delay.is_a?(::Numeric) && delay.finite? &&
291
+ delay.between?(0, 60)
292
+ end
293
+ raise ::Code::Error, "http: invalid retry delays"
294
+ end
295
+
296
+ safe = %w[get head options].include?(arguments.first.to_s.downcase)
297
+ safe ||= options.code_get("retry_unsafe").truthy?
298
+ safe ||=
299
+ (options.code_get("headers").as_json || {}).any? do |key, value|
300
+ key.downcase == "idempotency-key" && value.present?
301
+ end
302
+ delays = [] unless safe
303
+ attempt = 0
304
+ loop do
305
+ begin
306
+ response = code_request(*arguments, redirects: redirects)
307
+ rescue ::Code::Error => e
308
+ unless ["http timeout", "http error"].include?(e.message) &&
309
+ attempt < delays.size
310
+ raise
311
+ end
312
+
313
+ sleep(delays[attempt])
314
+ attempt += 1
315
+ next
316
+ end
317
+ status = response.code_get("code").raw
318
+ unless [408, 429].include?(status) || status.between?(500, 599)
319
+ return response
320
+ end
321
+ return response if attempt >= delays.size
322
+
323
+ retry_after =
324
+ response.code_get("headers").code_get("retry-after").to_s
325
+ delay = delays[attempt]
326
+ if retry_after.match?(/\A\d+\z/)
327
+ delay = [delay, retry_after.to_i].max
328
+ elsif retry_after.present?
329
+ begin
330
+ delay = [delay, ::Time.httpdate(retry_after) - ::Time.now].max
331
+ rescue ::ArgumentError
332
+ delay = delays[attempt]
333
+ end
334
+ end
335
+ return response if delay > 60
336
+
337
+ sleep(delay)
338
+ attempt += 1
339
+ end
340
+ end
341
+
342
+ def self.code_request(*arguments, redirects: nil)
277
343
  verb = arguments.first.to_code.to_s.downcase
278
344
  original_url = arguments.second.to_code.to_s
279
345
  options = arguments.third.to_code
280
346
  options = Dictionary.new if options.nothing?
347
+ if redirects.nil?
348
+ redirects =
349
+ (
350
+ if options.code_get("redirects").nothing?
351
+ 10
352
+ else
353
+ options.code_get("redirects").raw
354
+ end
355
+ )
356
+ end
357
+ max_retries =
358
+ (
359
+ if options.code_get("max_retries").nothing?
360
+ 0
361
+ else
362
+ options.code_get("max_retries").raw
363
+ end
364
+ )
365
+ unless redirects.between?(0, 10) && max_retries.between?(0, 5)
366
+ raise ::Code::Error, "http: invalid request limits"
367
+ end
368
+
281
369
  username = options.code_get("username").to_s
282
370
  password = options.code_get("password").to_s
283
371
  body = options.code_get("body").to_s
284
372
  headers = sanitized_headers(options.code_get("headers").raw || {})
285
373
  data = options.code_get("data").raw || {}
374
+ if options.code_has_key?("json").truthy?
375
+ if options.code_get("body").something? ||
376
+ options.code_get("data").something?
377
+ raise ::Code::Error,
378
+ "http: json cannot be combined with body or data"
379
+ end
380
+ body = ::JSON.generate(options.code_get("json").as_json)
381
+ headers["Content-Type"] = "application/json"
382
+ end
286
383
  timeout = options.code_get("timeout")
287
384
  open_timeout = options.code_get("open_timeout")
288
385
  read_timeout = options.code_get("read_timeout")
@@ -291,7 +388,7 @@ class Code
291
388
  query = query.to_a.flatten.map(&:to_s).each_slice(2).to_h.to_query
292
389
 
293
390
  url = original_url
294
- url = "#{url}?#{query}" if query.present?
391
+ url = "#{url}#{url.include?("?") ? "&" : "?"}#{query}" if query.present?
295
392
 
296
393
  if username.present? || password.present?
297
394
  authorization = ::Base64.strict_encode64("#{username}:#{password}")
@@ -299,12 +396,19 @@ class Code
299
396
  validate_header_size!("Authorization", headers["Authorization"])
300
397
  end
301
398
 
399
+ max_bytes = options.code_get("max_bytes")
400
+ max_bytes = max_bytes.nothing? ? nil : max_bytes.raw
401
+ if max_bytes && !max_bytes.between?(1, 50.megabytes)
402
+ raise ::Code::Error, "http: invalid response size limit"
403
+ end
404
+
302
405
  uri = parse_uri(url)
303
406
  resolved_ip = ::Code::Network.validate_public_uri!(uri, service: "http")
304
407
 
305
408
  http = ::Net::HTTP.new(uri.hostname, uri.port, nil)
306
409
  http.ipaddr = resolved_ip
307
410
  http.use_ssl = true if uri.scheme == "https"
411
+ http.max_retries = max_retries
308
412
  default_timeout = http_timeout(timeout, DEFAULT_TIMEOUT)
309
413
  open_timeout_value = http_timeout(open_timeout, default_timeout)
310
414
  read_timeout_value = http_timeout(read_timeout, default_timeout)
@@ -346,6 +450,11 @@ class Code
346
450
  validate_response_headers!(http_response)
347
451
 
348
452
  http_response.read_body do |chunk|
453
+ if max_bytes &&
454
+ response_body.bytesize + chunk.bytesize > max_bytes
455
+ raise ::Code::Error, "http response is too large"
456
+ end
457
+
349
458
  response_body << chunk
350
459
  end
351
460
  end
@@ -362,16 +471,25 @@ class Code
362
471
  new_uri = parse_uri(::URI.join(uri, location).to_s)
363
472
  ::Code::Network.validate_public_uri!(new_uri, service: "http")
364
473
 
365
- if same_origin?(new_uri, uri)
366
- code_fetch(
367
- "get",
368
- new_uri.to_s,
369
- { username: username, password: password, headers: headers },
370
- redirects: redirects - 1
371
- )
372
- else
373
- code_fetch("get", new_uri.to_s, redirects: redirects - 1)
474
+ redirect_options = options.as_json.except("query")
475
+ redirect_method = [307, 308].include?(code) ? verb : "get"
476
+ unless same_origin?(new_uri, uri)
477
+ unless %w[get head].include?(redirect_method)
478
+ raise ::Code::Error,
479
+ "http: cannot redirect request body to another origin"
480
+ end
481
+ redirect_options =
482
+ redirect_options.except("username", "password", "headers")
374
483
  end
484
+ if redirect_method == "get"
485
+ redirect_options = redirect_options.except("body", "data", "json")
486
+ end
487
+ code_request(
488
+ redirect_method,
489
+ new_uri.to_s,
490
+ redirect_options,
491
+ redirects: redirects - 1
492
+ )
375
493
  else
376
494
  status = STATUS_CODES.key(code) || :ok
377
495
  response_headers = response.each_header.to_h
@@ -379,7 +497,7 @@ class Code
379
497
  request.to_hash.transform_values { |values| List.new(values) }
380
498
  body = response_body.to_s
381
499
 
382
- Dictionary.new(
500
+ HttpResponse.new(
383
501
  :code => code,
384
502
  :status => status,
385
503
  :body => body,
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Code
4
+ class Object
5
+ class HttpResponse < Dictionary
6
+ CLASS_DOCUMENTATION = {
7
+ name: "HttpResponse",
8
+ description: "an HTTP response with JSON and retry status helpers.",
9
+ examples: %w[
10
+ Http.get("http://httpbin.org/status/200").code
11
+ Http.get("http://httpbin.org/status/204").success?
12
+ Http.get("http://httpbin.org/status/500").retryable?
13
+ ]
14
+ }.freeze
15
+ INSTANCE_FUNCTIONS = {
16
+ "json" => {
17
+ name: "json",
18
+ description: "parses the response body or returns nothing.",
19
+ examples: [
20
+ "Http.get(\"http://httpbin.org/status/200\").json",
21
+ "Http.get(\"http://httpbin.org/status/204\").json",
22
+ "Http.get(\"http://httpbin.org/status/500\").json"
23
+ ]
24
+ },
25
+ "json!" => {
26
+ name: "json!",
27
+ description: "parses the response body or raises on malformed JSON.",
28
+ examples: [
29
+ "Http.get(\"http://httpbin.org/status/200\").json! rescue nothing",
30
+ "Http.get(\"http://httpbin.org/status/204\").json! rescue nothing",
31
+ "Http.get(\"http://httpbin.org/status/500\").json! rescue nothing"
32
+ ]
33
+ },
34
+ "retryable?" => {
35
+ name: "retryable?",
36
+ description:
37
+ "reports request timeout rate limit and server error statuses.",
38
+ examples: [
39
+ "Http.get(\"http://httpbin.org/status/408\").retryable?",
40
+ "Http.get(\"http://httpbin.org/status/429\").retryable?",
41
+ "Http.get(\"http://httpbin.org/status/200\").retryable?"
42
+ ]
43
+ }
44
+ }.freeze
45
+
46
+ def self.function_documentation(scope)
47
+ scope == :instance ? INSTANCE_FUNCTIONS : {}
48
+ end
49
+
50
+ def call(**args)
51
+ case args.fetch(:operator, nil).to_s
52
+ when "json"
53
+ sig(args)
54
+ code_json
55
+ when "json!"
56
+ sig(args)
57
+ code_json!
58
+ when "retryable?"
59
+ sig(args)
60
+ code_retryable?
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ def code_json
67
+ Json.code_parse(code_get("body"))
68
+ end
69
+
70
+ def code_json!
71
+ Json.code_parse!(code_get("body"))
72
+ end
73
+
74
+ def code_retryable?
75
+ status = code_get("code").raw
76
+ ([408, 429].include?(status) || status.between?(500, 599)).to_code
77
+ end
78
+ end
79
+ end
80
+ end
@@ -13,6 +13,16 @@ class Code
13
13
  ]
14
14
  }.freeze
15
15
  CLASS_FUNCTIONS = {
16
+ "parse!" => {
17
+ name: "parse!",
18
+ description:
19
+ "parses JSON and raises an evaluation error on invalid input.",
20
+ examples: [
21
+ 'Json.parse!("[1]")',
22
+ 'Json.parse!("true")',
23
+ 'Json.parse!("bad") rescue nothing'
24
+ ]
25
+ },
16
26
  "parse" => {
17
27
  name: "parse",
18
28
  description:
@@ -47,6 +57,9 @@ class Code
47
57
  code_value = code_arguments.code_first
48
58
 
49
59
  case code_operator.to_s
60
+ when "parse!"
61
+ sig(args) { String }
62
+ code_parse!(code_value)
50
63
  when "parse"
51
64
  sig(args) { String }
52
65
  code_parse(code_value)
@@ -75,6 +88,12 @@ class Code
75
88
  Nothing.new
76
89
  end
77
90
 
91
+ def self.code_parse!(value)
92
+ ::JSON.parse(value.to_code.raw).to_code
93
+ rescue ::JSON::ParserError
94
+ raise ::Code::Error, "invalid JSON"
95
+ end
96
+
78
97
  def self.code_generate(value, pretty: nil)
79
98
  value.to_code.code_to_json(pretty: pretty)
80
99
  end
@@ -10,6 +10,16 @@ class Code
10
10
  examples: ["\"hello\"", ":hello.upcase", "\"a,b\".split(\",\")"]
11
11
  }.freeze
12
12
  INSTANCE_FUNCTIONS = {
13
+ "truncate" => {
14
+ name: "truncate",
15
+ description:
16
+ "shortens text to a character limit with an optional omission.",
17
+ examples: [
18
+ "\"abcdef\".truncate(5)",
19
+ "\"abcdef\".truncate(5, omission: \"…\")",
20
+ "\"abc\".truncate(5)"
21
+ ]
22
+ },
13
23
  "&" => {
14
24
  name: "&",
15
25
  description: "returns a function that calls the named selector.",
@@ -593,6 +603,9 @@ class Code
593
603
  code_value = code_arguments.code_first
594
604
 
595
605
  case code_operator.to_s
606
+ when "truncate"
607
+ sig(args) { [Integer, { omission: String.maybe }] }
608
+ code_truncate(code_arguments.code_first, code_arguments.code_second)
596
609
  when "&", "to_function"
597
610
  sig(args)
598
611
  code_to_function(**globals)
@@ -1183,9 +1196,11 @@ class Code
1183
1196
  code_value = value.to_code
1184
1197
 
1185
1198
  List.new(
1186
- raw.scan(code_value.raw).map do |match|
1187
- match.is_a?(::Array) ? List.new(match) : String.new(match)
1188
- end
1199
+ raw
1200
+ .scan(code_value.raw)
1201
+ .map do |match|
1202
+ match.is_a?(::Array) ? List.new(match) : String.new(match)
1203
+ end
1189
1204
  )
1190
1205
  end
1191
1206
 
@@ -1205,6 +1220,17 @@ class Code
1205
1220
  .force_encoding(::Encoding::UTF_8)
1206
1221
  .encode(::Encoding::UTF_8, invalid: :replace, undef: :replace)
1207
1222
  end
1223
+
1224
+ def code_truncate(limit, options = nil)
1225
+ limit = limit.to_code.raw
1226
+ omission = options.to_code.code_get("omission")
1227
+ omission = omission.nothing? ? "..." : omission.to_s
1228
+ if limit < omission.length
1229
+ raise ::Code::Error, "invalid truncation limit"
1230
+ end
1231
+
1232
+ raw.truncate(limit, omission: omission).to_code
1233
+ end
1208
1234
  end
1209
1235
  end
1210
1236
  end
@@ -14,6 +14,15 @@ class Code
14
14
  ]
15
15
  }.freeze
16
16
  CLASS_FUNCTIONS = {
17
+ "join" => {
18
+ name: "join",
19
+ description: "resolves a relative URL against a base URL.",
20
+ examples: [
21
+ "Url.join(\"https://example.com/a/b\", \"../c\")",
22
+ "Url.join(\"https://example.com/a\", \"/b\")",
23
+ "Url.join(\"https://example.com\", \"https://other.example.com\")"
24
+ ]
25
+ },
17
26
  "encode" => {
18
27
  name: "encode",
19
28
  description: "returns url-escaped text for a value.",
@@ -44,8 +53,75 @@ class Code
44
53
  }
45
54
  }.freeze
46
55
 
56
+ INSTANCE_FUNCTIONS = {
57
+ "scheme" => {
58
+ name: "scheme",
59
+ description: "returns the scheme URL component.",
60
+ examples: [
61
+ "Url.parse(\"https://example.com/path?q=1\").scheme",
62
+ "Url.parse(\"https://user:pass@example.com/#fragment\").scheme",
63
+ "Url.parse(\"/relative\").scheme"
64
+ ]
65
+ },
66
+ "host" => {
67
+ name: "host",
68
+ description: "returns the host URL component.",
69
+ examples: [
70
+ "Url.parse(\"https://example.com/path?q=1\").host",
71
+ "Url.parse(\"https://user:pass@example.com/#fragment\").host",
72
+ "Url.parse(\"/relative\").host"
73
+ ]
74
+ },
75
+ "path" => {
76
+ name: "path",
77
+ description: "returns the path URL component.",
78
+ examples: [
79
+ "Url.parse(\"https://example.com/path?q=1\").path",
80
+ "Url.parse(\"https://user:pass@example.com/#fragment\").path",
81
+ "Url.parse(\"/relative\").path"
82
+ ]
83
+ },
84
+ "query" => {
85
+ name: "query",
86
+ description: "returns the query URL component.",
87
+ examples: [
88
+ "Url.parse(\"https://example.com/path?q=1\").query",
89
+ "Url.parse(\"https://user:pass@example.com/#fragment\").query",
90
+ "Url.parse(\"/relative\").query"
91
+ ]
92
+ },
93
+ "fragment" => {
94
+ name: "fragment",
95
+ description: "returns the fragment URL component.",
96
+ examples: [
97
+ "Url.parse(\"https://example.com/path?q=1\").fragment",
98
+ "Url.parse(\"https://user:pass@example.com/#fragment\").fragment",
99
+ "Url.parse(\"/relative\").fragment"
100
+ ]
101
+ },
102
+ "user" => {
103
+ name: "user",
104
+ description: "returns the user URL component.",
105
+ examples: [
106
+ "Url.parse(\"https://example.com/path?q=1\").user",
107
+ "Url.parse(\"https://user:pass@example.com/#fragment\").user",
108
+ "Url.parse(\"/relative\").user"
109
+ ]
110
+ },
111
+ "password" => {
112
+ name: "password",
113
+ description: "returns the password URL component.",
114
+ examples: [
115
+ "Url.parse(\"https://example.com/path?q=1\").password",
116
+ "Url.parse(\"https://user:pass@example.com/#fragment\").password",
117
+ "Url.parse(\"/relative\").password"
118
+ ]
119
+ }
120
+ }.freeze
121
+
47
122
  def self.function_documentation(scope)
48
123
  return CLASS_FUNCTIONS if scope == :class
124
+ return INSTANCE_FUNCTIONS if scope == :instance
49
125
 
50
126
  {}
51
127
  end
@@ -61,6 +137,9 @@ class Code
61
137
  code_arguments = args.fetch(:arguments, []).to_code
62
138
 
63
139
  case code_operator.to_s
140
+ when "join"
141
+ sig(args) { [String, String] }
142
+ code_join(*code_arguments.raw)
64
143
  when "encode"
65
144
  sig(args) { Object.maybe }
66
145
  code_encode(*code_arguments.raw)
@@ -75,6 +154,41 @@ class Code
75
154
  end
76
155
  end
77
156
 
157
+ def self.code_join(base, relative)
158
+ ::URI.join(base.to_s, relative.to_s).to_s.to_code
159
+ rescue ::URI::InvalidURIError, ::URI::BadURIError
160
+ raise ::Code::Error, "invalid URL"
161
+ end
162
+
163
+ def call(**args)
164
+ operator = args.fetch(:operator, nil).to_s
165
+ case operator
166
+ when "scheme"
167
+ sig(args)
168
+ code_scheme
169
+ when "host"
170
+ sig(args)
171
+ code_host
172
+ when "path"
173
+ sig(args)
174
+ code_path
175
+ when "query"
176
+ sig(args)
177
+ code_query
178
+ when "fragment"
179
+ sig(args)
180
+ code_fragment
181
+ when "user"
182
+ sig(args)
183
+ code_user
184
+ when "password"
185
+ sig(args)
186
+ code_password
187
+ else
188
+ super
189
+ end
190
+ end
191
+
78
192
  def self.code_encode(string = nil)
79
193
  code_string = string.to_code
80
194
 
@@ -88,6 +202,34 @@ class Code
88
202
  def self.code_parse(string = nil)
89
203
  new(string)
90
204
  end
205
+
206
+ def code_scheme
207
+ raw.scheme.to_code
208
+ end
209
+
210
+ def code_host
211
+ raw.host.to_code
212
+ end
213
+
214
+ def code_path
215
+ raw.path.to_code
216
+ end
217
+
218
+ def code_query
219
+ raw.query.to_code
220
+ end
221
+
222
+ def code_fragment
223
+ raw.fragment.to_code
224
+ end
225
+
226
+ def code_user
227
+ raw.user.to_code
228
+ end
229
+
230
+ def code_password
231
+ raw.password.to_code
232
+ end
91
233
  end
92
234
  end
93
235
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: code-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.12
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Marié
@@ -368,6 +368,7 @@ files:
368
368
  - lib/code/object/global.rb
369
369
  - lib/code/object/html.rb
370
370
  - lib/code/object/http.rb
371
+ - lib/code/object/http_response.rb
371
372
  - lib/code/object/ics.rb
372
373
  - lib/code/object/identifier_list.rb
373
374
  - lib/code/object/integer.rb
@@ -410,7 +411,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
410
411
  - !ruby/object:Gem::Version
411
412
  version: '0'
412
413
  requirements: []
413
- rubygems_version: 4.0.17
414
+ rubygems_version: 4.0.10
414
415
  specification_version: 4
415
416
  summary: a programming language for the internet
416
417
  test_files: []