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 +4 -4
- data/VERSION +1 -1
- data/lib/code/object/global.rb +35 -0
- data/lib/code/object/http.rb +132 -14
- data/lib/code/object/http_response.rb +80 -0
- data/lib/code/object/json.rb +19 -0
- data/lib/code/object/string.rb +29 -3
- data/lib/code/object/url.rb +142 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eec903cbdd5d6368b89062c2b714515f30296bc14da6c56955cfb56883d1c8ec
|
|
4
|
+
data.tar.gz: efff6f91bce16b5bd93295f694be06337a7edbbfd3c882108ffc0f4b1381be17
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aaa741df2a2cedebe5db8b763cb0aaac30f5829175baa76a90b4207bbfdb31f2b3eb87cf397eb475ef50e881db24c054e199677f09928f56c359712d5ee83101
|
|
7
|
+
data.tar.gz: 59c8234dfb28efcb3672fbfb23673bfa5f3748edb92618fc0b5bda474ad700cca898da79f705ce750ac22b47539605544bb5c15d9e53287d872f97e77ffd5d32
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.0
|
|
1
|
+
5.1.0
|
data/lib/code/object/global.rb
CHANGED
|
@@ -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
|
data/lib/code/object/http.rb
CHANGED
|
@@ -94,7 +94,8 @@ class Code
|
|
|
94
94
|
},
|
|
95
95
|
"fetch" => {
|
|
96
96
|
name: "fetch",
|
|
97
|
-
description:
|
|
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:
|
|
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}
|
|
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
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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
|
-
|
|
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
|
data/lib/code/object/json.rb
CHANGED
|
@@ -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
|
data/lib/code/object/string.rb
CHANGED
|
@@ -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
|
|
1187
|
-
|
|
1188
|
-
|
|
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
|
data/lib/code/object/url.rb
CHANGED
|
@@ -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
|
|
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.
|
|
414
|
+
rubygems_version: 4.0.10
|
|
414
415
|
specification_version: 4
|
|
415
416
|
summary: a programming language for the internet
|
|
416
417
|
test_files: []
|