uri-smtp 0.4.0 → 0.6.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: 70ec42fc77da96a4bf7e8766406f110edbbd0de008148a7b62ad64b90137515e
4
- data.tar.gz: def1342386b5d4ef7599f09750fd2dea8a709f06c452459c4f174374de4f48de
3
+ metadata.gz: 9aa4d8b92e611c33b96e27f740ed87ecabb90dd69c454828d8246e3c405b9ba5
4
+ data.tar.gz: 476ad95a2ef969f89fb9bf1b56f278686091bb99ad9de8c5ab379e1bcb10aaf6
5
5
  SHA512:
6
- metadata.gz: 1eeb8f2e3f8debbc08271ae35c9a55cadbd2b2d7380a86d174cfa91461012b4986e1e9489c6fde38eedf018d1e5e8601807e6ba23fb6ed1aab3d0a755d82e628
7
- data.tar.gz: d450be99efefaa24c20c2c6315a4d7b0a406d9ba043b558e6742a9bbe510670e0b6d076c5a1584ace1270ce650d73b9fd7549d93dd0853365b938b97713affe4
6
+ metadata.gz: 63b77eee5fd37f45f1f270bf956ab2ff6f5ae4699907e3b86ebf249dc3b4d7228f6ff95bbf7f36aff748b3d80a0d124162808a2a034ea9c721f361eab0f0ee0e
7
+ data.tar.gz: 031a0988bc86f82cfb8dd6998fbe3f188f0957e25bd80845bc80f5f4bc7ff06e33df30bbe1a0e57a77d7614f6cb2979424b974e2bfa40a51ca21c9713a1ba488
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --readme README.md
2
+ --title 'URI::SMTP Documentation'
3
+ --charset utf-8
4
+ --markup markdown
5
+ --markup-provider redcarpet
6
+ 'lib/**/*.rb' - '*.md'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,30 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.6.0] - 2025-07-27
4
+
5
+ - Add: API-docs at https://eval.github.io/uri-smtp/
6
+ - Fix: "smtp+insecure+foo://..." not considered `#insecure?`
7
+ - Fix: "smtp://foo.org?auth=none" being ignored
8
+ - Fix: "smtp+insecure://..." having auth "insecure"
9
+ - Remove: `#starttls?`
10
+
11
+ ## [0.5.0] - 2025-07-25
12
+
13
+ - Add: `uri#read_timeout`, `uri#open_timeout`
14
+ Coerced integers from query:
15
+ ```ruby
16
+ URI("smtp://foo?read_timeout=1").read_timeout #=> 1
17
+ ```
18
+ Included in `to_h`.
19
+ - Add: `uri.decoded_userinfo(format: ...)`
20
+ `:format` can be one of `[:string :array :hash]`
21
+ - Add: domain can appear in fragment
22
+ ```ruby
23
+ URI("smtp://foo.org#sender.org").domain #=> "sender.org"
24
+ ```
25
+ NOTE Any domain from the query takes precedence.
26
+ - Fix: "smtps+foo://..." having no tls
27
+
3
28
  ## [0.4.0] - 2025-07-23
4
29
 
5
30
  - FIX: correct settings for action_mailer using mail v2.8.1
@@ -11,3 +36,4 @@
11
36
  ## [0.2.0] - 2025-07-18
12
37
 
13
38
  - Feature complete
39
+
data/README.md CHANGED
@@ -1,6 +1,25 @@
1
1
  # URI::SMTP
2
2
 
3
- This library allows for parsing SMTP-URIs.
3
+ Extends Ruby's `URI` with support for SMTP-uri's.
4
+ This allows for more concise SMTP-config:
5
+ ```diff
6
+ # config/environments/production.rb
7
+ config.action_mailer.delivery_method = :smtp
8
+ - config.action_mailer.smtp_settings = {
9
+ - address: "smtp.gmail.com",
10
+ - port: 587,
11
+ - domain: "example.com",
12
+ - user_name: Rails.application.credentials.dig(:smtp, :user_name),
13
+ - password: Rails.application.credentials.dig(:smtp, :password),
14
+ - authentication: "plain",
15
+ - enable_starttls: true,
16
+ - open_timeout: 5,
17
+ - read_timeout: 5
18
+ - }
19
+ # given ENV["SMPT_URL"]:
20
+ # "smtp://user_name:password@smtp.gmail.com?open_timeout=5&read_timeout=5#example.com"
21
+ + config.action_mailer.smtp_settings = URI(ENV.fetch("SMTP_URL")).to_h(format: :am)
22
+ ```
4
23
 
