net-imap 0.5.8 → 0.5.10

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: b6d2d79f907ce00d056ead396fafe73bbdf84fc54b01e1f2536d6490c001c723
4
- data.tar.gz: 6fba3cd04144fedd7b178959451631ea14cff8f248ca963961b3945b636cec4b
3
+ metadata.gz: 261794e07175481d35e146b718183fc057a3a54e2fd9958b25918c6bd4178ec4
4
+ data.tar.gz: 2568eb1d284b3f1662d1cf0c1bc79eb65722fa3ba8dc03072b52fbd3716c9294
5
5
  SHA512:
6
- metadata.gz: 80f15f967d4260638d423b802204360e25704cc47c59d110a85f401c1554882521097d5014ecc3e7a0a17f87e8995ae87f553d73a26f3c8abc84744ad5d236ed
7
- data.tar.gz: 2790cbc5ee60b3da3648e8bd91df90ecac7c72e0febca11464400460fd5c34db3123d307f39901ea7c313942f2feb11bb3c0f7dbc06b96094b6d1738426b278a
6
+ metadata.gz: 357279f77c69c27b78924847216afad6c806abb7bfee5f0c95aab6c397cb10298440a59cb32b9f75a4174e6965d6bfe48a5e847e20d9098845033e8453f73302
7
+ data.tar.gz: e3c29888d787de23a843bfb9ff9194d1fa136763e9d9bcf36800953362f7611c489da1f9150ee2a23baa571125f4f2af2cebe4ee4666447783e355bd9995eb3a
data/Gemfile CHANGED
@@ -14,7 +14,9 @@ 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
22
  gem "simplecov", require: false
@@ -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"
@@ -18,21 +18,9 @@ module Net
18
18
  #
19
19
  # == Creating sequence sets
20
20
  #
21
- # SequenceSet.new with no arguments creates an empty sequence set. Note
22
- # that an empty sequence set is invalid in the \IMAP grammar.
23
- #
24
- # set = Net::IMAP::SequenceSet.new
25
- # set.empty? #=> true
26
- # set.valid? #=> false
27
- # set.valid_string #!> raises DataFormatError
28
- # set << 1..10
29
- # set.empty? #=> false
30
- # set.valid? #=> true
31
- # set.valid_string #=> "1:10"
32
- #
33
21
  # SequenceSet.new may receive a single optional argument: a non-zero 32 bit
34
22
  # unsigned integer, a range, a <tt>sequence-set</tt> formatted string,
35
- # another sequence set, a Set (containing only numbers or <tt>*</tt>), or an
23
+ # another SequenceSet, a Set (containing only numbers or <tt>*</tt>), or an
36
24
  # Array containing any of these (array inputs may be nested).
37
25
  #
38
26
  # set = Net::IMAP::SequenceSet.new(1)
@@ -48,30 +36,114 @@ module Net
48
36
  # set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
49
37
  # set.valid_string #=> "1:10,55,1024:2048"
50
38
  #
51
- # Use ::[] with one or more arguments to create a frozen SequenceSet. An
52
- # invalid (empty) set cannot be created with ::[].
39
+ # SequenceSet.new with no arguments creates an empty sequence set. Note
40
+ # that an empty sequence set is invalid in the \IMAP grammar.
53
41
  #
42
+ # set = Net::IMAP::SequenceSet.new
43
+ # set.empty? #=> true
44
+ # set.valid? #=> false
45
+ # set.valid_string #!> raises DataFormatError
46
+ # set << 1..10
47
+ # set.empty? #=> false
48
+ # set.valid? #=> true
49
+ # set.valid_string #=> "1:10"
50
+ #
51
+ # Using SequenceSet.new with another SequenceSet input behaves the same as
52
+ # calling #dup on the other set. The input's #string will be preserved.
53
+ #
54
+ # input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
55
+ # copy = Net::IMAP::SequenceSet.new(input)
56
+ # input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
57
+ # copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
58
+ # copy2 = input.dup # same as calling new with a SequenceSet input
59
+ # copy == input #=> true, same set membership
60
+ # copy.eql? input #=> true, same string value
61
+ # copy.equal? input #=> false, different objects
62
+ #
63
+ # copy.normalize!
64
+ # copy.valid_string #=> "1:10,1024,2048"
65
+ # copy == input #=> true, same set membership
66
+ # copy.eql? input #=> false, different string value
67
+ #
68
+ # copy << 999
69
+ # copy.valid_string #=> "1:10,999,1024,2048"
70
+ # copy == input #=> false, different set membership
71
+ # copy.eql? input #=> false, different string value
72
+ #
73
+ # Use Net::IMAP::SequenceSet() to coerce a single (optional) input.
74
+ # A SequenceSet input is returned without duplication, even when frozen.
75
+ #
76
+ # set = Net::IMAP::SequenceSet()
77
+ # set.string #=> nil
78
+ # set.frozen? #=> false
79
+ #
80
+ # # String order is preserved
81
+ # set = Net::IMAP::SequenceSet("1,2,3:7,5,6:10,2048,1024")
82
+ # set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
83
+ # set.frozen? #=> false
84
+ #
85
+ # # Other inputs are normalized
86
+ # set = Net::IMAP::SequenceSet([1, 2, [3..7, 5], 6..10, 2048, 1024])
87
+ # set.valid_string #=> "1:10,1024,2048"
88
+ # set.frozen? #=> false
89
+ #
90
+ # unfrozen = set
91
+ # frozen = set.dup.freeze
92
+ # unfrozen.equal? Net::IMAP::SequenceSet(unfrozen) #=> true
93
+ # frozen.equal? Net::IMAP::SequenceSet(frozen) #=> true
94
+ #
95
+ # Use ::[] to coerce one or more arguments into a valid frozen SequenceSet.
96
+ # A valid frozen SequenceSet is returned directly, without allocating a new
97
+ # object. ::[] will not create an invalid (empty) set.
98
+ #
99
+ # Net::IMAP::SequenceSet[] #!> raises ArgumentError
100
+ # Net::IMAP::SequenceSet[nil] #!> raises DataFormatError
101
+ # Net::IMAP::SequenceSet[""] #!> raises DataFormatError
102
+ #
103
+ # # String order is preserved
54
104
  # set = Net::IMAP::SequenceSet["1,2,3:7,5,6:10,2048,1024"]
