code-ruby 3.1.2 → 4.0.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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/bin/code +97 -20
  4. data/lib/code/concerns/shared.rb +331 -15
  5. data/lib/code/format.rb +15 -1
  6. data/lib/code/network.rb +87 -0
  7. data/lib/code/node/call.rb +79 -2
  8. data/lib/code/node/call_argument.rb +14 -0
  9. data/lib/code/node/code.rb +5 -4
  10. data/lib/code/node/function_parameter.rb +7 -4
  11. data/lib/code/node/list.rb +29 -1
  12. data/lib/code/object/base_64.rb +132 -6
  13. data/lib/code/object/boolean.rb +60 -0
  14. data/lib/code/object/class.rb +138 -2
  15. data/lib/code/object/code.rb +111 -3
  16. data/lib/code/object/context.rb +57 -1
  17. data/lib/code/object/cryptography.rb +63 -0
  18. data/lib/code/object/date.rb +13339 -462
  19. data/lib/code/object/decimal.rb +1725 -0
  20. data/lib/code/object/dictionary.rb +1790 -11
  21. data/lib/code/object/duration.rb +28 -0
  22. data/lib/code/object/function.rb +261 -23
  23. data/lib/code/object/global.rb +534 -1
  24. data/lib/code/object/html.rb +179 -7
  25. data/lib/code/object/http.rb +244 -14
  26. data/lib/code/object/ics.rb +75 -13
  27. data/lib/code/object/identifier_list.rb +17 -2
  28. data/lib/code/object/integer.rb +1937 -2
  29. data/lib/code/object/json.rb +75 -1
  30. data/lib/code/object/list.rb +3383 -10
  31. data/lib/code/object/nothing.rb +53 -0
  32. data/lib/code/object/number.rb +110 -0
  33. data/lib/code/object/parameter.rb +140 -0
  34. data/lib/code/object/range.rb +576 -14
  35. data/lib/code/object/smtp.rb +95 -12
  36. data/lib/code/object/string.rb +944 -3
  37. data/lib/code/object/super.rb +10 -1
  38. data/lib/code/object/time.rb +13358 -498
  39. data/lib/code/object/url.rb +65 -0
  40. data/lib/code/object.rb +543 -0
  41. data/lib/code/parser.rb +161 -24
  42. data/lib/code-ruby.rb +3 -0
  43. data/lib/code.rb +30 -3
  44. metadata +135 -84
  45. data/.github/dependabot.yml +0 -15
  46. data/.github/workflows/ci.yml +0 -38
  47. data/.gitignore +0 -30
  48. data/.node-version +0 -1
  49. data/.npm-version +0 -1
  50. data/.prettierignore +0 -2
  51. data/.rspec +0 -1
  52. data/.rubocop.yml +0 -140
  53. data/.ruby-version +0 -1
  54. data/.tool-versions +0 -3
  55. data/AGENTS.md +0 -43
  56. data/Gemfile +0 -22
  57. data/Gemfile.lock +0 -292
  58. data/Rakefile +0 -5
  59. data/bin/bundle +0 -123
  60. data/bin/bundle-audit +0 -31
  61. data/bin/bundler-audit +0 -31
  62. data/bin/dorian +0 -31
  63. data/bin/rspec +0 -31
  64. data/bin/rubocop +0 -31
  65. data/bin/test +0 -5
  66. data/code-ruby.gemspec +0 -34
  67. data/docs/precedence.txt +0 -36
  68. data/package-lock.json +0 -14
  69. data/package.json +0 -7
  70. data/spec/bin/code_spec.rb +0 -48
  71. data/spec/code/format_spec.rb +0 -153
  72. data/spec/code/node/call_spec.rb +0 -11
  73. data/spec/code/object/boolean_spec.rb +0 -18
  74. data/spec/code/object/cryptography_spec.rb +0 -25
  75. data/spec/code/object/decimal_spec.rb +0 -50
  76. data/spec/code/object/dictionary_spec.rb +0 -98
  77. data/spec/code/object/function_spec.rb +0 -268
  78. data/spec/code/object/http_spec.rb +0 -33
  79. data/spec/code/object/ics_spec.rb +0 -50
  80. data/spec/code/object/integer_spec.rb +0 -42
  81. data/spec/code/object/list_spec.rb +0 -22
  82. data/spec/code/object/nothing_spec.rb +0 -14
  83. data/spec/code/object/range_spec.rb +0 -23
  84. data/spec/code/object/string_spec.rb +0 -26
  85. data/spec/code/parser/boolean_spec.rb +0 -11
  86. data/spec/code/parser/chained_call_spec.rb +0 -16
  87. data/spec/code/parser/dictionary_spec.rb +0 -18
  88. data/spec/code/parser/function_spec.rb +0 -16
  89. data/spec/code/parser/group_spec.rb +0 -11
  90. data/spec/code/parser/if_modifier_spec.rb +0 -18
  91. data/spec/code/parser/list_spec.rb +0 -17
  92. data/spec/code/parser/number_spec.rb +0 -11
  93. data/spec/code/parser/string_spec.rb +0 -20
  94. data/spec/code/parser_spec.rb +0 -52
  95. data/spec/code/type_spec.rb +0 -21
  96. data/spec/code_spec.rb +0 -717
  97. data/spec/spec_helper.rb +0 -21
  98. data/spec/zeitwerk/loader_spec.rb +0 -7