5
24
  ## Installation
6
25
 
@@ -21,27 +40,29 @@ gem install uri-smtp
21
40
  ### parse
22
41
 
23
42
  ```ruby
24
- u = URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com?domain=sender.org")
43
+ u = URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com#sender.org")
25
44
 
26
45
  url.scheme #=> "smtps+login"
27
46
  url.auth #=> "login"
28
47
  url.starttls #=> false
48
+ url.starttls? #=> false
29
49
  url.tls? #=> true
30
50
  url.userinfo #=> "user%40gmail.com:p%40ss"
51
+ url.decoded_userinfo #=> "user@gmail.com:p@ss"
31
52
  url.decoded_user #=> "user@gmail.com"
32
53
  url.user #=> "user%40gmail.com"
33
54
  url.decoded_password #=> "p@ss"
34
55
  url.password #=> "p%40ss"
35
56
  url.host #=> "smtp.gmail.com"
36
57
  url.port #=> 465
37
- url.domain #=> s"ender.org"
38
- url.query #=> "domain=sender.org"
58
+ url.domain #=> "sender.org"
39
59
  ```
40
60
 
41
61
  ### to_h
42
62
 
43
63
  ```ruby
44
64
  URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com?domain=sender.org").to_h
65
+ #=>
45
66
  {auth: "login",
46
67
  domain: "sender.org",
47
68
  host: "smtp.gmail.com",
@@ -53,9 +74,10 @@ URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com?domain=sender.org").to
53
74
  password: "p@ss"}
54
75
  ```
55
76
 
56
- Formatting for action_mailer configuration, use `to_h(format: :am)`:
77
+ For [ActionMailer configuration](https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration), use `format: :action_mailer` (or `:am`):
57
78
  ```ruby
58
79
  URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com?domain=sender.org").to_h(format: :am)
80
+ #=>
59
81
  {address: "smtp.gmail.com",
60
82
  authentication: "login",
61
83
  domain: "sender.org",
@@ -65,6 +87,8 @@ URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com?domain=sender.org").to
65
87
  password: "p@ss"}
66
88
  ```
67
89
 
