code-ruby 5.1.0 → 5.2.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: eec903cbdd5d6368b89062c2b714515f30296bc14da6c56955cfb56883d1c8ec
4
- data.tar.gz: efff6f91bce16b5bd93295f694be06337a7edbbfd3c882108ffc0f4b1381be17
3
+ metadata.gz: 978d1a5cd5c7828f25ac0d619f50d68dcee1596a9fd3d92ac4e1ba684bfe2481
4
+ data.tar.gz: 06c6cb5d2bf9e3b596d1524ea710dd3ec7f6c8abcd871f07c62f1eb291967c94
5
5
  SHA512:
6
- metadata.gz: aaa741df2a2cedebe5db8b763cb0aaac30f5829175baa76a90b4207bbfdb31f2b3eb87cf397eb475ef50e881db24c054e199677f09928f56c359712d5ee83101
7
- data.tar.gz: 59c8234dfb28efcb3672fbfb23673bfa5f3748edb92618fc0b5bda474ad700cca898da79f705ce750ac22b47539605544bb5c15d9e53287d872f97e77ffd5d32
6
+ metadata.gz: 9c314514dc4d2ca20ebd674fe158b90b1f2200008e9e56389fab38513d8b81a1f21f7bd2dbd85f3bf16fe0d376c6879737524bb7597070a413b767d0b43df5a4
7
+ data.tar.gz: 6ad13fe0f05f93e56fece2befeaf33fb08a10c5f3d2a8186021d57a2e7d6bd7c246615690e78bf950ab25f5453ce9af3b4d43399f6b7e3ea543d7fef83daecf1
data/VERSION CHANGED
@@ -1 +1 @@
1
- 5.1.0
1
+ 5.2.1
@@ -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
@@ -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) do
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
- code_from = from.to_code
74
- code_to = to.to_code
75
- code_subject = subject.to_code
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
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.1.0
4
+ version: 5.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dorian Marié