code-ruby 5.1.0 → 5.2.2
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/format.rb +18 -2
- data/lib/code/object/cryptography.rb +30 -0
- data/lib/code/object/http.rb +8 -3
- data/lib/code/object/smtp.rb +104 -36
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 52e251b96064139d4e72a393de2b727b5d792ff0026d3d5f4830e582b54a7573
|
|
4
|
+
data.tar.gz: 456d6ddc2f99e1d662273af433beaf0461e960be102d829c26a9637af0c29bae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bd013119d2df47feb3c438cee2dccb21e13a01ca319085f7a4dc012966efc02a21578b07ccd104e851f5a0d3c633ac45e07ffea4e7dc4ba34e4fd012fb9bbca8
|
|
7
|
+
data.tar.gz: 057ad7fc9b03bbe40e4c921833e0da2926c0dfb9688b5c63a389cb4e66a687354bebc9591fb95eb8f56f41785fac888d976964c950c88c5532646c164e35c9ed
|
data/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
5.
|
|
1
|
+
5.2.2
|
data/lib/code/format.rb
CHANGED
|
@@ -459,11 +459,12 @@ class Code
|
|
|
459
459
|
extract_string_concatenation_parts(left_operation: operation)
|
|
460
460
|
return format_string(merged_string, indent: indent) if merged_string
|
|
461
461
|
|
|
462
|
-
|
|
462
|
+
first_operator = format_operator(Array(operation[:others]).first&.fetch(:operator, ""))
|
|
463
|
+
expression = format_boolean_operand(operation[:first], first_operator, indent: indent)
|
|
463
464
|
|
|
464
465
|
Array(operation[:others]).each do |other|
|
|
465
|
-
right = format_nested_statement(other[:statement], indent: indent)
|
|
466
466
|
operator = format_operator(other[:operator])
|
|
467
|
+
right = format_boolean_operand(other[:statement], operator, indent: indent)
|
|
467
468
|
|
|
468
469
|
expression =
|
|
469
470
|
if compact_operator?(operator)
|
|
@@ -510,6 +511,21 @@ class Code
|
|
|
510
511
|
expression
|
|
511
512
|
end
|
|
512
513
|
|
|
514
|
+
def format_boolean_operand(statement, operator, indent:)
|
|
515
|
+
if BOOLEAN_WORD_OPERATORS.include?(operator) && statement.is_a?(Hash) &&
|
|
516
|
+
statement[:left_operation]
|
|
517
|
+
nested = Array(statement[:left_operation][:others])
|
|
518
|
+
if nested.any? do |other|
|
|
519
|
+
nested_operator = format_operator(other[:operator])
|
|
520
|
+
BOOLEAN_WORD_OPERATORS.include?(nested_operator) && nested_operator != operator
|
|
521
|
+
end
|
|
522
|
+
return format_group([statement], indent: indent)
|
|
523
|
+
end
|
|
524
|
+
end
|
|
525
|
+
|
|
526
|
+
format_nested_statement(statement, indent: indent)
|
|
527
|
+
end
|
|
528
|
+
|
|
513
529
|
def format_compact_receiver(expression, indent:)
|
|
514
530
|
return expression unless compact_receiver_needs_parentheses?(expression)
|
|
515
531
|
|
|
@@ -13,7 +13,18 @@ class Code
|
|
|
13
13
|
"Cryptography.sha512(:hello, format: :base64)"
|
|
14
14
|
]
|
|
15
15
|
}.freeze
|
|
16
|
+
HMAC_ALGORITHM = "SHA256"
|
|
17
|
+
DEFAULT_FORMAT = "hex"
|
|
16
18
|
CLASS_FUNCTIONS = {
|
|
19
|
+
"hmac_sha256" => {
|
|
20
|
+
name: "hmac_sha256",
|
|
21
|
+
description: "returns an HMAC SHA256 signature as hex, digest, or base64.",
|
|
22
|
+
examples: [
|
|
23
|
+
'Cryptography.hmac_sha256("key", "hello")',
|
|
24
|
+
'Cryptography.hmac_sha256("key", "hello", format: :base64)',
|
|
25
|
+
'Cryptography.hmac_sha256("key", "hello", format: :digest).size'
|
|
26
|
+
]
|
|
27
|
+
},
|
|
17
28
|
"md5" => {
|
|
18
29
|
name: "md5",
|
|
19
30
|
description:
|
|
@@ -78,6 +89,9 @@ class Code
|
|
|
78
89
|
arguments = code_arguments.raw
|
|
79
90
|
|
|
80
91
|
case code_operator.to_s
|
|
92
|
+
when "hmac_sha256"
|
|
93
|
+
sig(args) { [Object, Object, { format: String.maybe }] }
|
|
94
|
+
code_hmac_sha256(*arguments)
|
|
81
95
|
when "md5"
|
|
82
96
|
sig(args) { [Object, { format: String.maybe }] }
|
|
83
97
|
code_md5(*arguments)
|
|
@@ -98,6 +112,22 @@ class Code
|
|
|
98
112
|
end
|
|
99
113
|
end
|
|
100
114
|
|
|
115
|
+
def self.code_hmac_sha256(key, payload, options = nil)
|
|
116
|
+
code_format = (options || {}).to_code.code_get("format")
|
|
117
|
+
format = code_format.nothing? ? DEFAULT_FORMAT : code_format.to_s.downcase
|
|
118
|
+
digest = OpenSSL::HMAC.digest(HMAC_ALGORITHM, key.to_s, payload.to_s)
|
|
119
|
+
case format
|
|
120
|
+
when "hex", "hexdigest"
|
|
121
|
+
digest.unpack1("H*").to_code
|
|
122
|
+
when "digest"
|
|
123
|
+
digest.to_code
|
|
124
|
+
when "base64"
|
|
125
|
+
::Base64.strict_encode64(digest).to_code
|
|
126
|
+
else
|
|
127
|
+
raise Error, "Cryptography: unknown format #{format.inspect}"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
101
131
|
def self.code_md5(*arguments)
|
|
102
132
|
payload = arguments[0]
|
|
103
133
|
options = arguments[1] || Nothing.new
|
data/lib/code/object/http.rb
CHANGED
|
@@ -197,7 +197,8 @@ class Code
|
|
|
197
197
|
not_extended: 510,
|
|
198
198
|
network_authentication_required: 511
|
|
199
199
|
}.freeze
|
|
200
|
-
DEFAULT_TIMEOUT =
|
|
200
|
+
DEFAULT_TIMEOUT = 30
|
|
201
|
+
DEFAULT_OPEN_TIMEOUT = 10
|
|
201
202
|
MAX_HEADER_BYTES = 32.kilobytes
|
|
202
203
|
HEADER_NAME = /\A[A-Za-z0-9!#$%&'*+\-.^_`|~]+\z/
|
|
203
204
|
RESTRICTED_HEADERS = %w[
|
|
@@ -348,7 +349,7 @@ class Code
|
|
|
348
349
|
redirects =
|
|
349
350
|
(
|
|
350
351
|
if options.code_get("redirects").nothing?
|
|
351
|
-
|
|
352
|
+
0
|
|
352
353
|
else
|
|
353
354
|
options.code_get("redirects").raw
|
|
354
355
|
end
|
|
@@ -410,7 +411,11 @@ class Code
|
|
|
410
411
|
http.use_ssl = true if uri.scheme == "https"
|
|
411
412
|
http.max_retries = max_retries
|
|
412
413
|
default_timeout = http_timeout(timeout, DEFAULT_TIMEOUT)
|
|
413
|
-
open_timeout_value =
|
|
414
|
+
open_timeout_value =
|
|
415
|
+
http_timeout(
|
|
416
|
+
open_timeout,
|
|
417
|
+
timeout.nothing? ? DEFAULT_OPEN_TIMEOUT : default_timeout
|
|
418
|
+
)
|
|
414
419
|
read_timeout_value = http_timeout(read_timeout, default_timeout)
|
|
415
420
|
write_timeout_value = http_timeout(write_timeout, default_timeout)
|
|
416
421
|
|
data/lib/code/object/smtp.rb
CHANGED
|
@@ -8,6 +8,38 @@ class Code
|
|
|
8
8
|
description: "stores smtp settings and sends email messages.",
|
|
9
9
|
examples: %w[Smtp Smtp.new Smtp.new.functions.keys.include?(:send)]
|
|
10
10
|
}.freeze
|
|
11
|
+
MESSAGE_ARGUMENTS = {
|
|
12
|
+
from: String.maybe,
|
|
13
|
+
to: String.maybe,
|
|
14
|
+
subject: String.maybe,
|
|
15
|
+
body: String.maybe,
|
|
16
|
+
body_text: String.maybe,
|
|
17
|
+
body_html: String.maybe,
|
|
18
|
+
message_id: String.maybe
|
|
19
|
+
}.freeze
|
|
20
|
+
TEXT_CONTENT_TYPE = "text/plain; charset=UTF-8"
|
|
21
|
+
HTML_CONTENT_TYPE = "text/html; charset=UTF-8"
|
|
22
|
+
MULTIPART_CONTENT_TYPE = "multipart/alternative"
|
|
23
|
+
CLASS_FUNCTIONS = {
|
|
24
|
+
"encode" => {
|
|
25
|
+
name: "encode",
|
|
26
|
+
description: "encodes a message as MIME email.",
|
|
27
|
+
examples: [
|
|
28
|
+
'Smtp.encode({from: "a@example.com", to: "b@example.com", body: "hello"})',
|
|
29
|
+
'Smtp.encode({subject: "hello", body_text: "world"})',
|
|
30
|
+
'Smtp.encode({body_html: "<p>hello</p>"})'
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
"address" => {
|
|
34
|
+
name: "address",
|
|
35
|
+
description: "parses a single email address and display name.",
|
|
36
|
+
examples: [
|
|
37
|
+
'Smtp.address("a@example.com")',
|
|
38
|
+
'Smtp.address("Alice <a@example.com>")',
|
|
39
|
+
'Smtp.address("Bob <b@example.com>")'
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
}.freeze
|
|
11
43
|
INSTANCE_FUNCTIONS = {
|
|
12
44
|
"send" => {
|
|
13
45
|
name: "send",
|
|
@@ -23,7 +55,7 @@ class Code
|
|
|
23
55
|
def self.function_documentation(scope)
|
|
24
56
|
return INSTANCE_FUNCTIONS if scope == :instance
|
|
25
57
|
|
|
26
|
-
|
|
58
|
+
CLASS_FUNCTIONS
|
|
27
59
|
end
|
|
28
60
|
|
|
29
61
|
DEFAULT_PORT = 587
|
|
@@ -31,6 +63,69 @@ class Code
|
|
|
31
63
|
DEFAULT_DOMAIN = "localhost.localdomain"
|
|
32
64
|
ALLOWED_PORTS = [465, 587].freeze
|
|
33
65
|
|
|
66
|
+
def self.call(**args)
|
|
67
|
+
code_operator = args.fetch(:operator, nil).to_code
|
|
68
|
+
code_arguments = args.fetch(:arguments, []).to_code
|
|
69
|
+
|
|
70
|
+
case code_operator.to_s
|
|
71
|
+
when "encode"
|
|
72
|
+
sig(args) { MESSAGE_ARGUMENTS }
|
|
73
|
+
code_encode(code_arguments.code_first)
|
|
74
|
+
when "address"
|
|
75
|
+
sig(args) { String }
|
|
76
|
+
code_address(*code_arguments.raw)
|
|
77
|
+
else
|
|
78
|
+
super
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.code_address(value)
|
|
83
|
+
text = value.to_s
|
|
84
|
+
raise Error, "smtp: invalid address" if text.match?(/[\r\n]/)
|
|
85
|
+
|
|
86
|
+
addresses = Mail::AddressList.new(text).addresses
|
|
87
|
+
raise Error, "smtp: expected one address" unless addresses.length == 1
|
|
88
|
+
|
|
89
|
+
address = addresses.first
|
|
90
|
+
unless address.local.present? && address.domain.present?
|
|
91
|
+
raise Error, "smtp: invalid address"
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
{ email: address.address, name: address.display_name }.to_code
|
|
95
|
+
rescue Mail::Field::ParseError
|
|
96
|
+
raise Error, "smtp: invalid address"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def self.code_encode(message)
|
|
100
|
+
build_mail(message.to_code).encoded.to_code
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def self.build_mail(message)
|
|
104
|
+
mail = Mail.new
|
|
105
|
+
mail.from = message.code_get("from").to_s
|
|
106
|
+
mail.to = message.code_get("to").to_s
|
|
107
|
+
mail.subject = message.code_get("subject").to_s
|
|
108
|
+
message_id = message.code_get("message_id").to_s
|
|
109
|
+
if message_id.match?(/[\r\n]/)
|
|
110
|
+
raise Error, "smtp: invalid message id"
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
mail.message_id = message_id if message_id.present?
|
|
114
|
+
|
|
115
|
+
text_part = Mail::Part.new
|
|
116
|
+
text_part.content_type = TEXT_CONTENT_TYPE
|
|
117
|
+
text_part.body = message.code_get("body_text").to_s.presence || message.code_get("body").to_s
|
|
118
|
+
mail.content_type = MULTIPART_CONTENT_TYPE
|
|
119
|
+
mail.add_part(text_part)
|
|
120
|
+
if message.code_get("body_html").to_s.present?
|
|
121
|
+
html_part = Mail::Part.new
|
|
122
|
+
html_part.content_type = HTML_CONTENT_TYPE
|
|
123
|
+
html_part.body = message.code_get("body_html").to_s
|
|
124
|
+
mail.add_part(html_part)
|
|
125
|
+
end
|
|
126
|
+
mail
|
|
127
|
+
end
|
|
128
|
+
|
|
34
129
|
def call(**args)
|
|
35
130
|
code_operator = args.fetch(:operator, nil).to_code
|
|
36
131
|
code_arguments = args.fetch(:arguments, []).to_code
|
|
@@ -38,16 +133,7 @@ class Code
|
|
|
38
133
|
|
|
39
134
|
case code_operator.to_s
|
|
40
135
|
when "send"
|
|
41
|
-
sig(args)
|
|
42
|
-
{
|
|
43
|
-
from: String.maybe,
|
|
44
|
-
to: String.maybe,
|
|
45
|
-
subject: String.maybe,
|
|
46
|
-
body: String.maybe,
|
|
47
|
-
body_text: String.maybe,
|
|
48
|
-
body_html: String.maybe
|
|
49
|
-
}
|
|
50
|
-
end
|
|
136
|
+
sig(args) { MESSAGE_ARGUMENTS }
|
|
51
137
|
|
|
52
138
|
code_send(
|
|
53
139
|
from: code_value.code_get("from"),
|
|
@@ -55,7 +141,8 @@ class Code
|
|
|
55
141
|
subject: code_value.code_get("subject"),
|
|
56
142
|
body: code_value.code_get("body"),
|
|
57
143
|
body_text: code_value.code_get("body_text"),
|
|
58
|
-
body_html: code_value.code_get("body_html")
|
|
144
|
+
body_html: code_value.code_get("body_html"),
|
|
145
|
+
message_id: code_value.code_get("message_id")
|
|
59
146
|
)
|
|
60
147
|
else
|
|
61
148
|
super
|
|
@@ -68,31 +155,12 @@ class Code
|
|
|
68
155
|
subject: nil,
|
|
69
156
|
body: nil,
|
|
70
157
|
body_text: nil,
|
|
71
|
-
body_html: nil
|
|
158
|
+
body_html: nil,
|
|
159
|
+
message_id: nil
|
|
72
160
|
)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
code_body = body.to_code
|
|
77
|
-
code_body_text = body_text.to_code
|
|
78
|
-
code_body_html = body_html.to_code
|
|
79
|
-
|
|
80
|
-
mail = Mail.new
|
|
81
|
-
mail.from = code_from.to_s
|
|
82
|
-
mail.to = code_to.to_s
|
|
83
|
-
mail.subject = code_subject.to_s
|
|
84
|
-
|
|
85
|
-
text_part = Mail::Part.new
|
|
86
|
-
text_part.content_type = "text/plain; charset=UTF-8"
|
|
87
|
-
text_part.body = code_body_text.to_s.presence || code_body.to_s
|
|
88
|
-
|
|
89
|
-
html_part = Mail::Part.new
|
|
90
|
-
html_part.content_type = "text/html; charset=UTF-8"
|
|
91
|
-
html_part.body = code_body_html.to_s
|
|
92
|
-
|
|
93
|
-
mail.content_type = "multipart/alternative"
|
|
94
|
-
mail.add_part(text_part)
|
|
95
|
-
mail.add_part(html_part) if code_body_html.to_s.present?
|
|
161
|
+
mail = self.class.build_mail(
|
|
162
|
+
{ from:, to:, subject:, body:, body_text:, body_html:, message_id: }.to_code
|
|
163
|
+
)
|
|
96
164
|
|
|
97
165
|
address = code_get("address").to_s
|
|
98
166
|
port = code_get("port").nothing? ? DEFAULT_PORT : code_get("port").to_i
|