net-imap 0.4.22 → 0.6.3
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/Gemfile +12 -2
- data/README.md +10 -4
- data/docs/styles.css +75 -14
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +40 -95
- data/lib/net/imap/config/attr_accessors.rb +8 -9
- data/lib/net/imap/config/attr_inheritance.rb +64 -1
- data/lib/net/imap/config/attr_type_coercion.rb +22 -10
- data/lib/net/imap/config/attr_version_defaults.rb +90 -0
- data/lib/net/imap/config.rb +241 -125
- data/lib/net/imap/connection_state.rb +48 -0
- data/lib/net/imap/data_encoding.rb +80 -31
- data/lib/net/imap/deprecated_client_options.rb +6 -3
- data/lib/net/imap/errors.rb +158 -0
- data/lib/net/imap/esearch_result.rb +225 -0
- data/lib/net/imap/fetch_data.rb +126 -47
- data/lib/net/imap/flags.rb +1 -1
- data/lib/net/imap/response_data.rb +123 -187
- data/lib/net/imap/response_parser/parser_utils.rb +19 -23
- data/lib/net/imap/response_parser.rb +182 -38
- data/lib/net/imap/response_reader.rb +10 -12
- data/lib/net/imap/sasl/anonymous_authenticator.rb +3 -3
- data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
- data/lib/net/imap/sasl/authenticators.rb +8 -4
- data/lib/net/imap/sasl/client_adapter.rb +77 -26
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +4 -4
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +218 -56
- data/lib/net/imap/sasl/external_authenticator.rb +2 -2
- data/lib/net/imap/sasl/gs2_header.rb +7 -7
- data/lib/net/imap/sasl/login_authenticator.rb +4 -3
- data/lib/net/imap/sasl/oauthbearer_authenticator.rb +6 -6
- data/lib/net/imap/sasl/plain_authenticator.rb +7 -7
- data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
- data/lib/net/imap/sasl/scram_authenticator.rb +8 -8
- data/lib/net/imap/sasl.rb +7 -4
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/search_result.rb +10 -5
- data/lib/net/imap/sequence_set.rb +1104 -421
- data/lib/net/imap/stringprep/nameprep.rb +1 -1
- data/lib/net/imap/stringprep/trace.rb +4 -4
- data/lib/net/imap/uidplus_data.rb +4 -147
- data/lib/net/imap/vanished_data.rb +65 -0
- data/lib/net/imap.rb +1002 -313
- data/net-imap.gemspec +1 -1
- data/rakelib/rfcs.rake +2 -0
- data/rakelib/string_prep_tables_generator.rb +6 -2
- metadata +7 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4249dc5d175bd3ae3a3b3b79e0f368e674ec96045510a8b504e829feefb54f3d
|
|
4
|
+
data.tar.gz: 6adbee15b0303b36ec1c574991d4bdbf518bcd5621c6b668f05df0bc201ba50a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a20c230ac3977d9acc09bc964badc60acbedaca84246d1ce67787c78443cdea350ade6907e58b61a5c9f09bf3b12e5e57cd319ef82a096c3780f97dc7017ed31
|
|
7
|
+
data.tar.gz: 68ab8b48664998bbf05d5367fea4e5b06e3ba3b2d4e48ba8ae026069060b23f7dafaf7ecf2681fc5aa4ff0f134e55d9399ca51456ff3a7ec718ba829629866b7
|
data/Gemfile
CHANGED
|
@@ -4,13 +4,23 @@ source "https://rubygems.org"
|
|
|
4
4
|
|
|
5
5
|
gemspec
|
|
6
6
|
|
|
7
|
-
gem "digest"
|
|
7
|
+
# gem "digest" # not included as a workaround for #576
|
|
8
8
|
gem "strscan"
|
|
9
9
|
gem "base64"
|
|
10
|
+
gem "psych", ">= 5.3.0" # 5.2.5 for Data serialization, 5.3.0 for TruffleRuby
|
|
10
11
|
|
|
12
|
+
gem "irb"
|
|
11
13
|
gem "rake"
|
|
12
14
|
gem "rdoc"
|
|
13
15
|
gem "test-unit"
|
|
14
16
|
gem "test-unit-ruby-core", git: "https://github.com/ruby/test-unit-ruby-core"
|
|
15
17
|
|
|
16
|
-
gem "benchmark
|
|
18
|
+
gem "benchmark", require: false
|
|
19
|
+
gem "benchmark-driver", require: false
|
|
20
|
+
gem "vernier", require: false, platform: :mri
|
|
21
|
+
|
|
22
|
+
group :test do
|
|
23
|
+
gem "simplecov", require: false, platforms: %i[mri windows]
|
|
24
|
+
gem "simplecov-html", require: false, platforms: %i[mri windows]
|
|
25
|
+
gem "simplecov-json", require: false, platforms: %i[mri windows]
|
|
26
|
+
end
|
data/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# Net::IMAP
|
|
2
2
|
|
|
3
3
|
Net::IMAP implements Internet Message Access Protocol (IMAP) client
|
|
4
|
-
functionality. The protocol is described in
|
|
4
|
+
functionality. The protocol is described in
|
|
5
|
+
[RFC3501](https://www.rfc-editor.org/rfc/rfc3501),
|
|
6
|
+
[RFC9051](https://www.rfc-editor.org/rfc/rfc9051) and various extensions.
|
|
5
7
|
|
|
6
8
|
## Installation
|
|
7
9
|
|
|
@@ -50,12 +52,16 @@ end
|
|
|
50
52
|
|
|
51
53
|
```ruby
|
|
52
54
|
imap.select('Mail/sent-mail')
|
|
53
|
-
if
|
|
55
|
+
if imap.list('Mail/', 'sent-apr03').empty?
|
|
54
56
|
imap.create('Mail/sent-apr03')
|
|
55
57
|
end
|
|
56
58
|
imap.search(["BEFORE", "30-Apr-2003", "SINCE", "1-Apr-2003"]).each do |message_id|
|
|
57
|
-
imap.
|
|
58
|
-
|
|
59
|
+
if imap.capable?(:move) || imap.capable?(:IMAP4rev2)
|
|
60
|
+
imap.move(message_id, "Mail/sent-apr03")
|
|
61
|
+
else
|
|
62
|
+
imap.copy(message_id, "Mail/sent-apr03")
|
|
63
|
+
imap.store(message_id, "+FLAGS", [:Deleted])
|
|
64
|
+
end
|
|
59
65
|
end
|
|
60
66
|
imap.expunge
|
|
61
67
|
```
|
data/docs/styles.css
CHANGED
|
@@ -1,24 +1,85 @@
|
|
|
1
1
|
/* this is a work in progress. :) */
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
/***********************************************
|
|
4
|
+
* Method descriptions
|
|
5
|
+
***********************************************/
|
|
6
|
+
|
|
7
|
+
main .method-detail {
|
|
8
|
+
display: grid;
|
|
9
|
+
grid-template-columns: 1fr auto;
|
|
10
|
+
justify-content: space-between;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
main .method-header,
|
|
14
|
+
main .method-controls,
|
|
15
|
+
.attribute-method-heading {
|
|
6
16
|
padding: 0.5em;
|
|
7
|
-
border
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
17
|
+
/* border: 1px solid var(--highlight-color); */
|
|
18
|
+
background: var(--table-header-background-color);
|
|
19
|
+
line-height: 1.6;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.attribute-method-heading .attribute-access-type {
|
|
23
|
+
float: right;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
main .method-header {
|
|
27
|
+
border-right: none;
|
|
28
|
+
border-radius: 4px 0 0 4px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
main .method-heading :any-link {
|
|
32
|
+
text-decoration: none;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
main .method-controls {
|
|
36
|
+
border-left: none;
|
|
37
|
+
border-radius: 0 4px 4px 0;
|
|
12
38
|
}
|
|
13
39
|
|
|
14
40
|
main .method-description, main .aliases {
|
|
41
|
+
grid-column: 1 / span 2;
|
|
15
42
|
padding-left: 1em;
|
|
16
43
|
}
|
|
17
44
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
45
|
+
@media (max-width: 700px) {
|
|
46
|
+
main .method-header, main .method-controls, main .method-description {
|
|
47
|
+
grid-column: 1 / span 2;
|
|
48
|
+
margin: 0;
|
|
49
|
+
}
|
|
50
|
+
main .method-controls {
|
|
51
|
+
background: none;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/***********************************************
|
|
56
|
+
* Description lists
|
|
57
|
+
***********************************************/
|
|
58
|
+
|
|
59
|
+
main dt {
|
|
60
|
+
margin-bottom: 0; /* override rdoc 6.8 */
|
|
61
|
+
float: unset; /* override rdoc 6.8 */
|
|
62
|
+
line-height: 1.5; /* matches `main p` */
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
main dl.note-list dt {
|
|
66
|
+
margin-right: 1em;
|
|
67
|
+
float: left;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
main dl.note-list dt:has(+ dt) {
|
|
71
|
+
margin-right: 0.25em;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
main dl.note-list dt:has(+ dt)::after {
|
|
75
|
+
content: ', ';
|
|
76
|
+
font-weight: normal;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
main dd {
|
|
80
|
+
margin: 0 0 1em 1em;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
main dd p:first-child {
|
|
84
|
+
margin-top: 0;
|
|
24
85
|
}
|
|
@@ -9,7 +9,7 @@ module Net::IMAP::Authenticators
|
|
|
9
9
|
"%s.%s is deprecated. Use %s.%s instead." % [
|
|
10
10
|
Net::IMAP, __method__, Net::IMAP::SASL, __method__
|
|
11
11
|
],
|
|
12
|
-
uplevel: 1
|
|
12
|
+
uplevel: 1, category: :deprecated
|
|
13
13
|
)
|
|
14
14
|
Net::IMAP::SASL.add_authenticator(...)
|
|
15
15
|
end
|
|
@@ -20,7 +20,7 @@ module Net::IMAP::Authenticators
|
|
|
20
20
|
"%s.%s is deprecated. Use %s.%s instead." % [
|
|
21
21
|
Net::IMAP, __method__, Net::IMAP::SASL, __method__
|
|
22
22
|
],
|
|
23
|
-
uplevel: 1
|
|
23
|
+
uplevel: 1, category: :deprecated
|
|
24
24
|
)
|
|
25
25
|
Net::IMAP::SASL.authenticator(...)
|
|
26
26
|
end
|
|
@@ -119,138 +119,83 @@ module Net
|
|
|
119
119
|
put_string("\\" + symbol.to_s)
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
CommandData = Data.define(:data) do # :nodoc:
|
|
123
123
|
def send_data(imap, tag)
|
|
124
|
-
|
|
124
|
+
raise NoMethodError, "#{self.class} must implement #{__method__}"
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
def validate
|
|
128
128
|
end
|
|
129
|
-
|
|
130
|
-
private
|
|
131
|
-
|
|
132
|
-
def initialize(data)
|
|
133
|
-
@data = data
|
|
134
|
-
end
|
|
135
129
|
end
|
|
136
130
|
|
|
137
|
-
class
|
|
131
|
+
class RawData < CommandData # :nodoc:
|
|
138
132
|
def send_data(imap, tag)
|
|
139
|
-
imap.__send__(:put_string,
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def validate
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
private
|
|
146
|
-
|
|
147
|
-
def initialize(data)
|
|
148
|
-
@data = data
|
|
133
|
+
imap.__send__(:put_string, data)
|
|
149
134
|
end
|
|
150
135
|
end
|
|
151
136
|
|
|
152
|
-
class
|
|
137
|
+
class Atom < CommandData # :nodoc:
|
|
153
138
|
def send_data(imap, tag)
|
|
154
|
-
imap.__send__(:
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def validate
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
private
|
|
161
|
-
|
|
162
|
-
def initialize(data)
|
|
163
|
-
@data = data
|
|
139
|
+
imap.__send__(:put_string, data)
|
|
164
140
|
end
|
|
165
141
|
end
|
|
166
142
|
|
|
167
|
-
class
|
|
143
|
+
class QuotedString < CommandData # :nodoc:
|
|
168
144
|
def send_data(imap, tag)
|
|
169
|
-
imap.__send__(:
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
def validate
|
|
173
|
-
end
|
|
174
|
-
|
|
175
|
-
private
|
|
176
|
-
|
|
177
|
-
def initialize(data)
|
|
178
|
-
@data = data
|
|
145
|
+
imap.__send__(:send_quoted_string, data)
|
|
179
146
|
end
|
|
180
147
|
end
|
|
181
148
|
|
|
182
|
-
class
|
|
149
|
+
class Literal < CommandData # :nodoc:
|
|
183
150
|
def send_data(imap, tag)
|
|
184
|
-
imap.__send__(:
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
def validate
|
|
188
|
-
validate_internal(@data)
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
private
|
|
192
|
-
|
|
193
|
-
def initialize(data)
|
|
194
|
-
@data = data
|
|
151
|
+
imap.__send__(:send_literal, data, tag)
|
|
195
152
|
end
|
|
153
|
+
end
|
|
196
154
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
155
|
+
class PartialRange < CommandData # :nodoc:
|
|
156
|
+
uint32_max = 2**32 - 1
|
|
157
|
+
POS_RANGE = 1..uint32_max
|
|
158
|
+
NEG_RANGE = -uint32_max..-1
|
|
159
|
+
Positive = ->{ (_1 in Range) and POS_RANGE.cover?(_1) }
|
|
160
|
+
Negative = ->{ (_1 in Range) and NEG_RANGE.cover?(_1) }
|
|
161
|
+
|
|
162
|
+
def initialize(data:)
|
|
163
|
+
min, max = case data
|
|
164
|
+
in Range
|
|
165
|
+
data.minmax.map { Integer _1 }
|
|
166
|
+
in ResponseParser::Patterns::PARTIAL_RANGE
|
|
167
|
+
data.split(":").map { Integer _1 }.minmax
|
|
168
|
+
else
|
|
169
|
+
raise ArgumentError, "invalid partial range input: %p" % [data]
|
|
170
|
+
end
|
|
171
|
+
data = min..max
|
|
172
|
+
unless data in Positive | Negative
|
|
173
|
+
raise ArgumentError, "invalid partial-range: %p" % [data]
|
|
215
174
|
end
|
|
175
|
+
super
|
|
176
|
+
rescue TypeError, RangeError
|
|
177
|
+
raise ArgumentError, "expected range min/max to be Integers"
|
|
216
178
|
end
|
|
217
179
|
|
|
218
|
-
def
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
NumValidator.ensure_nz_number(data)
|
|
223
|
-
when Range
|
|
224
|
-
when Array
|
|
225
|
-
data.each do |i|
|
|
226
|
-
validate_internal(i)
|
|
227
|
-
end
|
|
228
|
-
when ThreadMember
|
|
229
|
-
data.children.each do |i|
|
|
230
|
-
validate_internal(i)
|
|
231
|
-
end
|
|
232
|
-
else
|
|
233
|
-
raise DataFormatError, data.inspect
|
|
234
|
-
end
|
|
180
|
+
def formatted = "%d:%d" % data.minmax
|
|
181
|
+
|
|
182
|
+
def send_data(imap, tag)
|
|
183
|
+
imap.__send__(:put_string, formatted)
|
|
235
184
|
end
|
|
236
185
|
end
|
|
237
186
|
|
|
238
|
-
class ClientID # :nodoc:
|
|
187
|
+
class ClientID < CommandData # :nodoc:
|
|
239
188
|
|
|
240
189
|
def send_data(imap, tag)
|
|
241
|
-
imap.__send__(:send_data, format_internal(
|
|
190
|
+
imap.__send__(:send_data, format_internal(data), tag)
|
|
242
191
|
end
|
|
243
192
|
|
|
244
193
|
def validate
|
|
245
|
-
validate_internal(
|
|
194
|
+
validate_internal(data)
|
|
246
195
|
end
|
|
247
196
|
|
|
248
197
|
private
|
|
249
198
|
|
|
250
|
-
def initialize(data)
|
|
251
|
-
@data = data
|
|
252
|
-
end
|
|
253
|
-
|
|
254
199
|
def validate_internal(client_id)
|
|
255
200
|
client_id.to_h.each do |k,v|
|
|
256
201
|
unless StringFormatter.valid_string?(k)
|
|
@@ -27,24 +27,23 @@ module Net
|
|
|
27
27
|
|
|
28
28
|
def self.attr_accessor(name) # :nodoc: internal API
|
|
29
29
|
name = name.to_sym
|
|
30
|
+
raise ArgumentError, "already defined #{name}" if attributes.include?(name)
|
|
31
|
+
attributes << name
|
|
30
32
|
def_delegators :data, name, :"#{name}="
|
|
31
33
|
end
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
private_class_method :attributes
|
|
35
|
+
# An array of Config attribute names
|
|
36
|
+
singleton_class.attr_reader :attributes
|
|
37
|
+
@attributes = []
|
|
37
38
|
|
|
38
39
|
def self.struct # :nodoc: internal API
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
end
|
|
42
|
-
self::Struct
|
|
40
|
+
attributes.freeze
|
|
41
|
+
Struct.new(*attributes)
|
|
43
42
|
end
|
|
44
43
|
|
|
45
44
|
def initialize # :notnew:
|
|
46
45
|
super()
|
|
47
|
-
@data =
|
|
46
|
+
@data = Config::Struct.new
|
|
48
47
|
end
|
|
49
48
|
|
|
50
49
|
# Freezes the internal attributes struct, in addition to +self+.
|
|
@@ -54,9 +54,72 @@ module Net
|
|
|
54
54
|
# Creates a new config, which inherits from +self+.
|
|
55
55
|
def new(**attrs) self.class.new(self, **attrs) end
|
|
56
56
|
|
|
57
|
+
# :call-seq:
|
|
58
|
+
# inherited?(attr) -> true or false
|
|
59
|
+
# inherited?(*attrs) -> true or false
|
|
60
|
+
# inherited? -> true or false
|
|
61
|
+
#
|
|
57
62
|
# Returns +true+ if +attr+ is inherited from #parent and not overridden
|
|
58
63
|
# by this config.
|
|
59
|
-
|
|
64
|
+
#
|
|
65
|
+
# When multiple +attrs+ are given, returns +true+ if *all* of them are
|
|
66
|
+
# inherited, or +false+ if any of them are overriden. When no +attrs+
|
|
67
|
+
# are given, returns +true+ if *all* attributes are inherited, or
|
|
68
|
+
# +false+ if any attribute is overriden.
|
|
69
|
+
#
|
|
70
|
+
# Related: #overrides?
|
|
71
|
+
def inherited?(*attrs)
|
|
72
|
+
attrs = data.members if attrs.empty?
|
|
73
|
+
attrs.all? { data[_1] == INHERITED }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# :call-seq:
|
|
77
|
+
# inherits_defaults?(*attrs) -> true | Rational | nil | false
|
|
78
|
+
#
|
|
79
|
+
# Returns whether all +attrs+ are inherited from a default config.
|
|
80
|
+
# When no +attrs+ are given, returns whether *all* attributes are
|
|
81
|
+
# inherited from a default config.
|
|
82
|
+
#
|
|
83
|
+
# Returns +true+ when all attributes inherit from Config.default, the
|
|
84
|
+
# version number (as a Rational) when all attributes inherit from a
|
|
85
|
+
# versioned default (see Config@Versioned+defaults), +nil+ if any
|
|
86
|
+
# attributes inherit from Config.global overrides (but not from
|
|
87
|
+
# non-global ancestors), or +false+ when any attributes have been
|
|
88
|
+
# overridden by +self+ or an ancestor (besides global or default
|
|
89
|
+
# configs),
|
|
90
|
+
#
|
|
91
|
+
# Related: #overrides?
|
|
92
|
+
def inherits_defaults?(*attrs)
|
|
93
|
+
if equal?(Config.default)
|
|
94
|
+
true
|
|
95
|
+
elsif equal?(Config.global)
|
|
96
|
+
true if inherited?(*attrs)
|
|
97
|
+
elsif (v = AttrVersionDefaults::VERSIONS.find { equal? Config[_1] })
|
|
98
|
+
attrs = DEFAULT_TO_INHERIT if attrs.empty?
|
|
99
|
+
attrs &= DEFAULT_TO_INHERIT
|
|
100
|
+
(attrs.empty? || parent.inherits_defaults?(*attrs)) && v
|
|
101
|
+
else
|
|
102
|
+
inherited?(*attrs) && parent.inherits_defaults?(*attrs)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# :call-seq:
|
|
107
|
+
# overrides?(attr) -> true or false
|
|
108
|
+
# overrides?(*attrs) -> true or false
|
|
109
|
+
# overrides? -> true or false
|
|
110
|
+
#
|
|
111
|
+
# Returns +true+ if +attr+ is defined on this config and not inherited
|
|
112
|
+
# from #parent.
|
|
113
|
+
#
|
|
114
|
+
# When multiple +attrs+ are given, returns +true+ if
|
|
115
|
+
# *any* of them are defined on +self+. When no +attrs+ are given,
|
|
116
|
+
# returns +true+ if *any* attribute is overriden.
|
|
117
|
+
#
|
|
118
|
+
# Related: #inherited?
|
|
119
|
+
def overrides?(*attrs)
|
|
120
|
+
attrs = data.members if attrs.empty?
|
|
121
|
+
attrs.any? { data[_1] != INHERITED }
|
|
122
|
+
end
|
|
60
123
|
|
|
61
124
|
# :call-seq:
|
|
62
125
|
# reset -> self
|
|
@@ -19,7 +19,7 @@ module Net
|
|
|
19
19
|
AttrTypeCoercion.attr_accessor(attr, type: type)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
module_function def Integer
|
|
22
|
+
module_function def Integer? = NilOrInteger
|
|
23
23
|
end
|
|
24
24
|
private_constant :Macros
|
|
25
25
|
|
|
@@ -28,14 +28,26 @@ module Net
|
|
|
28
28
|
end
|
|
29
29
|
private_class_method :included
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
if defined?(Ractor.shareable_proc)
|
|
32
|
+
def self.safe(&b)
|
|
33
|
+
case obj = b.call
|
|
34
|
+
when Proc
|
|
35
|
+
Ractor.shareable_proc(&obj)
|
|
36
|
+
else
|
|
37
|
+
Ractor.make_shareable obj
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
elsif defined?(Ractor.make_shareable)
|
|
41
|
+
def self.safe(&b)
|
|
42
|
+
obj = nil.instance_eval(&b).freeze
|
|
43
|
+
Ractor.make_shareable obj
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
def self.safe(&b) nil.instance_eval(&b).freeze end
|
|
47
|
+
end
|
|
33
48
|
private_class_method :safe
|
|
34
49
|
|
|
35
|
-
Types = Hash.new do |h, type|
|
|
36
|
-
type.nil? || Proc === type or raise TypeError, "type not nil or Proc"
|
|
37
|
-
safe{type}
|
|
38
|
-
end
|
|
50
|
+
Types = Hash.new do |h, type| type => Proc | nil; safe{type} end
|
|
39
51
|
Types[:boolean] = Boolean = safe{-> {!!_1}}
|
|
40
52
|
Types[Integer] = safe{->{Integer(_1)}}
|
|
41
53
|
|
|
@@ -48,10 +60,10 @@ module Net
|
|
|
48
60
|
NilOrInteger = safe{->val { Integer val unless val.nil? }}
|
|
49
61
|
|
|
50
62
|
Enum = ->(*enum) {
|
|
51
|
-
|
|
52
|
-
expected = -"one of #{
|
|
63
|
+
safe_enum = safe{enum}
|
|
64
|
+
expected = -"one of #{safe_enum.map(&:inspect).join(", ")}"
|
|
53
65
|
safe{->val {
|
|
54
|
-
return val if
|
|
66
|
+
return val if safe_enum.include?(val)
|
|
55
67
|
raise ArgumentError, "expected %s, got %p" % [expected, val]
|
|
56
68
|
}}
|
|
57
69
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
|
|
5
|
+
module Net
|
|
6
|
+
class IMAP
|
|
7
|
+
class Config
|
|
8
|
+
# >>>
|
|
9
|
+
# *NOTE:* This module is an internal implementation detail, with no
|
|
10
|
+
# guarantee of backward compatibility.
|
|
11
|
+
#
|
|
12
|
+
# Adds a +defaults+ parameter to +attr_accessor+, which is used to compile
|
|
13
|
+
# Config.version_defaults.
|
|
14
|
+
module AttrVersionDefaults
|
|
15
|
+
# The <tt>x.y</tt> part of Net::IMAP::VERSION, as a Rational number.
|
|
16
|
+
CURRENT_VERSION = VERSION.to_r
|
|
17
|
+
|
|
18
|
+
# The config version used for <tt>Config[:next]</tt>.
|
|
19
|
+
NEXT_VERSION = CURRENT_VERSION + 0.1r
|
|
20
|
+
|
|
21
|
+
# The config version used for <tt>Config[:future]</tt>.
|
|
22
|
+
FUTURE_VERSION = 1.0r
|
|
23
|
+
|
|
24
|
+
VERSIONS = ((0.0r..FUTURE_VERSION) % 0.1r).to_a.freeze
|
|
25
|
+
|
|
26
|
+
# See Config.version_defaults.
|
|
27
|
+
singleton_class.attr_reader :version_defaults
|
|
28
|
+
|
|
29
|
+
@version_defaults = Hash.new {|h, k|
|
|
30
|
+
# NOTE: String responds to both so the order is significant.
|
|
31
|
+
# And ignore non-numeric conversion to zero, because: "wat!?".to_r == 0
|
|
32
|
+
(h.fetch(k.to_r, nil) || h.fetch(k.to_f, nil) if k.is_a?(Numeric)) ||
|
|
33
|
+
(h.fetch(k.to_sym, nil) if k.respond_to?(:to_sym)) ||
|
|
34
|
+
(h.fetch(k.to_r, nil) if k.respond_to?(:to_r) && k.to_r != 0r) ||
|
|
35
|
+
(h.fetch(k.to_f, nil) if k.respond_to?(:to_f) && k.to_f != 0.0)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# :stopdoc: internal APIs only
|
|
39
|
+
|
|
40
|
+
def attr_accessor(name, defaults: nil, default: (unset = true), **kw)
|
|
41
|
+
unless unset
|
|
42
|
+
version = DEFAULT_TO_INHERIT.include?(name) ? nil : 0.0r
|
|
43
|
+
defaults = { version => default }
|
|
44
|
+
end
|
|
45
|
+
defaults&.each_pair do |version, default|
|
|
46
|
+
AttrVersionDefaults.version_defaults[version] ||= {}
|
|
47
|
+
AttrVersionDefaults.version_defaults[version][name] = default
|
|
48
|
+
end
|
|
49
|
+
super(name, **kw)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.compile_default!
|
|
53
|
+
raise "Config.default already compiled" if Config.default
|
|
54
|
+
default = VERSIONS.select { _1 <= CURRENT_VERSION }
|
|
55
|
+
.filter_map { version_defaults[_1] }
|
|
56
|
+
.prepend(version_defaults.delete(nil))
|
|
57
|
+
.inject(&:merge)
|
|
58
|
+
Config.new(**default).freeze
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.compile_version_defaults!
|
|
62
|
+
version_defaults[0.0r] = Config[version_defaults.fetch(0.0r)]
|
|
63
|
+
|
|
64
|
+
VERSIONS.each_cons(2) do |prior, version|
|
|
65
|
+
updates = version_defaults[version]
|
|
66
|
+
version_defaults[version] = version_defaults[prior]
|
|
67
|
+
.then { updates ? _1.dup.update(**updates).freeze : _1 }
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Safe conversions one way only:
|
|
71
|
+
# 0.6r.to_f == 0.6 # => true
|
|
72
|
+
# 0.6 .to_r == 0.6r # => false
|
|
73
|
+
version_defaults.to_a.each do |k, v|
|
|
74
|
+
next unless k in Rational
|
|
75
|
+
version_defaults[k.to_f] = v
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
version_defaults[:original] = Config[0.0r]
|
|
79
|
+
version_defaults[:current] = Config[CURRENT_VERSION]
|
|
80
|
+
version_defaults[:default] = Config[CURRENT_VERSION]
|
|
81
|
+
version_defaults[:next] = Config[NEXT_VERSION]
|
|
82
|
+
version_defaults[:future] = Config[FUTURE_VERSION]
|
|
83
|
+
|
|
84
|
+
version_defaults.freeze
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|