net-imap 0.5.6 → 0.5.13

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: cdbdda0ed73da899ec338f66022a16104562d3701c568b0a6d4897270a608ac5
4
- data.tar.gz: b6a7ec70776b32f8eb57d01a0869503eb5d76f719ac091eb92e0206608e936e9
3
+ metadata.gz: 1da7db7eb1545ee4a81555ef7f46c65e82e48ad8e4b64a4112881698df4ebc5d
4
+ data.tar.gz: b3a668019d4d36c2a9cfc8d41c5e65d1054a630dc2c5cb8ea7050fbaaf264f6b
5
5
  SHA512:
6
- metadata.gz: 381bf2428719ed8decb5d241fda0e19f28031dd4a77980b3717bb29c37bed1c927f00e5b57862e209ecf24b2e9b38c01088d6e1a90fc4b4cc026cdd9e6611100
7
- data.tar.gz: 513c6a77d46b6d2cf67aea4511023acc76c69940e3b1a0d0eae7223b53ff63bc8e6e009f51fef826b09f76f6ad1d92e84243e8457f59d3912db7e74bf69d3d1b
6
+ metadata.gz: 731921ff022993cd51ada037fa764067d4da07e6a8119545f7b225354b32f19c3936a8c807c3badd3664dde424c924842ecb0ac9851d74bb9bf568ebbbb800e6
7
+ data.tar.gz: da993296fd57d59c56c483920d901c9b6b44329c0bb5812c495de2024f01e449b0117be5900013875902ff1101ac0dfeaf177f407ea34e69ae210bd87f91e86a
data/Gemfile CHANGED
@@ -4,7 +4,7 @@ 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
10
 
@@ -14,10 +14,12 @@ gem "rdoc"
14
14
  gem "test-unit"
15
15
  gem "test-unit-ruby-core", git: "https://github.com/ruby/test-unit-ruby-core"
16
16
 
17
+ gem "benchmark", require: false
17
18
  gem "benchmark-driver", require: false
19
+ gem "vernier", require: false, platform: :mri
18
20
 
19
21
  group :test do
20
- gem "simplecov", require: false
21
- gem "simplecov-html", require: false
22
- gem "simplecov-json", require: false
22
+ gem "simplecov", require: false, platforms: %i[mri windows]
23
+ gem "simplecov-html", require: false, platforms: %i[mri windows]
24
+ gem "simplecov-json", require: false, platforms: %i[mri windows]
23
25
  end
@@ -18,6 +18,8 @@ module Net
18
18
  super(attr)
19
19
  AttrTypeCoercion.attr_accessor(attr, type: type)
20
20
  end
21
+
22
+ module_function def Integer? = NilOrInteger
21
23
  end
22
24
  private_constant :Macros
23
25
 
@@ -26,34 +28,45 @@ module Net
26
28
  end
27
29
  private_class_method :included
28
30
 
29
- def self.attr_accessor(attr, type: nil)
30
- return unless type
31
- if :boolean == type then boolean attr
32
- elsif Integer == type then integer attr
33
- elsif Array === type then enum attr, type
34
- else raise ArgumentError, "unknown type coercion %p" % [type]
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
35
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
36
47
  end
48
+ private_class_method :safe
37
49
 
38
- def self.boolean(attr)
39
- define_method :"#{attr}=" do |val| super !!val end
40
- define_method :"#{attr}?" do send attr end
41
- end
50
+ Types = Hash.new do |h, type| type => Proc | nil; safe{type} end
51
+ Types[:boolean] = Boolean = safe{-> {!!_1}}
52
+ Types[Integer] = safe{->{Integer(_1)}}
42
53
 
43
- def self.integer(attr)
44
- define_method :"#{attr}=" do |val| super Integer val end
54
+ def self.attr_accessor(attr, type: nil)
55
+ type = Types[type] or return
56
+ define_method :"#{attr}=" do |val| super type[val] end
57
+ define_method :"#{attr}?" do send attr end if type == Boolean
45
58
  end
46
59
 