55
105
  # set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
106
+ # set.frozen? #=> true
107
+ #
108
+ # # Other inputs are normalized
56
109
  # set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
57
- # set.valid_string #=> "1:10,55,1024:2048"
110
+ # set.valid_string #=> "1:10,1024,2048"
111
+ # set.frozen? #=> true
112
+ #
113
+ # frozen = set
114
+ # unfrozen = set.dup
115
+ # frozen.equal? Net::IMAP::SequenceSet[frozen] #=> true
116
+ # unfrozen.equal? Net::IMAP::SequenceSet[unfrozen] #=> false
117
+ #
118
+ # Objects which respond to +to_sequence_set+ (such as SearchResult and
119
+ # ThreadMember) can be coerced to a SequenceSet with ::new, ::try_convert,
120
+ # ::[], or Net::IMAP::SequenceSet.
121
+ #
122
+ # search = imap.uid_search(["SUBJECT", "hello", "NOT", "SEEN"])
123
+ # seqset = Net::IMAP::SequenceSet(search) - already_fetched
124
+ # fetch = imap.uid_fetch(seqset, "FAST")
58
125
  #
59
126
  # == Ordered and Normalized sets
60
127
  #
61
128
  # Sometimes the order of the set's members is significant, such as with the
62
129
  # +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
63
- # sequence set is created by the parser or with a single string value, that
64
- # #string representation is preserved.
130
+ # sequence set is created from a single string (such as by the parser), that
131
+ # #string representation is preserved. Assigning a string with #string= or
132
+ # #replace will also preserve that string. Use #each_entry, #entries, or
133
+ # #each_ordered_number to enumerate the entries in their #string order.
134
+ # Hash equality (using #eql?) is based on the string representation.
65
135
  #
66
- # Internally, SequenceSet stores a normalized representation which sorts all
67
- # entries, de-duplicates numbers, and coalesces adjacent or overlapping
68
- # ranges. Most methods use this normalized representation to achieve
69
- # <tt>O(lg n)</tt> porformance. Use #entries or #each_entry to enumerate
70
- # the set in its original order.
136
+ # Internally, SequenceSet uses a normalized uint32 set representation which
137
+ # sorts and de-duplicates all numbers and coalesces adjacent or overlapping
138
+ # entries. Many methods use this sorted set representation for <tt>O(lg
139
+ # n)</tt> searches. Use #each_element, #elements, #each_range, #ranges,
140
+ # #each_number, or #numbers to enumerate the set in sorted order. Basic
141
+ # object equality (using #==) is based on set membership, without regard to
142
+ # #entry order or #string normalization.
71
143
  #
72
- # Most modification methods convert #string to its normalized form. To
73
- # preserve #string order while modifying a set, use #append, #string=, or
74
- # #replace.
144
+ # Most modification methods reset #string to its #normalized form, so that
145
+ # #entries and #elements are identical. Use #append to preserve #entries
146
+ # order while modifying a set.
75
147
  #
76
148
  # == Using <tt>*</tt>
77
149
  #
@@ -153,6 +225,7 @@ module Net
153
225
  # * ::new: Creates a new mutable sequence set, which may be empty (invalid).
154
226
  # * ::try_convert: Calls +to_sequence_set+ on an object and verifies that
155
227
  # the result is a SequenceSet.
228
+ # * Net::IMAP::SequenceSet(): Coerce an input using ::try_convert or ::new.
156
229
  # * ::empty: Returns a frozen empty (invalid) SequenceSet.
157
230
  # * ::full: Returns a frozen SequenceSet containing every possible number.
158
231
  #
@@ -178,8 +251,7 @@ module Net
178
251
  #
179
252
  # <i>Set membership:</i>
180
253
  # - #include? (aliased as #member?):
181
- # Returns whether a given element (nz-number, range, or <tt>*</tt>) is
182
- # contained by the set.
254
+ # Returns whether a given element is contained by the set.
183
255
  # - #include_star?: Returns whether the set contains <tt>*</tt>.
184
256
  #
185
257
  # <i>Minimum and maximum value elements:</i>
@@ -324,6 +396,23 @@ module Net
324
396
  STARS = [:*, ?*, -1].freeze
325
397
  private_constant :STARS
326
398
 
399
+ INSPECT_MAX_LEN = 512
400
+ INSPECT_TRUNCATE_LEN = 16
401
+ private_constant :INSPECT_MAX_LEN, :INSPECT_TRUNCATE_LEN
402
+
403
+ # /(,\d+){100}\z/ is shockingly slow on huge strings.
404
+ # /(,\d{0,10}){100}\z/ is ok, but ironically, Regexp.linear_time? is false.
405
+ #
406
+ # This unrolls all nested quantifiers. It's much harder to read, but it's
407
+ # also the fastest out of all the versions I tested.
408
+ nz_uint32 = /[1-9](?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d(?:\d)?)?)?)?)?)?)?)?)?/
409
+ num_or_star = /#{nz_uint32}|\*/
410
+ entry = /#{num_or_star}(?::#{num_or_star})?/
411
+ entries = ([entry] * INSPECT_TRUNCATE_LEN).join(",")
412
+ INSPECT_ABRIDGED_HEAD_RE = /\A#{entries},/
413
+ INSPECT_ABRIDGED_TAIL_RE = /,#{entries}\z/
414
+ private_constant :INSPECT_ABRIDGED_HEAD_RE, :INSPECT_ABRIDGED_TAIL_RE
415
+
327
416
  class << self
328
417
 
329
418
  # :call-seq:
@@ -337,13 +426,12 @@ module Net
337
426
  # An empty SequenceSet is invalid and will raise a DataFormatError.
338
427
  #
339
428
  # Use ::new to create a mutable or empty SequenceSet.
429
+ #
430
+ # Related: ::new, Net::IMAP::SequenceSet(), ::try_convert
340
431
  def [](first, *rest)
341
432
  if rest.empty?
342
- if first.is_a?(SequenceSet) && first.frozen? && first.valid?
343
- first
344
- else
345
- new(first).validate.freeze
346
- end
433
+ set = try_convert(first)&.validate
434
+ set&.frozen? ? set : (set&.dup || new(first).validate).freeze
347
435
  else