90
+ Besides renaming some keys, this also works around a quirk in `v2.8.1` of the mail-gem (e.g. `tls: false` [skips setting up STARTTLS](https://github.com/mikel/mail/blob/2.8.1/lib/mail/network/delivery_methods/smtp.rb#L115)).
91
+
68
92
 
69
93
  Full Rails config:
70
94
  ```ruby
@@ -89,17 +113,17 @@ There's no official specification for SMTP-URIs. There's some prior work though.
89
113
  SMTP with TLS.
90
114
 
91
115
  > [!NOTE]
92
- > to get `url.starttls #=> :auto`, provide it in the query: `smtp://foo?auth=auto`. In that case `net-smtp` uses STARTTLS when the server supports it (but won't halt like when using `:always`).
116
+ > to get `url.starttls #=> :auto`, provide it in the query: `smtp://user:pw@foo?auth=auto`. In that case `net-smtp` uses STARTTLS when the server supports it (but won't halt like when using `:always`).
93
117
 
94
118
 
95
119
  ### auth
96
120
 
97
- There's no restriction to the value of auth. Though the following values have special meaning:
121
+ Any value for auth that passes the URI-parser is acceptable. Though the following values have special meaning:
98
122
 
99
123
  - `none`
100
124
  No authentication is required.
101
125
  - `plain`
102
- Authenticate with a username and password using AUTH PLAIN. This is the default behavior.
126
+ Authenticate with a username and password using AUTH PLAIN. This is the default behavior when no authentication is provided.
103
127
 
104
128
  > [!NOTE]
105
129
  > any query's value for `auth` takes precedence.
@@ -136,6 +160,7 @@ There's no restriction to the value of auth. Though the following values have sp
136
160
  ## Development
137
161
 
138
162
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
163
+ Use `bin/yard server --reload` when working on documentation.
139
164
 
140
165
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
141
166
 
@@ -4,6 +4,6 @@ require "uri"
4
4
 
5
5
  module URI
6
6
  class SMTP < URI::Generic
7
- VERSION = "0.4.0"
7
+ VERSION = "0.6.0"
8
8
  end
9
9
  end
data/lib/uri/smtp.rb CHANGED
@@ -3,77 +3,226 @@
3
3
  require "uri"
4
4
  require_relative "smtp/version"
5
5
 
6
+ # See https://docs.ruby-lang.org/en/master/URI.html
6
7
  module URI
8
+ # Class that adds smtp(s)-scheme to the standard URI-module.
7
9
  class SMTP < URI::Generic
8
10
  class Error < StandardError; end
9
11
 
10
- def initialize(scheme,
11
- userinfo, host, port, registry,
12
- path, opaque,
13
- query,
14
- fragment,
15
- parser = DEFAULT_PARSER,
16
- arg_check = false)
17
- super
18
- end
19
-
12
+ # @return [Integer]
20
13
  def port
21
14
  return @port if @port
22
15
  return 25 if host_local?
23
16
  return 465 if tls?
17
+
24
18
  587
25
19
  end
26
20
 
21
+ # Return mechanism of authentication (default `"plain"`).
22
+ #
23
+ # Only returns value when {URI::SMTP#userinfo} is provided and authentication is not `"none"`.
24
+ #
25
+ # Authentication can be provided via scheme (e.g. `"smtp+login://..."`) or via
26
+ # query-params (e.g. `"smtp://foo.org?auth=cram-md5"`). The latter takes precedence when both are provided.
27
+ # A provided value of `"none"` results in `nil`. Other values are returned as is.
28
+ # @example
29
+ # # no userinfo
30
+ # URI("smtp://foo.org").auth #=> nil
31
+ #
32
+ # # "none"
33
+ # URI("smtp+none://user@foo.org").auth #=> nil
34
+ #
35
+ # # default value
36
+ # URI("smtp://user@foo.org").auth #=> "plain"
37
+ #
38
+ # # query takes precedence
39
+ # URI("smtp+login://user@foo.org?auth=cram-md5").auth #=> "cram-md5"
40
+ # @return [String, nil] mechanism of authentication or `nil`:
41
+ # @return [nil] when there's no `userinfo`.
42
+ # @return [nil] if 'auth via query' is `"none"`, e.g. `"smtp://foo.org?auth=none"`.
43
+ # @return [String] 'auth via query' when present.
44
+ # @return [nil] if 'auth via scheme' is `"none"`, e.g. `"smtp+none://foo.org"`.
45
+ # @return [String] 'auth via scheme' when present, e.g. `"smtp+login://foo.org"`.
46
+ # @return [String] else `"plain"`
27
47
  def auth
28
48
  # net-smtp: passing authtype without user/pw raises error
29
49
  return nil unless userinfo
50
+ return nil if parsed_query["auth"] == "none"
30
51
  return parsed_query["auth"] if parsed_query.has_key?("auth")
31
52
  return nil if scheme_auth == "none"
32
53
  return scheme_auth if scheme_auth
54
+
33
55
  "plain"
34
56
  end
35
57
 
58
+ # Decoded userinfo formatted as String, Array or Hash.
59
+ #
60
+ # **NOTE** not provided user or password result in `nil` (format: :array) or absent keys (format: :hash).
61
+ #
62
+ # @example no userinfo => `nil`
63
+ # URI("smtp://foo.org").decoded_userinfo #=> nil
64
+ # URI("smtp://foo.org").decoded_userinfo(format: :array) #=> nil
65
+ # URI("smtp://foo.org").decoded_userinfo(format: :hash) #=> nil
66
+ #
67
+ # @example format `:array`
68
+ # # absent user/password is `nil`
69
+ # URI("smtp://user@foo.org").decoded_userinfo(format: :array) #=> ["user", nil]
70
+ # URI("smtp://:pw@foo.org").decoded_userinfo(format: :array) #=> [nil, "pw"]
71
+ # # decoded values
72
+ # URI("smtp://user%40gmail.com:p%40ss@foo.org").decoded_userinfo(format: :array) #=> ["user@gmail.com", "p@ss"]
73
+ #
74
+ # @example format `:hash`
75
+ # # absent user/password is left out
76
+ # URI("smtp://user%40gmail.com@foo.org").decoded_userinfo(format: :hash) #=> {user: "user@gmail.com"}
77
+ # URI("smtp://:p%40ss@foo.org").decoded_userinfo(format: :hash) #=> {password: "p@ss"}
78
+ #
79
+ # @param format [Symbol] the format type, `:string` (default), `:array` or `:hash`.
80
+ # @return [String, Array, Hash] Decoded userinfo formatted as String, Array or Hash.
81
+ def decoded_userinfo(format: :string)
82
+ return if userinfo.nil?
83
+
84
+ case format
85
+ when :string
86
+ [decoded_user, decoded_password].join(":")
87
+ when :array
88
+ [string_presence(decoded_user), string_presence(decoded_password)]
89
+ when :hash
90
+ {
91
+ user: string_presence(decoded_user),
92
+ password: string_presence(decoded_password)
93
+ }.delete_if { |_k, v| v.nil? }
94
+ else
95
+ raise ArgumentError,
96
+ "Unknown format #{format.inspect}. Should be one of #{%i[string array hash].inspect}."
97
+ end
98
+ end
99
+
100
+ # The host to send mail from, i.e. the `HELO` domain.
101
+ # @return [String] the query-key `domain` when present, e.g. `"smtp://foo.org?domain=sender.org"`.
102
+ # @return [String] the `fragment` when present, e.g. `"smtp://foo.org#sender.org"`.
103
+ # @return [nil] otherwise
36
104
  def domain
37
- parsed_query["domain"]
105
+ parsed_query["domain"] || fragment
38
106
  end
39
107
 
40
- def scheme_auth
41
- scheme[/.*(?:\+(.+))/, 1]
108
+ # @return [Integer]
109
+ def read_timeout
110
+ parsed_query["read_timeout"]
42
111
  end
43
112
 
113
+ # @return [Integer]
114
+ def open_timeout
115
+ parsed_query["open_timeout"]
116
+ end
117
+
118
+ # Whether or not to use `STARTTLS`.
119
+ #
120
+ # The possible return values (i.e. `:always`, `:auto` and `false`) map to what {https://github.com/ruby/net-smtp net-smtp} uses:
121
+ # - `:always` use `STARTTLS` or disconnect when server does not support it.
122
+ # - `:auto` use `STARTTLS` when supported, otherwise continue unencrypted.
123
+ # - `false` don't use `STARTTLS`.
124
+ #
125
+ # @return [false] when `tls?`.
126
+ # @return [:always, :auto, false] when query-key `starttls` is present, e.g. `"smtp://foo.org?starttls=auto"`.
127
+ # @return [false] when `host_local?` (the host is considered one for local development).
128
+ # @return [false] when `insecure?` (i.e. `scheme` starts with `"smtp+insecure"`).
129
+ # @return [:always] otherwise.
44
130
  def starttls
45
131
  return false if tls?
46
132
  return parsed_query["starttls"] if parsed_query.has_key?("starttls")
47
133
  return false if host_local?
48
134
  return false if insecure?
135
+
49
136
  :always
50
137
  end
51
- alias_method :starttls?, :starttls
52
138
 
139
+ # @return [Boolean] whether or not `scheme` starts with `"smtps"`.
53
140
  def tls
54
- scheme == "smtps"
141
+ !!scheme[/^smtps/]
55
142
  end
56
143
  alias_method :tls?, :tls
57
144
 
145
+ # Whether or not the scheme indicates to skip STARTTLS.
146
+ #
147
+ # @see #starttls
148
+ #
149
+ # @example
150
+ # URI("smtp+insecure://foo.org").insecure? #=> true
151
+ # # This is equivalent (though shorter and more descriptive) to
152
+ # URI("smtp://foo.org?starttls=false")
153
+ #
154
+ # # combine with authentication
155
+ # URI("smtp+insecure+login://user:pw@foo.org").insecure? #=> true
156
+ # @return [Boolean] whether `scheme` starts with `"smtp+insecure"`.
58
157
  def insecure?
59
- scheme == "smtp+insecure"
158
+ scheme.start_with?("smtp+insecure")
60
159
  end
61
160
 
161
+ # Whether or not `host` is considered local.
162
+ #
163
+ # Hostnames that are considered local have certain defaults (i.e. port `25` and no `STARTTLS`).
164
+ # @example
165
+ # # Point to mailcatcher (https://github.com/sj26/mailcatcher)
166
+ # URI("smtp://127.0.0.1:1025").host_local? #=> true
167
+ #
168
+ # URI("smtp://localhost").host_local? #=> true
169
+ # @return [Boolean] whether or not `host` is considered local.
62
170
  def host_local?
63
171
  %w[127.0.0.1 localhost].include?(host)
64
172
  end
65
173
 
174
+ # `query` as Hash with values `starttls`, `read_timeout` and `open_timeout` coerced.
175
+ # @return [Hash] `query` parsed.
66
176
  def parsed_query
67
- @parsed_query ||= URI.decode_www_form(query.to_s).to_h.tap do
68
- _1["starttls"] &&= case _1["starttls"]
69
- when "always", "auto" then _1["starttls"].to_sym
70
- when "false" then false
71
- else
72
- :always
177
+ @parsed_query ||= URI.decode_www_form(query.to_s).to_h
178
+ .delete_if { |_k, v| !string_presence(v) }
179
+ .tap do
180
+ _1["read_timeout"] &&= _1["read_timeout"].to_i
181
+ _1["open_timeout"] &&= _1["open_timeout"].to_i
182
+ _1["starttls"] &&= case _1["starttls"]
183
+ when "always", "auto" then _1["starttls"].to_sym
184
+ when "false" then false
185
+ else
186
+ :always
187
+ end
73
188
  end
74
- end
75
189
  end
76
190
 
191
+ # Return {Hash} representing the URI.
192
+ #
193
+ # `format` should be one of: `nil` or `:action_mailer` (or `:am`).
194
+ #
195
+ # Format `:action_mailer` matches how {https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration ActionMailer} should be configured and works around some quirks in Mail v2.8.1.
196
+ #
197
+ # **NOTE** keys with nil-values are stripped.
198
+ # @example default format
199
+ # URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com#sender.org").to_h
200
+ # # =>
201
+ # # {auth: "login",
202
+ # # domain: "sender.org",
203
+ # # host: "smtp.gmail.com",
204
+ # # port: 465,
205
+ # # scheme: "smtps+login",
206
+ # # starttls: false,
207
+ # # tls: true,
208
+ # # user: "user@gmail.com",
209
+ # # password: "p@ss"}
210
+ # @example format `:action_mailer`/`:am`, ActionMailer configuration
211
+ # URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com#sender.org").to_h(format: :am)
212
+ # # =>
213
+ # # {address: "smtp.gmail.com",
214
+ # # authentication: "login",
215
+ # # domain: "sender.org",
216
+ # # port: 465,
217
+ # # tls: true,
218
+ # # user_name: "user@gmail.com",
219
+ # # password: "p@ss"}
220
+ # @example Rails configuration
221
+ # # file: config/environments/development.rb
222
+ # # Config via env-var SMTP_URL or fallback to mailcatcher.
223
+ # config.action_mailer.smtp_settings = URI(ENV.fetch("SMTP_URL", "http://127.0.0.1:1025")).to_h(format: :am)
224
+ # @param format [Symbol] the format type, `nil` (default), `:action_mailer`/`:am`.
225
+ # @return [Hash]
77
226
  def to_h(format: nil)
78
227
  case format
79
228
  when :am, :action_mailer
@@ -83,7 +232,9 @@ module URI
83
232
  domain:,
84
233
  enable_starttls: starttls == :always,
85
234
  enable_starttls_auto: starttls == :auto,
235
+ open_timeout:,
86
236
  port:,
237
+ read_timeout:,
87
238
  tls:
88
239
  }.tap do
89
240
  unless _1[:authentication].nil?
@@ -104,7 +255,9 @@ module URI
104
255
  auth:,
105
256
  domain:,
106
257
  host:,
258
+ open_timeout:,
107
259
  port:,
260
+ read_timeout:,
108
261
  scheme:,
109
262
  starttls:,
110
263
  tls:
@@ -117,9 +270,33 @@ module URI
117
270
  end
118
271
  end
119
272
 
273
+ # Parse `uri` and instantiate instance of URI::SMTP.
274
+ # @example
275
+ # URI::SMTP.parse("smtps+plain://user:pw@foo.org#sender.org")
276
+ # #=> #<URI::SMTP smtps+plain://user:pw@foo.org#sender.org>
277
+ # @return [URI::SMTP] URI::SMTP instance from `uri`.
120
278
  def self.parse(uri)
121
279
  new(*URI.split(uri))
122
280
  end
281
+
282
+ private
283
+
284
+ def scheme_auth
285
+ string_absense_in(scheme.split("+").last, %w[smtp smtps insecure])
286
+ end
287
+
288
+ # string_presence("") #=> nil
289
+ # string_presence(" ") #=> nil
290
+ # string_presence(" FOO ") #=> " FOO "
291
+ def string_presence(s)
292
+ s.to_s.strip.then { _1 unless _1.empty? }
293
+ end
294
+
295
+ # string_absense_in("foo", %w[bar baz]) #=> "foo"
296
+ # string_absense_in("bar", %w[bar baz]) #=> nil
297
+ def string_absense_in(s, array)
298
+ s unless array.include?(s)
299
+ end
123
300
  end
124
301
 
125
302
  register_scheme "SMTP", SMTP
@@ -128,6 +305,8 @@ end
128
305
 
129
306
  module UriSmtpExtensions
130
307
  def parse(uri)
308
+ # Ensure 'plus schemes' (e.g., `smtp+login://`, `smtp+oauth://`) are parsed as URI::SMTP
309
+ # instead of URI::Generic objects.
131
310
  if uri.is_a?(String) && uri.start_with?("smtp")
132
311
  return URI::SMTP.parse(uri)
133
312
  end
data/rakelib/yard.rake ADDED
@@ -0,0 +1,12 @@
1
+ require "yard"
2
+
3
+ YARD::Rake::YardocTask.new(:docs) do |t|
4
+ # Options defined in `.yardopts` are read first, then merged with
5
+ # options defined here.
6
+ #
7
+ # It's recommended to define options in `.yardopts` instead of here,
8
+ # as `.yardopts` can be read by external YARD tools, like the
9
+ # hot-reload YARD server `yard server --reload`.
10
+
11
+ # t.options += ['--title', "Something custom"]
12
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uri-smtp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gert Goet
@@ -17,12 +17,14 @@ extra_rdoc_files: []
17
17
  files:
18
18
  - ".rspec"
19
19
  - ".standard.yml"
20
+ - ".yardopts"
20
21
  - CHANGELOG.md
21
22
  - LICENSE.txt
22
23
  - README.md
23
24
  - Rakefile
24
25
  - lib/uri/smtp.rb
25
26
  - lib/uri/smtp/version.rb
27
+ - rakelib/yard.rake
26
28
  - sig/uri/smtp.rbs
27
29
  homepage: https://github.com/eval/uri-smtp
28
30
  licenses:
@@ -31,6 +33,7 @@ metadata:
31
33
  homepage_uri: https://github.com/eval/uri-smtp
32
34
  source_code_uri: https://github.com/eval/uri-smtp
33
35
  changelog_uri: https://github.com/eval/uri-smtp/blob/main/CHANGELOG.md
36
+ documentation_uri: https://eval.github.io/uri-smtp/
34
37
  rdoc_options: []
35
38
  require_paths:
36
39
  - lib