47
- def self.enum(attr, enum)
48
- enum = enum.dup.freeze
49
- expected = -"one of #{enum.map(&:inspect).join(", ")}"
50
- define_method :"#{attr}=" do |val|
51
- unless enum.include?(val)
52
- raise ArgumentError, "expected %s, got %p" % [expected, val]
53
- end
54
- super val
55
- end
56
- end
60
+ NilOrInteger = safe{->val { Integer val unless val.nil? }}
61
+
62
+ Enum = ->(*enum) {
63
+ safe_enum = safe{enum}
64
+ expected = -"one of #{safe_enum.map(&:inspect).join(", ")}"
65
+ safe{->val {
66
+ return val if safe_enum.include?(val)
67
+ raise ArgumentError, "expected %s, got %p" % [expected, val]
68
+ }}
69
+ }
57
70
 
58
71
  end
59
72
  end
@@ -0,0 +1,93 @@
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_accessor :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
+ # Temporarily assign Config.default, enabling #load_defaults(:default)
63
+ version_defaults[:default] = Config.default
64
+ # Use #load_defaults so some attributes are inherited from global.
65
+ version_defaults[:default] = Config.new.load_defaults(:default).freeze
66
+ version_defaults[0.0r] = Config[version_defaults.fetch(0.0r)]
67
+
68
+ VERSIONS.each_cons(2) do |prior, version|
69
+ updates = version_defaults[version]
70
+ version_defaults[version] = version_defaults[prior]
71
+ .then { updates ? _1.dup.update(**updates).freeze : _1 }
72
+ end
73
+
74
+ # Safe conversions one way only:
75
+ # 0.6r.to_f == 0.6 # => true
76
+ # 0.6 .to_r == 0.6r # => false
77
+ version_defaults.to_a.each do |k, v|
78
+ next unless k in Rational
79
+ version_defaults[k.to_f] = v
80
+ end
81
+
82
+ version_defaults[:original] = Config[0.0r]
83
+ version_defaults[:current] = Config[CURRENT_VERSION]
84
+ version_defaults[:next] = Config[NEXT_VERSION]
85
+ version_defaults[:future] = Config[FUTURE_VERSION]
86
+
87
+ version_defaults.freeze
88
+ end
89
+
90
+ end
91
+ end
92
+ end
93
+ end
@@ -3,6 +3,7 @@
3
3
  require_relative "config/attr_accessors"
4
4
  require_relative "config/attr_inheritance"
5
5
  require_relative "config/attr_type_coercion"
6
+ require_relative "config/attr_version_defaults"
6
7
 
7
8
  module Net
8
9
  class IMAP
@@ -131,8 +132,17 @@ module Net
131
132
  def self.global; @global if defined?(@global) end
132
133
 
133
134
  # A hash of hard-coded configurations, indexed by version number or name.
134
- def self.version_defaults; @version_defaults end
135
- @version_defaults = {}
135
+ # Values can be accessed with any object that responds to +to_sym+ or
136
+ # +to_r+/+to_f+ with a non-zero number.
137
+ #
138
+ # Config::[] gets named or numbered versions from this hash.
139
+ #
140
+ # For example:
141
+ # Net::IMAP::Config.version_defaults[0.5] == Net::IMAP::Config[0.5]
142
+ # Net::IMAP::Config[0.5] == Net::IMAP::Config[0.5r] # => true
143
+ # Net::IMAP::Config["current"] == Net::IMAP::Config[:current] # => true
144
+ # Net::IMAP::Config["0.5.6"] == Net::IMAP::Config[0.5r] # => true
145
+ def self.version_defaults; AttrVersionDefaults.version_defaults end
136
146
 
137
147
  # :call-seq:
138
148
  # Net::IMAP::Config[number] -> versioned config
@@ -155,24 +165,24 @@ module Net
155
165
  elsif config.nil? && global.nil? then nil
156
166
  elsif config.respond_to?(:to_hash) then new(global, **config).freeze
157
167
  else
158
- version_defaults.fetch(config) do
168
+ version_defaults[config] or
159
169
  case config
160
170
  when Numeric
161
171
  raise RangeError, "unknown config version: %p" % [config]
162
- when Symbol
172
+ when String, Symbol
163
173
  raise KeyError, "unknown config name: %p" % [config]
164
174
  else