348
436
  new(first).merge(*rest).validate.freeze
349
437
  end
@@ -358,6 +446,8 @@ module Net
358
446
  #
359
447
  # If +obj.to_sequence_set+ doesn't return a SequenceSet, an exception is
360
448
  # raised.
449
+ #
450
+ # Related: Net::IMAP::SequenceSet(), ::new, ::[]
361
451
  def try_convert(obj)
362
452
  return obj if obj.is_a?(SequenceSet)
363
453
  return nil unless obj.respond_to?(:to_sequence_set)
@@ -376,23 +466,91 @@ module Net
376
466
  end
377
467
 
378
468
  # Create a new SequenceSet object from +input+, which may be another
379
- # SequenceSet, an IMAP formatted +sequence-set+ string, a number, a
380
- # range, <tt>:*</tt>, or an enumerable of these.
381
- #
382
- # Use ::[] to create a frozen (non-empty) SequenceSet.
469
+ # SequenceSet, an IMAP formatted +sequence-set+ string, a non-zero 32 bit
470
+ # unsigned integer, a range, <tt>:*</tt>, a Set of numbers or <tt>*</tt>,
471
+ # an object that responds to +to_sequence_set+ (such as SearchResult) or
472
+ # an Array of these (array inputs may be nested).
473
+ #
474
+ # set = Net::IMAP::SequenceSet.new(1)
475
+ # set.valid_string #=> "1"
476
+ # set = Net::IMAP::SequenceSet.new(1..100)
477
+ # set.valid_string #=> "1:100"
478
+ # set = Net::IMAP::SequenceSet.new(1...100)
479
+ # set.valid_string #=> "1:99"
480
+ # set = Net::IMAP::SequenceSet.new([1, 2, 5..])
481
+ # set.valid_string #=> "1:2,5:*"
482
+ # set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
483
+ # set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
484
+ # set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
485
+ # set.valid_string #=> "1:10,1024,2048"
486
+ #
487
+ # With no arguments (or +nil+) creates an empty sequence set. Note that
488
+ # an empty sequence set is invalid in the \IMAP grammar.
489
+ #
490
+ # set = Net::IMAP::SequenceSet.new
491
+ # set.empty? #=> true
492
+ # set.valid? #=> false
493
+ # set.valid_string #!> raises DataFormatError
494
+ # set << 1..10
495
+ # set.empty? #=> false
496
+ # set.valid? #=> true
497
+ # set.valid_string #=> "1:10"
498
+ #
499
+ # When +input+ is a SequenceSet, ::new behaves the same as calling #dup on
500
+ # that other set. The input's #string will be preserved.
501
+ #
502
+ # input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
503
+ # copy = Net::IMAP::SequenceSet.new(input)
504
+ # input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
505
+ # copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
506
+ # copy2 = input.dup # same as calling new with a SequenceSet input
507
+ # copy == input #=> true, same set membership
508
+ # copy.eql? input #=> true, same string value
509
+ # copy.equal? input #=> false, different objects
510
+ #
511
+ # copy.normalize!
512
+ # copy.valid_string #=> "1:10,1024,2048"
513
+ # copy == input #=> true, same set membership
514
+ # copy.eql? input #=> false, different string value
515
+ #
516
+ # copy << 999
517
+ # copy.valid_string #=> "1:10,999,1024,2048"
518
+ # copy == input #=> false, different set membership
519
+ # copy.eql? input #=> false, different string value
520
+ #
521
+ # === Alternative set creation methods
522
+ #
523
+ # * ::[] returns a frozen validated (non-empty) SequenceSet, without
524
+ # allocating a new object when the input is already a valid frozen
525
+ # SequenceSet.
526
+ # * Net::IMAP::SequenceSet() coerces an input to SequenceSet, without
527
+ # allocating a new object when the input is already a SequenceSet.
528
+ # * ::try_convert calls +to_sequence_set+ on inputs that support it and
529
+ # returns +nil+ for inputs that don't.
530
+ # * ::empty and ::full both return frozen singleton sets which can be
531
+ # combined with set operations (#|, #&, #^, #-, etc) to make new sets.
532
+ #
533
+ # See SequenceSet@Creating+sequence+sets.
383
534
  def initialize(input = nil) input ? replace(input) : clear end
384
535
 
385
536
  # Removes all elements and returns self.
386
- def clear; @tuples, @string = [], nil; self end
537
+ def clear
538
+ modifying! # redundant check, to normalize the error message for JRuby
539
+ @tuples, @string = [], nil
540
+ self
541
+ end
387
542
 
388
543
  # Replace the contents of the set with the contents of +other+ and returns
389
544
  # +self+.
390
545
  #
391
- # +other+ may be another SequenceSet, or it may be an IMAP +sequence-set+
392
- # string, a number, a range, <tt>*</tt>, or an enumerable of these.
546
+ # +other+ may be another SequenceSet or any other object that would be
547
+ # accepted by ::new.
393
548
  def replace(other)
394
549
  case other
395
- when SequenceSet then initialize_dup(other)
550
+ when SequenceSet then
551
+ modifying! # short circuit before doing any work
552
+ @tuples = other.deep_copy_tuples
553
+ @string = other.instance_variable_get(:@string)
396
554
  when String then self.string = other
397
555
  else clear; merge other
398
556
  end
@@ -421,36 +579,40 @@ module Net
421
579
  # If the set was created from a single string, it is not normalized. If
422
580
  # the set is updated the string will be normalized.
423
581
  #
424
- # Related: #valid_string, #normalized_string, #to_s
582
+ # Related: #valid_string, #normalized_string, #to_s, #inspect
425
583
  def string; @string ||= normalized_string if valid? end
426
584
 
427
585
  # Returns an array with #normalized_string when valid and an empty array
428
586
  # otherwise.
429
587
  def deconstruct; valid? ? [normalized_string] : [] end
430
588
 
431
- # Assigns a new string to #string and resets #elements to match. It
432
- # cannot be set to an empty string—assign +nil+ or use #clear instead.
433
- # The string is validated but not normalized.
589
+ # Assigns a new string to #string and resets #elements to match.
590
+ # Assigning +nil+ or an empty string are equivalent to calling #clear.
591
+ #
592
+ # Non-empty strings are validated but not normalized.
434
593
  #