@@ -3,6 +3,38 @@
3
3
  class Code
4
4
  class Object
5
5
  class Smtp < Dictionary
6
+ CLASS_DOCUMENTATION = {
7
+ name: "Smtp",
8
+ description: "stores smtp settings and sends email messages.",
9
+ examples: [
10
+ "Smtp",
11
+ "Smtp.new",
12
+ "Smtp.new.functions.keys.include?(:send)"
13
+ ]
14
+ }.freeze
15
+ INSTANCE_FUNCTIONS = {
16
+ "send" => {
17
+ name: "send",
18
+ description: "sends an email using the receiver's smtp settings.",
19
+ examples: [
20
+ "Smtp.new.respond_to?(:send)",
21
+ "Smtp.new.functions.keys.include?(:send)",
22
+ "Smtp.new.instance_functions.keys.include?(:send)"
23
+ ]
24
+ }
25
+ }.freeze
26
+
27
+ def self.function_documentation(scope)
28
+ return INSTANCE_FUNCTIONS if scope == :instance
29
+
30
+ {}
31
+ end
32
+
33
+ DEFAULT_PORT = 587
34
+ DEFAULT_TIMEOUT = 10
35
+ DEFAULT_DOMAIN = "localhost.localdomain"
36
+ ALLOWED_PORTS = [465, 587].freeze
37
+
6
38
  def call(**args)
7
39
  code_operator = args.fetch(:operator, nil).to_code
8
40
  code_arguments = args.fetch(:arguments, []).to_code
@@ -66,22 +98,73 @@ class Code
66
98
  mail.add_part(text_part)
67
99
  mail.add_part(html_part) if code_body_html.to_s.present?
68
100
 
69
- mail.delivery_method(
70
- :smtp,
71
- {
72
- address: code_get("address").to_s,
73
- port: code_get("port").to_i,
74
- user_name: code_get("user_name").to_s,
75
- password: code_get("password").to_s,
76
- authentication: code_get("authentication").to_s,
77
- enable_starttls_auto: code_get("enable_starttls_auto").truthy?
78
- }
79
- )
101
+ address = code_get("address").to_s
102
+ port = code_get("port").nothing? ? DEFAULT_PORT : code_get("port").to_i
103
+ authentication = code_get("authentication").to_s
104
+ starttls = code_get("enable_starttls_auto")
105
+ if starttls.something? && starttls.falsy?
106
+ raise Error, "smtp: tls is required"
107
+ end
108
+
109
+ resolved_ip = validate_delivery_target!(address, port)
80
110
 
81
- mail.deliver!
111
+ encoded_message = mail.encoded
112
+ ::Code.ensure_input_size!(encoded_message, label: "smtp message")
113
+
114
+ deliver_mail(
115
+ mail,
116
+ address: address,
117
+ resolved_ip: resolved_ip,
118
+ port: port,
119
+ user_name: code_get("user_name").to_s,
120
+ password: code_get("password").to_s,
121
+ authentication: authentication
122
+ )
82
123
 
83
124
  Nothing.new
84
125
  end
126
+
127
+ private
128
+
129
+ def validate_delivery_target!(address, port)
130
+ resolved_ip = ::Code::Network.validate_public_host!(address, service: "smtp").first
131
+
132
+ return resolved_ip if ALLOWED_PORTS.include?(port)
133
+
134
+ raise Error, "smtp: unsupported port"
135
+ end
136
+
137
+ def deliver_mail(mail, address:, resolved_ip:, port:, user_name:, password:, authentication:)
138
+ envelope = Mail::SmtpEnvelope.new(mail)
139
+ smtp =
140
+ Net::SMTP.new(
141
+ resolved_ip,
142
+ port,
143
+ tls: port == 465,
144
+ starttls: port == 587 ? :always : false,
145
+ tls_verify: true,
146
+ tls_hostname: address,
147
+ ssl_context_params: {
148
+ verify_mode: OpenSSL::SSL::VERIFY_PEER
149
+ }
150
+ )
151
+ smtp.open_timeout = DEFAULT_TIMEOUT
152
+ smtp.read_timeout = DEFAULT_TIMEOUT
153
+
154
+ smtp.start(
155
+ DEFAULT_DOMAIN,
156
+ user_name.presence,
157
+ password.presence,
158
+ authentication.presence
159
+ ) do |connection|
160
+ connection.sendmail(envelope.message, envelope.from, envelope.to)
161
+ end
162
+ rescue Timeout::Error
163
+ raise Error, "smtp timeout"
164
+ rescue OpenSSL::SSL::SSLError, IOError, SystemCallError, SocketError,
165
+ Net::SMTPError
166
+ raise Error, "smtp error"
167
+ end
85
168
  end
86
169
  end
87
170
  end