165
175
  raise TypeError, "no implicit conversion of %s to %s" % [
166
176
  config.class, Config
167
177
  ]
168
178
  end
169
- end
170
179
  end
171
180
  end
172
181
 
173
182
  include AttrAccessors
174
183
  include AttrInheritance
175
184
  include AttrTypeCoercion
185
+ extend AttrVersionDefaults
176
186
 
177
187
  # The debug mode (boolean). The default value is +false+.
178
188
  #
@@ -184,7 +194,7 @@ module Net
184
194
  #
185
195
  # *NOTE:* Versioned default configs inherit #debug from Config.global, and
186
196
  # #load_defaults will not override #debug.
187
- attr_accessor :debug, type: :boolean
197
+ attr_accessor :debug, type: :boolean, default: false
188
198
 
189
199
  # method: debug?
190
200
  # :call-seq: debug? -> boolean
@@ -193,13 +203,16 @@ module Net
193
203
 
194
204
  # Seconds to wait until a connection is opened.
195
205
  #
206
+ # Applied separately for establishing TCP connection and starting a TLS
207
+ # connection.
208
+ #
196
209
  # If the IMAP object cannot open a connection within this time,
197
210
  # it raises a Net::OpenTimeout exception.
198
211
  #
199
- # See Net::IMAP.new.
212
+ # See Net::IMAP.new and Net::IMAP#starttls.
200
213
  #
201
214
  # The default value is +30+ seconds.
202
- attr_accessor :open_timeout, type: Integer
215
+ attr_accessor :open_timeout, type: Integer, default: 30
203
216
 
204
217
  # Seconds to wait until an IDLE response is received, after
205
218
  # the client asks to leave the IDLE state.
@@ -207,7 +220,7 @@ module Net
207
220
  # See Net::IMAP#idle and Net::IMAP#idle_done.
208
221
  #
209
222
  # The default value is +5+ seconds.
210
- attr_accessor :idle_response_timeout, type: Integer
223
+ attr_accessor :idle_response_timeout, type: Integer, default: 5
211
224
 
212
225
  # Whether to use the +SASL-IR+ extension when the server and \SASL
213
226
  # mechanism both support it. Can be overridden by the +sasl_ir+ keyword
@@ -223,7 +236,10 @@ module Net
223
236
  #
224
237
  # [+true+ <em>(default since +v0.4+)</em>]
225
238
  # Use +SASL-IR+ when it is supported by the server and the mechanism.
226
- attr_accessor :sasl_ir, type: :boolean
239
+ attr_accessor :sasl_ir, type: :boolean, defaults: {
240
+ 0.0r => false,
241
+ 0.4r => true,
242
+ }
227
243
 
228
244
  # Controls the behavior of Net::IMAP#login when the +LOGINDISABLED+
229
245
  # capability is present. When enforced, Net::IMAP will raise a
@@ -245,9 +261,49 @@ module Net
245
261
  # present. When capabilities are unknown, Net::IMAP will automatically
246
262
  # send a +CAPABILITY+ command first before sending +LOGIN+.
247
263
  #
248
- attr_accessor :enforce_logindisabled, type: [
264
+ attr_accessor :enforce_logindisabled, type: Enum[
249
265
  false, :when_capabilities_cached, true
250
- ]
266
+ ], defaults: {
267
+ 0.0r => false,
268
+ 0.5r => true,
269
+ }
270
+
271
+ # The maximum allowed server response size. When +nil+, there is no limit
272
+ # on response size.
273
+ #
274
+ # The default value (512 MiB, since +v0.5.7+) is <em>very high</em> and
275
+ # unlikely to be reached. A _much_ lower value should be used with
276
+ # untrusted servers (for example, when connecting to a user-provided
277
+ # hostname). When using a lower limit, message bodies should be fetched
278
+ # in chunks rather than all at once.
279
+ #
280
+ # <em>Please Note:</em> this only limits the size per response. It does
281
+ # not prevent a flood of individual responses and it does not limit how
282
+ # many unhandled responses may be stored on the responses hash. See
283
+ # Net::IMAP@Unbounded+memory+use.
284
+ #
285
+ # Socket reads are limited to the maximum remaining bytes for the current
286
+ # response: max_response_size minus the bytes that have already been read.
287
+ # When the limit is reached, or reading a +literal+ _would_ go over the
288
+ # limit, ResponseTooLargeError is raised and the connection is closed.
289
+ #
290
+ # Note that changes will not take effect immediately, because the receiver
291
+ # thread may already be waiting for the next response using the previous
292
+ # value. Net::IMAP#noop can force a response and enforce the new setting
293
+ # immediately.
294
+ #
295
+ # ==== Versioned Defaults
296
+ #
297
+ # Net::IMAP#max_response_size <em>was added in +v0.2.5+ and +v0.3.9+ as an
298
+ # attr_accessor, and in +v0.4.20+ and +v0.5.7+ as a delegator to this
299
+ # config attribute.</em>
300
+ #
301
+ # * original: +nil+ <em>(no limit)</em>
302
+ # * +0.5+: 512 MiB
303
+ attr_accessor :max_response_size, type: Integer?, defaults: {
304
+ 0.0r => nil,
305
+ 0.5r => 512 << 20, # 512 MiB
306
+ }
251
307
 