435
- # Use #add or #merge to add a string to an existing set.
594
+ # Use #add, #merge, or #append to add a string to an existing set.
436
595
  #
437
596
  # Related: #replace, #clear
438
- def string=(str)
439
- if str.nil?
597
+ def string=(input)
598
+ if input.nil?
440
599
  clear
441
- else
442
- str = String.try_convert(str) or raise ArgumentError, "not a string"
600
+ elsif (str = String.try_convert(input))
601
+ modifying! # short-circuit before parsing the string
443
602
  tuples = str_to_tuples str
444
603
  @tuples, @string = [], -str
445
604
  tuples_add tuples
605
+ else
606
+ raise ArgumentError, "expected a string or nil, got #{input.class}"
446
607
  end
608
+ str
447
609
  end
448
610
 
449
611
  # Returns the \IMAP +sequence-set+ string representation, or an empty
450
612
  # string when the set is empty. Note that an empty set is invalid in the
451
613
  # \IMAP syntax.
452
614
  #
453
- # Related: #valid_string, #normalized_string, #to_s
615
+ # Related: #string, #valid_string, #normalized_string, #inspect
454
616
  def to_s; string || "" end
455
617
 
456
618
  # Freezes and returns the set. A frozen SequenceSet is Ractor-safe.
@@ -503,8 +665,9 @@ module Net
503
665
 
504
666
  # :call-seq: self === other -> true | false | nil
505
667
  #
506
- # Returns whether +other+ is contained within the set. Returns +nil+ if a
507
- # StandardError is raised while converting +other+ to a comparable type.
668
+ # Returns whether +other+ is contained within the set. +other+ may be any
669
+ # object that would be accepted by ::new. Returns +nil+ if StandardError
670
+ # is raised while converting +other+ to a comparable type.
508
671
  #
509
672
  # Related: #cover?, #include?, #include_star?
510
673
  def ===(other)
@@ -518,12 +681,12 @@ module Net
518
681
  # Returns whether +other+ is contained within the set. +other+ may be any
519
682
  # object that would be accepted by ::new.
520
683
  #
521
- # Related: #===, #include?, #include_star?
684
+ # Related: #===, #include?, #include_star?, #intersect?
522
685
  def cover?(other) input_to_tuples(other).none? { !include_tuple?(_1) } end
523
686
 
524
687
  # Returns +true+ when a given number or range is in +self+, and +false+
525
- # otherwise. Returns +false+ unless +number+ is an Integer, Range, or
526
- # <tt>*</tt>.
688
+ # otherwise. Returns +nil+ when +number+ isn't a valid SequenceSet
689
+ # element (Integer, Range, <tt>*</tt>, +sequence-set+ string).
527
690
  #
528
691
  # set = Net::IMAP::SequenceSet["5:10,100,111:115"]
529
692
  # set.include? 1 #=> false
@@ -531,8 +694,8 @@ module Net
531
694
  # set.include? 11..20 #=> false
532
695
  # set.include? 100 #=> true
533
696
  # set.include? 6 #=> true, covered by "5:10"
534
- # set.include? 4..9 #=> true, covered by "5:10"
535
- # set.include? "4:9" #=> true, strings are parsed
697
+ # set.include? 6..9 #=> true, covered by "5:10"
698
+ # set.include? "6:9" #=> true, strings are parsed
536
699
  # set.include? 4..9 #=> false, intersection is not sufficient
537
700
  # set.include? "*" #=> false, use #limit to re-interpret "*"
538
701
  # set.include? -1 #=> false, -1 is interpreted as "*"
@@ -541,11 +704,14 @@ module Net
541
704
  # set.include? :* #=> true
542
705
  # set.include? "*" #=> true
543
706
  # set.include? -1 #=> true
544
- # set.include? 200.. #=> true
545
- # set.include? 100.. #=> false
707
+ # set.include?(200..) #=> true
708
+ # set.include?(100..) #=> false
546
709
  #
547
- # Related: #include_star?, #cover?, #===
548
- def include?(element) include_tuple? input_to_tuple element end
710
+ # Related: #include_star?, #cover?, #===, #intersect?
711
+ def include?(element)
712
+ tuple = input_to_tuple element rescue nil
713
+ !!include_tuple?(tuple) if tuple
714
+ end
549
715
 
550
716
  alias member? include?
551
717
 
@@ -558,7 +724,7 @@ module Net
558
724
  # Net::IMAP::SequenceSet["5:10"].intersect? "7,9,11" #=> true
559
725
  # Net::IMAP::SequenceSet["5:10"].intersect? "11:33" #=> false
560
726
  #
561
- # Related: #intersection, #disjoint?
727
+ # Related: #intersection, #disjoint?, #cover?, #include?
562
728
  def intersect?(other)
563
729
  valid? && input_to_tuples(other).any? { intersect_tuple? _1 }
564
730
  end
@@ -577,7 +743,7 @@ module Net
577
743
 
578
744
  # :call-seq:
579
745
  # max(star: :*) => integer or star or nil
580
- # max(count, star: :*) => SequenceSet
746
+ # max(count) => SequenceSet
581
747
  #
582
748
  # Returns the maximum value in +self+, +star+ when the set includes
583
749
  # <tt>*</tt>, or +nil+ when the set is empty.
@@ -597,7 +763,7 @@ module Net
597
763
 
598
764
  # :call-seq:
599
765
  # min(star: :*) => integer or star or nil
600
- # min(count, star: :*) => SequenceSet
766
+ # min(count) => SequenceSet
601
767
  #
602
768
  # Returns the minimum value in +self+, +star+ when the only value in the
603
769
  # set is <tt>*</tt>, or +nil+ when the set is empty.
@@ -615,10 +781,11 @@ module Net
615
781
  end
616
782
  end
617
783
 
618
- # :call-seq: minmax(star: :*) => nil or [integer, integer or star]
784
+ # :call-seq: minmax(star: :*) => [min, max] or nil
619
785
  #
620
786
  # Returns a 2-element array containing the minimum and maximum numbers in
621
- # +self+, or +nil+ when the set is empty.
787
+ # +self+, or +nil+ when the set is empty. +star+ is handled the same way
788
+ # as by #min and #max.
622
789
  #
623
790
  # Related: #min, #max
624
791
  def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end
@@ -640,9 +807,7 @@ module Net
640
807
  # Returns a new sequence set that has every number in the +other+ object
641
808
  # added.
642
809
  #
643
- # +other+ may be any object that would be accepted by ::new: a non-zero 32
644
- # bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
645
- # another sequence set, or an enumerable containing any of these.
810
+ # +other+ may be any object that would be accepted by ::new.
646
811
  #
647
812
  # Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
648
813
  # #=> Net::IMAP::SequenceSet["1:6,99"]
@@ -666,9 +831,7 @@ module Net
666
831
  # Returns a new sequence set built by duplicating this set and removing
667
832
  # every number that appears in +other+.
668
833
  #
669
- # +other+ may be any object that would be accepted by ::new: a non-zero 32
670
- # bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
671
- # another sequence set, or an enumerable containing any of these.
834
+ # +other+ may be any object that would be accepted by ::new.
672
835
  #
673
836
  # Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
674
837
  # #=> Net::IMAP::SequenceSet["1,3,5"]
@@ -678,7 +841,7 @@ module Net
678
841
  # ==== Set identities
679
842
  #
680
843
  # <tt>lhs - rhs</tt> is equivalent to:
681
- # * <tt>~r - ~l</tt>
844
+ # * <tt>~rhs - ~lhs</tt>
682
845
  # * <tt>lhs & ~rhs</tt>
683
846
  # * <tt>~(~lhs | rhs)</tt>
684
847
  # * <tt>lhs & (lhs ^ rhs)</tt>
@@ -694,9 +857,7 @@ module Net
694
857
  # Returns a new sequence set containing only the numbers common to this
695
858
  # set and +other+.
696
859
  #
697
- # +other+ may be any object that would be accepted by ::new: a non-zero 32
698
- # bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
699
- # another sequence set, or an enumerable containing any of these.
860
+ # +other+ may be any object that would be accepted by ::new.
700
861
  #
701
862
  # Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
702
863
  # #=> Net::IMAP::SequenceSet["2,4"]
@@ -724,9 +885,7 @@ module Net
724
885
  # Returns a new sequence set containing numbers that are exclusive between
725
886
  # this set and +other+.
726
887
  #
727
- # +other+ may be any object that would be accepted by ::new: a non-zero 32
728
- # bit unsigned integer, range, <tt>sequence-set</tt> formatted string,
729
- # another sequence set, or an enumerable containing any of these.
888
+ # +other+ may be any object that would be accepted by ::new.
730
889
  #
731
890
  # Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
732
891
  # #=> Net::IMAP::SequenceSet["1,3,5:6"]
@@ -776,10 +935,11 @@ module Net
776
935
  # #string will be regenerated. Use #merge to add many elements at once.
777
936
  #
778
937
  # Use #append to append new elements to #string. See
779
- # Net::IMAP@Ordered+and+Normalized+Sets.
938
+ # SequenceSet@Ordered+and+Normalized+sets.
780
939
  #
781
940
  # Related: #add?, #merge, #union, #append
782
941
  def add(element)
942
+ modifying! # short-circuit before input_to_tuple
783
943
  tuple_add input_to_tuple element
784
944
  normalize!
785
945
  end
@@ -790,11 +950,11 @@ module Net
790
950
  # Unlike #add, #merge, or #union, the new value is appended to #string.
791
951
  # This may result in a #string which has duplicates or is out-of-order.
792
952
  #
793
- # See Net::IMAP@Ordered+and+Normalized+Sets.
953
+ # See SequenceSet@Ordered+and+Normalized+sets.
794
954
  #
795
955
  # Related: #add, #merge, #union
796
956
  def append(entry)
797
- modifying!
957
+ modifying! # short-circuit before input_to_tuple
798
958
  tuple = input_to_tuple entry
799
959
  entry = tuple_to_str tuple
800
960
  string unless empty? # write @string before tuple_add
@@ -812,6 +972,7 @@ module Net
812
972
  #
813
973
  # Related: #add, #merge, #union, #include?
814
974
  def add?(element)
975
+ modifying! # short-circuit before include?
815
976
  add element unless include? element
816
977
  end
817
978
 
@@ -824,6 +985,7 @@ module Net
824
985
  #
825
986
  # Related: #delete?, #delete_at, #subtract, #difference
826
987
  def delete(element)
988
+ modifying! # short-circuit before input_to_tuple
827
989
  tuple_subtract input_to_tuple element
828
990
  normalize!
829
991
  end
@@ -861,6 +1023,7 @@ module Net
861
1023
  #
862
1024
  # Related: #delete, #delete_at, #subtract, #difference, #disjoint?
863
1025
  def delete?(element)
1026
+ modifying! # short-circuit before input_to_tuple
864
1027
  tuple = input_to_tuple element
865
1028
  if tuple.first == tuple.last
866
1029
  return unless include_tuple? tuple
@@ -901,6 +1064,7 @@ module Net
901
1064
  #
902
1065
  # Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference
903
1066
  def slice!(index, length = nil)
1067
+ modifying! # short-circuit before slice
904
1068
  deleted = slice(index, length) and subtract deleted
905
1069
  deleted
906
1070
  end
@@ -908,14 +1072,13 @@ module Net
908
1072
  # Merges all of the elements that appear in any of the +sets+ into the
909
1073
  # set, and returns +self+.
910
1074
  #
911
- # The +sets+ may be any objects that would be accepted by ::new: non-zero
912
- # 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
913
- # strings, other sequence sets, or enumerables containing any of these.
1075
+ # The +sets+ may be any objects that would be accepted by ::new.
914
1076
  #
915
1077
  # #string will be regenerated after all sets have been merged.
916
1078
  #
917
1079
  # Related: #add, #add?, #union
918
1080
  def merge(*sets)
1081
+ modifying! # short-circuit before input_to_tuples
919
1082
  tuples_add input_to_tuples sets
920
1083
  normalize!
921
1084
  end
@@ -923,9 +1086,7 @@ module Net
923
1086
  # Removes all of the elements that appear in any of the given +sets+ from
924
1087
  # the set, and returns +self+.
925
1088
  #