252
308
  # Controls the behavior of Net::IMAP#responses when called without any
253
309
  # arguments (+type+ or +block+).
@@ -275,9 +331,13 @@ module Net
275
331
  # Raise an ArgumentError with the deprecation warning.
276
332
  #
277
333
  # Note: #responses_without_args is an alias for #responses_without_block.
278
- attr_accessor :responses_without_block, type: [
334
+ attr_accessor :responses_without_block, type: Enum[
279
335
  :silence_deprecation_warning, :warn, :frozen_dup, :raise,
280
- ]
336
+ ], defaults: {
337
+ 0.0r => :silence_deprecation_warning,
338
+ 0.5r => :warn,
339
+ 0.6r => :frozen_dup,
340
+ }
281
341
 
282
342
  alias responses_without_args responses_without_block # :nodoc:
283
343
  alias responses_without_args= responses_without_block= # :nodoc:
@@ -320,9 +380,13 @@ module Net
320
380
  #
321
381
  # [+false+ <em>(planned default for +v0.6+)</em>]
322
382
  # ResponseParser _only_ uses AppendUIDData and CopyUIDData.
323
- attr_accessor :parser_use_deprecated_uidplus_data, type: [
383
+ attr_accessor :parser_use_deprecated_uidplus_data, type: Enum[
324
384
  true, :up_to_max_size, false
325
- ]
385
+ ], defaults: {
386
+ 0.0r => true,
387
+ 0.5r => :up_to_max_size,
388
+ 0.6r => false,
389
+ }
326
390
 
327
391
  # The maximum +uid-set+ size that ResponseParser will parse into
328
392
  # deprecated UIDPlusData. This limit only applies when
@@ -346,7 +410,13 @@ module Net
346
410
  # * +0.5+: <tt>100</tt>
347
411
  # * +0.6+: <tt>0</tt>
348
412
  #
349
- attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer
413
+ attr_accessor :parser_max_deprecated_uidplus_data_size, type: Integer,
414
+ defaults: {
415
+ 0.0r => 10_000,
416
+ 0.4r => 1_000,
417
+ 0.5r => 100,
418
+ 0.6r => 0,
419
+ }
350
420
 
351
421
  # Creates a new config object and initialize its attribute with +attrs+.
352
422
  #
@@ -421,50 +491,10 @@ module Net
421
491
  to_h.reject {|k,v| DEFAULT_TO_INHERIT.include?(k) }
422
492
  end
423
493
 