926
- # The +sets+ may be any objects that would be accepted by ::new: non-zero
927
- # 32 bit unsigned integers, ranges, <tt>sequence-set</tt> formatted
928
- # strings, other sequence sets, or enumerables containing any of these.
1089
+ # The +sets+ may be any objects that would be accepted by ::new.
929
1090
  #
930
1091
  # Related: #difference
931
1092
  def subtract(*sets)
@@ -941,7 +1102,7 @@ module Net
941
1102
  # This is useful when the given order is significant, for example in a
942
1103
  # ESEARCH response to IMAP#sort.
943
1104
  #
944
- # See Net::IMAP@Ordered+and+Normalized+Sets.
1105
+ # See SequenceSet@Ordered+and+Normalized+sets.
945
1106
  #
946
1107
  # Related: #each_entry, #elements
947
1108
  def entries; each_entry.to_a end
@@ -950,7 +1111,7 @@ module Net
950
1111
  #
951
1112
  # The returned elements are sorted and coalesced, even when the input
952
1113
  # #string is not. <tt>*</tt> will sort last. See #normalize,
953
- # Net::IMAP@Ordered+and+Normalized+Sets.
1114
+ # SequenceSet@Ordered+and+Normalized+sets.
954
1115
  #
955
1116
  # By itself, <tt>*</tt> translates to <tt>:*</tt>. A range containing
956
1117
  # <tt>*</tt> translates to an endless range. Use #limit to translate both
@@ -967,7 +1128,7 @@ module Net
967
1128
  #
968
1129
  # The returned elements are sorted and coalesced, even when the input
969
1130
  # #string is not. <tt>*</tt> will sort last. See #normalize,
970
- # Net::IMAP@Ordered+and+Normalized+Sets.
1131
+ # SequenceSet@Ordered+and+Normalized+sets.
971
1132
  #
972
1133
  # <tt>*</tt> translates to an endless range. By itself, <tt>*</tt>
973
1134
  # translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
@@ -984,7 +1145,7 @@ module Net
984
1145
  # Returns a sorted array of all of the number values in the sequence set.
985
1146
  #
986
1147
  # The returned numbers are sorted and de-duplicated, even when the input
987
- # #string is not. See #normalize, Net::IMAP@Ordered+and+Normalized+Sets.
1148
+ # #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
988
1149
  #
989
1150
  # Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
990
1151
  # #=> [2, 5, 6, 7, 8, 9, 11, 12]
@@ -1016,7 +1177,7 @@ module Net
1016
1177
  # no sorting, deduplication, or coalescing. When #string is in its
1017
1178
  # normalized form, this will yield the same values as #each_element.
1018
1179
  #
1019
- # See Net::IMAP@Ordered+and+Normalized+Sets.
1180
+ # See SequenceSet@Ordered+and+Normalized+sets.
1020
1181
  #
1021
1182
  # Related: #entries, #each_element
1022
1183
  def each_entry(&block) # :yields: integer or range or :*
@@ -1028,7 +1189,7 @@ module Net
1028
1189
  # and returns self. Returns an enumerator when called without a block.
1029
1190
  #
1030
1191
  # The returned numbers are sorted and de-duplicated, even when the input
1031
- # #string is not. See #normalize, Net::IMAP@Ordered+and+Normalized+Sets.
1192
+ # #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
1032
1193
  #
1033
1194
  # Related: #elements, #each_entry
1034
1195
  def each_element # :yields: integer or range or :*
@@ -1424,6 +1585,7 @@ module Net
1424
1585
  #
1425
1586
  # Related: #limit
1426
1587
  def limit!(max:)
1588
+ modifying! # short-circuit, and normalize the error message for JRuby
1427
1589
  star = include_star?
1428
1590
  max = to_tuple_int(max)
1429
1591
  tuple_subtract [max + 1, STAR_INT]
@@ -1438,6 +1600,7 @@ module Net
1438
1600
  #
1439
1601
  # Related: #complement
1440
1602
  def complement!
1603
+ modifying! # short-circuit, and normalize the error message for JRuby
1441
1604
  return replace(self.class.full) if empty?
1442
1605
  return clear if full?
1443
1606
  flat = @tuples.flat_map { [_1 - 1, _2 + 1] }
@@ -1451,7 +1614,7 @@ module Net
1451
1614
  #
1452
1615
  # The returned set's #string is sorted and deduplicated. Adjacent or
1453
1616
  # overlapping elements will be merged into a single larger range.
1454
- # See Net::IMAP@Ordered+and+Normalized+Sets.
1617
+ # See SequenceSet@Ordered+and+Normalized+sets.
1455
1618
  #
1456
1619
  # Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
1457
1620
  # #=> Net::IMAP::SequenceSet["1:7,9:11"]
@@ -1464,39 +1627,87 @@ module Net
1464
1627
  end
1465
1628
 
1466
1629
  # Resets #string to be sorted, deduplicated, and coalesced. Returns
1467
- # +self+. See Net::IMAP@Ordered+and+Normalized+Sets.
1630
+ # +self+. See SequenceSet@Ordered+and+Normalized+sets.
1468
1631
  #
1469
1632
  # Related: #normalize, #normalized_string
1470
1633
  def normalize!
1634
+ modifying! # redundant check, to normalize the error message for JRuby
1471
1635
  @string = nil
1472
1636
  self
1473
1637
  end
1474
1638
 
1475
1639
  # Returns a normalized +sequence-set+ string representation, sorted
1476
1640
  # and deduplicated. Adjacent or overlapping elements will be merged into
1477
- # a single larger range. See Net::IMAP@Ordered+and+Normalized+Sets.
1641
+ # a single larger range. See SequenceSet@Ordered+and+Normalized+sets.
1478
1642
  #
1479
1643
  # Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
1480
1644
  # #=> "1:7,9:11"
1481
1645
  #
1482
1646
  # Returns +nil+ when the set is empty.
1483
1647
  #
1484
- # Related: #normalize!, #normalize
1648
+ # Related: #normalize!, #normalize, #string, #to_s
1485
1649
  def normalized_string
1486
1650
  @tuples.empty? ? nil : -@tuples.map { tuple_to_str _1 }.join(",")
1487
1651
  end
1488
1652
 