424
- @default = new(
425
- debug: false,
426
- open_timeout: 30,
427
- idle_response_timeout: 5,
428
- sasl_ir: true,
429
- enforce_logindisabled: true,
430
- responses_without_block: :warn,
431
- parser_use_deprecated_uidplus_data: :up_to_max_size,
432
- parser_max_deprecated_uidplus_data_size: 100,
433
- ).freeze
434
-
435
- @global = default.new
436
-
437
- version_defaults[:default] = Config[default.send(:defaults_hash)]
438
- version_defaults[:current] = Config[:default]
439
-
440
- version_defaults[0] = Config[:current].dup.update(
441
- sasl_ir: false,
442
- responses_without_block: :silence_deprecation_warning,
443
- enforce_logindisabled: false,
444
- parser_use_deprecated_uidplus_data: true,
445
- parser_max_deprecated_uidplus_data_size: 10_000,
446
- ).freeze
447
- version_defaults[0.0] = Config[0]
448
- version_defaults[0.1] = Config[0]
449
- version_defaults[0.2] = Config[0]
450
- version_defaults[0.3] = Config[0]
451
-
452
- version_defaults[0.4] = Config[0.3].dup.update(
453
- sasl_ir: true,
454
- parser_max_deprecated_uidplus_data_size: 1000,
455
- ).freeze
456
-
457
- version_defaults[0.5] = Config[:current]
458
-
459
- version_defaults[0.6] = Config[0.5].dup.update(
460
- responses_without_block: :frozen_dup,
461
- parser_use_deprecated_uidplus_data: false,
462
- parser_max_deprecated_uidplus_data_size: 0,
463
- ).freeze
464
- version_defaults[:next] = Config[0.6]
465
- version_defaults[:future] = Config[:next]
494
+ @default = AttrVersionDefaults.compile_default!
495
+ @global = default.new
496
+ AttrVersionDefaults.compile_version_defaults!
466
497
 
467
- version_defaults.freeze
468
498
  end
469
499
  end
470
500
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP
5
+ class ConnectionState < Net::IMAP::Data # :nodoc:
6
+ def self.define(symbol, *attrs)
7
+ symbol => Symbol
8
+ state = super(*attrs)
9
+ state.const_set :NAME, symbol
10
+ state
11
+ end
12
+
13
+ def symbol; self.class::NAME end
14
+ def name; self.class::NAME.name end
15
+ alias to_sym symbol
16
+
17
+ def deconstruct; [symbol, *super] end
18
+
19
+ def deconstruct_keys(names)
20
+ hash = super
21
+ hash[:symbol] = symbol if names.nil? || names.include?(:symbol)
22
+ hash[:name] = name if names.nil? || names.include?(:name)
23
+ hash
24
+ end
25
+
26
+ def to_h(&block)
27
+ hash = deconstruct_keys(nil)
28
+ block ? hash.to_h(&block) : hash
29
+ end
30
+
31
+ def not_authenticated?; to_sym == :not_authenticated end
32
+ def authenticated?; to_sym == :authenticated end
33
+ def selected?; to_sym == :selected end
34
+ def logout?; to_sym == :logout end
35
+
36
+ NotAuthenticated = define(:not_authenticated)
37
+ Authenticated = define(:authenticated)
38
+ Selected = define(:selected)
39
+ Logout = define(:logout)
40
+
41
+ class << self
42
+ undef :define
43
+ end
44
+ freeze
45
+ end
46
+
47
+ end
48
+ end
@@ -17,6 +17,39 @@ module Net
17
17
  class DataFormatError < Error
18
18
  end
19
19
 
20
+ # Error raised when the socket cannot be read, due to a Config limit.
21
+ class ResponseReadError < Error
22
+ end
23
+
24
+ # Error raised when a response is larger than IMAP#max_response_size.
25
+ class ResponseTooLargeError < ResponseReadError
26
+ attr_reader :bytes_read, :literal_size
27
+ attr_reader :max_response_size
28
+
29
+ def initialize(msg = nil, *args,
30
+ bytes_read: nil,
31
+ literal_size: nil,
32
+ max_response_size: nil,
33
+ **kwargs)
34
+ @bytes_read = bytes_read
35
+ @literal_size = literal_size
36
+ @max_response_size = max_response_size
37
+ msg ||= [
38
+ "Response size", response_size_msg, "exceeds max_response_size",
39
+ max_response_size && "(#{max_response_size}B)",
40
+ ].compact.join(" ")
41
+ super(msg, *args, **kwargs)
42
+ end
43
+
44
+ private
45
+
46
+ def response_size_msg
47
+ if bytes_read && literal_size
48
+ "(#{bytes_read}B read + #{literal_size}B literal)"
49
+ end
50
+ end
51
+ end
52
+
20
53
  # Error raised when a response from the server is non-parsable.