1653
+ # Returns an inspection string for the SequenceSet.
1654
+ #
1655
+ # Net::IMAP::SequenceSet.new.inspect
1656
+ # #=> "Net::IMAP::SequenceSet()"
1657
+ #
1658
+ # Net::IMAP::SequenceSet(1..5, 1024, 15, 2000).inspect
1659
+ # #=> 'Net::IMAP::SequenceSet("1:5,15,1024,2000")'
1660
+ #
1661
+ # Frozen sets have slightly different output:
1662
+ #
1663
+ # Net::IMAP::SequenceSet.empty.inspect
1664
+ # #=> "Net::IMAP::SequenceSet.empty"
1665
+ #
1666
+ # Net::IMAP::SequenceSet[1..5, 1024, 15, 2000].inspect
1667
+ # #=> 'Net::IMAP::SequenceSet["1:5,15,1024,2000"]'
1668
+ #
1669
+ # Large sets (by number of #entries) have abridged output, with only the
1670
+ # first and last entries:
1671
+ #
1672
+ # Net::IMAP::SequenceSet(((1..5000) % 2).to_a).inspect
1673
+ # #=> #<Net::IMAP::SequenceSet 2500 entries "1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,...(2468 entries omitted)...,4969,4971,4973,4975,4977,4979,4981,4983,4985,4987,4989,4991,4993,4995,4997,4999">
1674
+ #
1675
+ # Related: #to_s, #string
1489
1676
  def inspect
1490
- if empty?
1491
- (frozen? ? "%s.empty" : "#<%s empty>") % [self.class]
1492
- elsif frozen?
1493
- "%s[%p]" % [self.class, to_s]
1677
+ case (count = count_entries)
1678
+ when 0
1679
+ (frozen? ? "%s.empty" : "%s()") % [self.class]
1680
+ when ..INSPECT_MAX_LEN
1681
+ (frozen? ? "%s[%p]" : "%s(%p)") % [self.class, to_s]
1494
1682
  else
1495
- "#<%s %p>" % [self.class, to_s]
1683
+ if @string
1684
+ head = @string[INSPECT_ABRIDGED_HEAD_RE]
1685
+ tail = @string[INSPECT_ABRIDGED_TAIL_RE]
1686
+ else
1687
+ head = export_string_entries(@tuples.first(INSPECT_TRUNCATE_LEN)) + ","
1688
+ tail = "," + export_string_entries(@tuples.last(INSPECT_TRUNCATE_LEN))
1689
+ end
1690
+ '#<%s %d entries "%s...(%d entries omitted)...%s"%s>' % [
1691
+ self.class, count,
1692
+ head, count - INSPECT_TRUNCATE_LEN * 2, tail,
1693
+ frozen? ? " (frozen)" : "",
1694
+ ]
1496
1695
  end
1497
1696
  end
1498
1697
 
1499
- # Returns self
1698
+ private def count_entries
1699
+ @string ? @string.count(",") + 1 : @tuples.count
1700
+ end
1701
+
1702
+ ##
1703
+ # :method: to_sequence_set
1704
+ # :call-seq: to_sequence_set -> self
1705
+ #
1706
+ # Returns +self+
1707
+ #
1708
+ # Related: ::try_convert
1709
+
1710
+ # :nodoc: (work around rdoc bug)
1500
1711
  alias to_sequence_set itself
1501
1712
 
1502
1713
  # Unstable API: currently for internal use only (Net::IMAP#validate_data)
@@ -1526,6 +1737,8 @@ module Net
1526
1737
 
1527
1738
  attr_reader :tuples # :nodoc:
1528
1739
 
1740
+ def deep_copy_tuples; @tuples.map { _1.dup } end # :nodoc:
1741
+
1529
1742
  private
1530
1743
 
1531
1744
  def remain_frozen(set) frozen? ? set.freeze : set end
@@ -1533,12 +1746,12 @@ module Net
1533
1746
 
1534
1747
  # frozen clones are shallow copied
1535
1748
  def initialize_clone(other)
1536
- other.frozen? ? super : initialize_dup(other)
1749
+ @tuples = other.deep_copy_tuples unless other.frozen?
1750
+ super
1537
1751
  end
1538
1752
 
1539
1753
  def initialize_dup(other)
1540
- @tuples = other.tuples.map(&:dup)
1541
- @string = other.string&.-@
1754
+ @tuples = other.deep_copy_tuples
1542
1755
  super
1543
1756
  end
1544
1757
 
@@ -1563,9 +1776,8 @@ module Net
1563
1776
  when Array then set.flat_map { input_to_tuples _1 }
1564
1777
  when nil then []
1565
1778
  else
1566
- raise DataFormatError,
1567
- "expected nz-number, range, string, or enumerable; " \
1568
- "got %p" % [set]
1779
+ raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
1780
+ "got %p" % [set]
1569
1781
  end
1570
1782
  end
1571
1783
 
@@ -1591,6 +1803,10 @@ module Net
1591
1803
  def to_tuple_int(obj) STARS.include?(obj) ? STAR_INT : nz_number(obj) end
1592
1804
  def from_tuple_int(num) num == STAR_INT ? :* : num end
1593
1805
 
1806
+ def export_string_entries(entries)
1807
+ -entries.map { tuple_to_str _1 }.join(",")
1808
+ end
1809
+
1594
1810
  def tuple_to_str(tuple) tuple.uniq.map{ from_tuple_int _1 }.join(":") end
1595
1811
  def str_to_tuples(str) str.split(",", -1).map! { str_to_tuple _1 } end
1596
1812
  def str_to_tuple(str)
data/lib/net/imap.rb CHANGED
@@ -788,7 +788,7 @@ module Net
788
788
  # * {IMAP URLAUTH Authorization Mechanism Registry}[https://www.iana.org/assignments/urlauth-authorization-mechanism-registry/urlauth-authorization-mechanism-registry.xhtml]
789
789
  #
790
790
  class IMAP < Protocol
791
- VERSION = "0.5.8"
791
+ VERSION = "0.5.10"
792
792
 
793
793
  # Aliases for supported capabilities, to be used with the #enable command.