21
54
  class ResponseParseError < Error
22
55
  end
@@ -39,12 +39,49 @@ module Net
39
39
  # numbers or UIDs, +to_a+ returns that set as an array of integers.
40
40
  #
41
41
  # When both #all and #partial are +nil+, either because the server
42
- # returned no results or because +ALL+ and +PARTIAL+ were not included in
43
- # the IMAP#search +RETURN+ options, #to_a returns an empty array.
42
+ # returned no results or because neither +ALL+ or +PARTIAL+ were included
43
+ # in the IMAP#search +RETURN+ options, #to_a returns an empty array.
44
44
  #
45
45
  # Note that SearchResult also implements +to_a+, so it can be used without
46
46
  # checking if the server returned +SEARCH+ or +ESEARCH+ data.
47
- def to_a; all&.numbers || partial&.to_a || [] end
47
+ #
48
+ # Related: #each, #to_sequence_set, #all, #partial
49
+ def to_a; to_sequence_set.numbers end
50
+
51
+ # :call-seq: to_sequence_set -> SequenceSet or nil
52
+ #
53
+ # When either #all or #partial contains a SequenceSet of message sequence
54
+ # numbers or UIDs, +to_sequence_set+ returns that sequence set.
55
+ #
56
+ # When both #all and #partial are +nil+, either because the server
57
+ # returned no results or because neither +ALL+ or +PARTIAL+ were included
58
+ # in the IMAP#search +RETURN+ options, #to_sequence_set returns
59
+ # SequenceSet.empty.
60
+ #
61
+ # Note that SearchResult also implements +to_sequence_set+, so it can be
62
+ # used without checking if the server returned +SEARCH+ or +ESEARCH+ data.
63
+ #
64
+ # Related: #each, #to_a, #all, #partial
65
+ def to_sequence_set
66
+ all || partial&.to_sequence_set || SequenceSet.empty
67
+ end
68
+
69
+ # When either #all or #partial contains a SequenceSet of message sequence
70
+ # numbers or UIDs, +each+ yields each integer in the set.
71
+ #
72
+ # When both #all and #partial are +nil+, either because the server
73
+ # returned no results or because +ALL+ and +PARTIAL+ were not included in
74
+ # the IMAP#search +RETURN+ options, #each does not yield.
75
+ #
76
+ # Note that SearchResult also implements +#each+, so it can be used
77
+ # without checking if the server returned +SEARCH+ or +ESEARCH+ data.
78
+ #
79
+ # Related: #to_sequence_set, #to_a, #all, #partial
80
+ def each(&)
81
+ return to_enum(__callee__) unless block_given?
82
+ to_sequence_set.each_number(&)
83
+ self
84
+ end
48
85
 
49
86
  ##
50
87
  # attr_reader: tag
@@ -161,6 +198,8 @@ module Net
161
198
  #
162
199
  # See also: ESearchResult#to_a.
163
200
  def to_a; results&.numbers || [] end
201
+
202
+ alias to_sequence_set results
164
203
  end
165
204
 
166
205
  # :call-seq: partial -> PartialResult or nil
@@ -173,7 +173,7 @@ module Net
173
173
  SUBSCRIBED = :Subscribed
174
174
 
175
175
  # The mailbox is a remote mailbox.
176
- REMOTE = :Remove
176
+ REMOTE = :Remote
177
177
 
178
178
  # Alias for NO_INFERIORS, to match the \IMAP spelling.
179
179
  NOINFERIORS = NO_INFERIORS
@@ -6,7 +6,6 @@ module Net
6
6
  autoload :FetchData, "#{__dir__}/fetch_data"
7
7
  autoload :UIDFetchData, "#{__dir__}/fetch_data"
8
8
  autoload :SearchResult, "#{__dir__}/search_result"
9
- autoload :SequenceSet, "#{__dir__}/sequence_set"
10
9
  autoload :UIDPlusData, "#{__dir__}/uidplus_data"
11
10
  autoload :AppendUIDData, "#{__dir__}/uidplus_data"
12
11
  autoload :CopyUIDData, "#{__dir__}/uidplus_data"