794
794
  ENABLE_ALIASES = {
@@ -801,6 +801,7 @@ module Net
801
801
  autoload :ResponseReader, "#{dir}/response_reader"
802
802
  autoload :SASL, "#{dir}/sasl"
803
803
  autoload :SASLAdapter, "#{dir}/sasl_adapter"
804
+ autoload :SequenceSet, "#{dir}/sequence_set"
804
805
  autoload :StringPrep, "#{dir}/stringprep"
805
806
 
806
807
  include MonitorMixin
@@ -809,6 +810,22 @@ module Net
809
810
  include SSL
810
811
  end
811
812
 
813
+ # :call-seq:
814
+ # Net::IMAP::SequenceSet(set = nil) -> SequenceSet
815
+ #
816
+ # Coerces +set+ into a SequenceSet, using either SequenceSet.try_convert or
817
+ # SequenceSet.new.
818
+ #
819
+ # * When +set+ is a SequenceSet, that same set is returned.
820
+ # * When +set+ responds to +to_sequence_set+, +set.to_sequence_set+ is
821
+ # returned.
822
+ # * Otherwise, returns the result from calling SequenceSet.new with +set+.
823
+ #
824
+ # Related: SequenceSet.try_convert, SequenceSet.new, SequenceSet::[]
825
+ def self.SequenceSet(set = nil)
826
+ SequenceSet.try_convert(set) || SequenceSet.new(set)
827
+ end
828
+
812
829
  # Returns the global Config object
813
830
  def self.config; Config.global end
814
831
 
@@ -1114,28 +1131,27 @@ module Net
1114
1131
 
1115
1132
  # Disconnects from the server.
1116
1133
  #
1134
+ # Waits for receiver thread to close before returning. Slow or stuck
1135
+ # response handlers can cause #disconnect to hang until they complete.
1136
+ #
1117
1137
  # Related: #logout, #logout!
1118
1138
  def disconnect
1139
+ in_logout_state = try_state_logout?
1119
1140
  return if disconnected?
1120
- state_logout!
1121
1141
  begin
1122
- begin
1123
- # try to call SSL::SSLSocket#io.
1124
- @sock.io.shutdown
1125
- rescue NoMethodError
1126
- # @sock is not an SSL::SSLSocket.
1127
- @sock.shutdown
1128
- end
1142
+ @sock.to_io.shutdown
1129
1143
  rescue Errno::ENOTCONN
1130
1144
  # ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
1131
1145
  rescue Exception => e
1132
1146
  @receiver_thread.raise(e)
1133
1147
  end
1148
+ @sock.close
1134
1149
  @receiver_thread.join
1135
- synchronize do
1136
- @sock.close
1137
- end
1138
1150
  raise e if e
1151
+ ensure
1152
+ # Try again after shutting down the receiver thread. With no reciever
1153
+ # left to wait for, any remaining locks should be _very_ brief.
1154
+ state_logout! unless in_logout_state
1139
1155
  end
1140
1156
 
1141
1157
  # Returns true if disconnected from the server.
@@ -3062,8 +3078,8 @@ module Net
3062
3078
  raise @exception || Net::IMAP::Error.new("connection closed")
3063
3079
  end
3064
3080
  ensure
3081
+ remove_response_handler(response_handler)
3065
3082
  unless @receiver_thread_terminating
3066
- remove_response_handler(response_handler)
3067
3083
  put_string("DONE#{CRLF}")
3068
3084
  response = get_tagged_response(tag, "IDLE", idle_response_timeout)
3069
3085
  end
@@ -3346,8 +3362,6 @@ module Net
3346
3362
  rescue Exception => ex
3347
3363
  @receiver_thread_exception = ex
3348
3364
  # don't exit the thread with an exception
3349
- ensure
3350
- state_logout!
3351
3365
  end
3352
3366
  end
3353
3367
 
@@ -3429,6 +3443,8 @@ module Net
3429
3443
  @idle_done_cond.signal
3430
3444
  end
3431
3445
  end
3446
+ ensure
3447
+ state_logout!
3432
3448
  end
3433
3449
 
3434
3450
  def get_tagged_response(tag, cmd, timeout = nil)
@@ -3791,15 +3807,29 @@ module Net
3791
3807
  end
3792
3808
 
3793
3809
  def state_unselected!
3794
- state_authenticated! if connection_state.to_sym == :selected
3810
+ synchronize do
3811
+ state_authenticated! if connection_state.to_sym == :selected
3812
+ end
3795
3813
  end
3796
3814
 
3797
3815
  def state_logout!
3816
+ return true if connection_state in [:logout, *]
3798
3817
  synchronize do
3818
+ return true if connection_state in [:logout, *]
3799
3819
  @connection_state = ConnectionState::Logout.new
3800
3820
  end
3801
3821
  end
3802
3822
 
3823
+ # don't wait to aqcuire the lock
3824
+ def try_state_logout?
3825
+ return true if connection_state in [:logout, *]
3826
+ return false unless acquired_lock = mon_try_enter
3827
+ state_logout!
3828
+ true
3829
+ ensure
3830
+ mon_exit if acquired_lock
3831
+ end
3832
+
3803
3833
  def sasl_adapter
3804
3834
  SASLAdapter.new(self, &method(:send_command_with_continuations))
3805
3835
  end
@@ -388,9 +388,11 @@ class StringPrepTablesGenerator
388
388
  end
389
389
 
390
390
  def asgn_mapping(name, replacement = to_map(tables[name]))
391
+ indent = " " * 2
392
+ replacement = replacement.inspect.gsub(/" => "/, '"=>"')
391
393
  cname = name.tr(?., ?_).upcase
392
- "# Replacements for %s\n%s%s = %p.freeze" % [
393
- "IN_#{name}", " " * 2, "MAP_#{cname}", replacement,
394
+ "# Replacements for %s\n%s%s = %s.freeze" % [
395
+ "IN_#{name}", indent, "MAP_#{cname}", replacement,
394
396
  ]
395
397
  end
396
398
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-imap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.8
4
+ version: 0.5.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
@@ -129,7 +129,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
129
  - !ruby/object:Gem::Version
130
130
  version: '0'
131
131
  requirements: []
132
- rubygems_version: 3.6.7
132
+ rubygems_version: 3.6.9
133
133
  specification_version: 4
134
134
  summary: Ruby client api for Internet Message Access Protocol
135
135
  test_files: []