net-imap 0.5.8 → 0.5.9

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: 9a31378c34762136e5fc341801a0b4073157dc6c35df6aae3254d3f7b90a07b7
4
+ data.tar.gz: cbf787e39ecfde5a7af0061baba54eae3d7b12077679854ea62b335fc9b48798
5
5
  SHA512:
6
- metadata.gz: 80f15f967d4260638d423b802204360e25704cc47c59d110a85f401c1554882521097d5014ecc3e7a0a17f87e8995ae87f553d73a26f3c8abc84744ad5d236ed
7
- data.tar.gz: 2790cbc5ee60b3da3648e8bd91df90ecac7c72e0febca11464400460fd5c34db3123d307f39901ea7c313942f2feb11bb3c0f7dbc06b96094b6d1738426b278a
6
+ metadata.gz: fd0c8a34fa212bb42a55e943bf8184c614256b52da4ac13bdddb48f74c8517f5c7b72addb2153af42d76f2a8bcf42627ceb34e874b7842d8a6dfd542ba577578
7
+ data.tar.gz: 91dd4d1d35582abb9e4fd0df64c63ee5c9320f3404d548bff0a4023e32dc9a6ddd6b90cd1cc221e637a4719f3cc5699d57cc337ea17a6454b3aa7d7be9ffb423
data/Gemfile CHANGED
@@ -14,6 +14,7 @@ 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
18
19
 
19
20
  group :test do
@@ -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.
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
53
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,55,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
110
  # set.valid_string #=> "1:10,55,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>
@@ -337,13 +409,12 @@ module Net
337
409
  # An empty SequenceSet is invalid and will raise a DataFormatError.
338
410
  #
339
411
  # Use ::new to create a mutable or empty SequenceSet.
412
+ #
413
+ # Related: ::new, Net::IMAP::SequenceSet(), ::try_convert
340
414
  def [](first, *rest)
341
415
  if rest.empty?
342
- if first.is_a?(SequenceSet) && first.frozen? && first.valid?
343
- first
344
- else
345
- new(first).validate.freeze
346
- end
416
+ set = try_convert(first)&.validate
417
+ set&.frozen? ? set : (set&.dup || new(first).validate).freeze
347
418
  else
348
419
  new(first).merge(*rest).validate.freeze
349
420
  end
@@ -358,6 +429,8 @@ module Net
358
429
  #
359
430
  # If +obj.to_sequence_set+ doesn't return a SequenceSet, an exception is
360
431
  # raised.
432
+ #
433
+ # Related: Net::IMAP::SequenceSet(), ::new, ::[]
361
434
  def try_convert(obj)
362
435
  return obj if obj.is_a?(SequenceSet)
363
436
  return nil unless obj.respond_to?(:to_sequence_set)
@@ -376,20 +449,85 @@ module Net
376
449
  end
377
450
 
378
451
  # 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.
452
+ # SequenceSet, an IMAP formatted +sequence-set+ string, a non-zero 32 bit
453
+ # unsigned integer, a range, <tt>:*</tt>, a Set of numbers or <tt>*</tt>,
454
+ # an object that responds to +to_sequence_set+ (such as SearchResult) or
455
+ # an Array of these (array inputs may be nested).
456
+ #
457
+ # set = Net::IMAP::SequenceSet.new(1)
458
+ # set.valid_string #=> "1"
459
+ # set = Net::IMAP::SequenceSet.new(1..100)
460
+ # set.valid_string #=> "1:100"
461
+ # set = Net::IMAP::SequenceSet.new(1...100)
462
+ # set.valid_string #=> "1:99"
463
+ # set = Net::IMAP::SequenceSet.new([1, 2, 5..])
464
+ # set.valid_string #=> "1:2,5:*"
465
+ # set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
466
+ # set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
467
+ # set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
468
+ # set.valid_string #=> "1:10,55,1024:2048"
469
+ #
470
+ # With no arguments (or +nil+) creates an empty sequence set. Note that
471
+ # an empty sequence set is invalid in the \IMAP grammar.
472
+ #
473
+ # set = Net::IMAP::SequenceSet.new
474
+ # set.empty? #=> true
475
+ # set.valid? #=> false
476
+ # set.valid_string #!> raises DataFormatError
477
+ # set << 1..10
478
+ # set.empty? #=> false
479
+ # set.valid? #=> true
480
+ # set.valid_string #=> "1:10"
481
+ #
482
+ # When +input+ is a SequenceSet, ::new behaves the same as calling #dup on
483
+ # that other set. The input's #string will be preserved.
484
+ #
485
+ # input = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
486
+ # copy = Net::IMAP::SequenceSet.new(input)
487
+ # input.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
488
+ # copy.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
489
+ # copy2 = input.dup # same as calling new with a SequenceSet input
490
+ # copy == input #=> true, same set membership
491
+ # copy.eql? input #=> true, same string value
492
+ # copy.equal? input #=> false, different objects
493
+ #
494
+ # copy.normalize!
495
+ # copy.valid_string #=> "1:10,1024,2048"
496
+ # copy == input #=> true, same set membership
497
+ # copy.eql? input #=> false, different string value
498
+ #
499
+ # copy << 999
500
+ # copy.valid_string #=> "1:10,999,1024,2048"
501
+ # copy == input #=> false, different set membership
502
+ # copy.eql? input #=> false, different string value
503
+ #
504
+ # === Alternative set creation methods
505
+ #
506
+ # * ::[] returns a frozen validated (non-empty) SequenceSet, without
507
+ # allocating a new object when the input is already a valid frozen
508
+ # SequenceSet.
509
+ # * Net::IMAP::SequenceSet() coerces an input to SequenceSet, without
510
+ # allocating a new object when the input is already a SequenceSet.
511
+ # * ::try_convert calls +to_sequence_set+ on inputs that support it and
512
+ # returns +nil+ for inputs that don't.
513
+ # * ::empty and ::full both return frozen singleton sets which can be
514
+ # combined with set operations (#|, #&, #^, #-, etc) to make new sets.
515
+ #
516
+ # See SequenceSet@Creating+sequence+sets.
383
517
  def initialize(input = nil) input ? replace(input) : clear end
384
518
 
385
519
  # Removes all elements and returns self.
386
- def clear; @tuples, @string = [], nil; self end
520
+ def clear
521
+ modifying! # redundant check, to normalize the error message for JRuby
522
+ @tuples, @string = [], nil
523
+ self
524
+ end
387
525
 
388
526
  # Replace the contents of the set with the contents of +other+ and returns
389
527
  # +self+.
390
528
  #
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.
529
+ # +other+ may be another SequenceSet or any other object that would be
530
+ # accepted by ::new.
393
531
  def replace(other)
394
532
  case other
395
533
  when SequenceSet then initialize_dup(other)
@@ -439,11 +577,13 @@ module Net
439
577
  if str.nil?
440
578
  clear
441
579
  else
580
+ modifying! # redundant check, to normalize the error message for JRuby
442
581
  str = String.try_convert(str) or raise ArgumentError, "not a string"
443
582
  tuples = str_to_tuples str
444
583
  @tuples, @string = [], -str
445
584
  tuples_add tuples
446
585
  end
586
+ str
447
587
  end
448
588
 
449
589
  # Returns the \IMAP +sequence-set+ string representation, or an empty
@@ -503,8 +643,9 @@ module Net
503
643
 
504
644
  # :call-seq: self === other -> true | false | nil
505
645
  #
506
- # Returns whether +other+ is contained within the set. Returns +nil+ if a
507
- # StandardError is raised while converting +other+ to a comparable type.
646
+ # Returns whether +other+ is contained within the set. +other+ may be any
647
+ # object that would be accepted by ::new. Returns +nil+ if StandardError
648
+ # is raised while converting +other+ to a comparable type.
508
649
  #
509
650
  # Related: #cover?, #include?, #include_star?
510
651
  def ===(other)
@@ -518,12 +659,12 @@ module Net
518
659
  # Returns whether +other+ is contained within the set. +other+ may be any
519
660
  # object that would be accepted by ::new.
520
661
  #
521
- # Related: #===, #include?, #include_star?
662
+ # Related: #===, #include?, #include_star?, #intersect?
522
663
  def cover?(other) input_to_tuples(other).none? { !include_tuple?(_1) } end
523
664
 
524
665
  # 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>.
666
+ # otherwise. Returns +nil+ when +number+ isn't a valid SequenceSet
667
+ # element (Integer, Range, <tt>*</tt>, +sequence-set+ string).
527
668
  #
528
669
  # set = Net::IMAP::SequenceSet["5:10,100,111:115"]
529
670
  # set.include? 1 #=> false
@@ -531,8 +672,8 @@ module Net
531
672
  # set.include? 11..20 #=> false
532
673
  # set.include? 100 #=> true
533
674
  # 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
675
+ # set.include? 6..9 #=> true, covered by "5:10"
676
+ # set.include? "6:9" #=> true, strings are parsed
536
677
  # set.include? 4..9 #=> false, intersection is not sufficient
537
678
  # set.include? "*" #=> false, use #limit to re-interpret "*"
538
679
  # set.include? -1 #=> false, -1 is interpreted as "*"
@@ -541,11 +682,14 @@ module Net
541
682
  # set.include? :* #=> true
542
683
  # set.include? "*" #=> true
543
684
  # set.include? -1 #=> true
544
- # set.include? 200.. #=> true
545
- # set.include? 100.. #=> false
685
+ # set.include?(200..) #=> true
686
+ # set.include?(100..) #=> false
546
687
  #
547
- # Related: #include_star?, #cover?, #===
548
- def include?(element) include_tuple? input_to_tuple element end
688
+ # Related: #include_star?, #cover?, #===, #intersect?
689
+ def include?(element)
690
+ tuple = input_to_tuple element rescue nil
691
+ !!include_tuple?(tuple) if tuple
692
+ end
549
693
 
550
694
  alias member? include?
551
695
 
@@ -558,7 +702,7 @@ module Net
558
702
  # Net::IMAP::SequenceSet["5:10"].intersect? "7,9,11" #=> true
559
703
  # Net::IMAP::SequenceSet["5:10"].intersect? "11:33" #=> false
560
704
  #
561
- # Related: #intersection, #disjoint?
705
+ # Related: #intersection, #disjoint?, #cover?, #include?
562
706
  def intersect?(other)
563
707
  valid? && input_to_tuples(other).any? { intersect_tuple? _1 }
564
708
  end
@@ -577,7 +721,7 @@ module Net
577
721
 
578
722
  # :call-seq:
579
723
  # max(star: :*) => integer or star or nil
580
- # max(count, star: :*) => SequenceSet
724
+ # max(count) => SequenceSet
581
725
  #
582
726
  # Returns the maximum value in +self+, +star+ when the set includes
583
727
  # <tt>*</tt>, or +nil+ when the set is empty.
@@ -597,7 +741,7 @@ module Net
597
741
 
598
742
  # :call-seq:
599
743
  # min(star: :*) => integer or star or nil
600
- # min(count, star: :*) => SequenceSet
744
+ # min(count) => SequenceSet
601
745
  #
602
746
  # Returns the minimum value in +self+, +star+ when the only value in the
603
747
  # set is <tt>*</tt>, or +nil+ when the set is empty.
@@ -615,10 +759,11 @@ module Net
615
759
  end
616
760
  end
617
761
 
618
- # :call-seq: minmax(star: :*) => nil or [integer, integer or star]
762
+ # :call-seq: minmax(star: :*) => [min, max] or nil
619
763
  #
620
764
  # Returns a 2-element array containing the minimum and maximum numbers in
621
- # +self+, or +nil+ when the set is empty.
765
+ # +self+, or +nil+ when the set is empty. +star+ is handled the same way
766
+ # as by #min and #max.
622
767
  #
623
768
  # Related: #min, #max
624
769
  def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end
@@ -640,9 +785,7 @@ module Net
640
785
  # Returns a new sequence set that has every number in the +other+ object
641
786
  # added.
642
787
  #
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.
788
+ # +other+ may be any object that would be accepted by ::new.
646
789
  #
647
790
  # Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
648
791
  # #=> Net::IMAP::SequenceSet["1:6,99"]
@@ -666,9 +809,7 @@ module Net
666
809
  # Returns a new sequence set built by duplicating this set and removing
667
810
  # every number that appears in +other+.
668
811
  #
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.
812
+ # +other+ may be any object that would be accepted by ::new.
672
813
  #
673
814
  # Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
674
815
  # #=> Net::IMAP::SequenceSet["1,3,5"]
@@ -678,7 +819,7 @@ module Net
678
819
  # ==== Set identities
679
820
  #
680
821
  # <tt>lhs - rhs</tt> is equivalent to:
681
- # * <tt>~r - ~l</tt>
822
+ # * <tt>~rhs - ~lhs</tt>
682
823
  # * <tt>lhs & ~rhs</tt>
683
824
  # * <tt>~(~lhs | rhs)</tt>
684
825
  # * <tt>lhs & (lhs ^ rhs)</tt>
@@ -694,9 +835,7 @@ module Net
694
835
  # Returns a new sequence set containing only the numbers common to this
695
836
  # set and +other+.
696
837
  #
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.
838
+ # +other+ may be any object that would be accepted by ::new.
700
839
  #
701
840
  # Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
702
841
  # #=> Net::IMAP::SequenceSet["2,4"]
@@ -724,9 +863,7 @@ module Net
724
863
  # Returns a new sequence set containing numbers that are exclusive between
725
864
  # this set and +other+.
726
865
  #
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.
866
+ # +other+ may be any object that would be accepted by ::new.
730
867
  #
731
868
  # Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
732
869
  # #=> Net::IMAP::SequenceSet["1,3,5:6"]
@@ -776,10 +913,11 @@ module Net
776
913
  # #string will be regenerated. Use #merge to add many elements at once.
777
914
  #
778
915
  # Use #append to append new elements to #string. See
779
- # Net::IMAP@Ordered+and+Normalized+Sets.
916
+ # SequenceSet@Ordered+and+Normalized+sets.
780
917
  #
781
918
  # Related: #add?, #merge, #union, #append
782
919
  def add(element)
920
+ modifying! # short-circuit before input_to_tuple
783
921
  tuple_add input_to_tuple element
784
922
  normalize!
785
923
  end
@@ -790,11 +928,11 @@ module Net
790
928
  # Unlike #add, #merge, or #union, the new value is appended to #string.
791
929
  # This may result in a #string which has duplicates or is out-of-order.
792
930
  #
793
- # See Net::IMAP@Ordered+and+Normalized+Sets.
931
+ # See SequenceSet@Ordered+and+Normalized+sets.
794
932
  #
795
933
  # Related: #add, #merge, #union
796
934
  def append(entry)
797
- modifying!
935
+ modifying! # short-circuit before input_to_tuple
798
936
  tuple = input_to_tuple entry
799
937
  entry = tuple_to_str tuple
800
938
  string unless empty? # write @string before tuple_add
@@ -812,6 +950,7 @@ module Net
812
950
  #
813
951
  # Related: #add, #merge, #union, #include?
814
952
  def add?(element)
953
+ modifying! # short-circuit before include?
815
954
  add element unless include? element
816
955
  end
817
956
 
@@ -824,6 +963,7 @@ module Net
824
963
  #
825
964
  # Related: #delete?, #delete_at, #subtract, #difference
826
965
  def delete(element)
966
+ modifying! # short-circuit before input_to_tuple
827
967
  tuple_subtract input_to_tuple element
828
968
  normalize!
829
969
  end
@@ -861,6 +1001,7 @@ module Net
861
1001
  #
862
1002
  # Related: #delete, #delete_at, #subtract, #difference, #disjoint?
863
1003
  def delete?(element)
1004
+ modifying! # short-circuit before input_to_tuple
864
1005
  tuple = input_to_tuple element
865
1006
  if tuple.first == tuple.last
866
1007
  return unless include_tuple? tuple
@@ -901,6 +1042,7 @@ module Net
901
1042
  #
902
1043
  # Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference
903
1044
  def slice!(index, length = nil)
1045
+ modifying! # short-circuit before slice
904
1046
  deleted = slice(index, length) and subtract deleted
905
1047
  deleted
906
1048
  end
@@ -908,14 +1050,13 @@ module Net
908
1050
  # Merges all of the elements that appear in any of the +sets+ into the
909
1051
  # set, and returns +self+.
910
1052
  #
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.
1053
+ # The +sets+ may be any objects that would be accepted by ::new.
914
1054
  #
915
1055
  # #string will be regenerated after all sets have been merged.
916
1056
  #
917
1057
  # Related: #add, #add?, #union
918
1058
  def merge(*sets)
1059
+ modifying! # short-circuit before input_to_tuples
919
1060
  tuples_add input_to_tuples sets
920
1061
  normalize!
921
1062
  end
@@ -923,9 +1064,7 @@ module Net
923
1064
  # Removes all of the elements that appear in any of the given +sets+ from
924
1065
  # the set, and returns +self+.
925
1066
  #
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.
1067
+ # The +sets+ may be any objects that would be accepted by ::new.
929
1068
  #
930
1069
  # Related: #difference
931
1070
  def subtract(*sets)
@@ -941,7 +1080,7 @@ module Net
941
1080
  # This is useful when the given order is significant, for example in a
942
1081
  # ESEARCH response to IMAP#sort.
943
1082
  #
944
- # See Net::IMAP@Ordered+and+Normalized+Sets.
1083
+ # See SequenceSet@Ordered+and+Normalized+sets.
945
1084
  #
946
1085
  # Related: #each_entry, #elements
947
1086
  def entries; each_entry.to_a end
@@ -950,7 +1089,7 @@ module Net
950
1089
  #
951
1090
  # The returned elements are sorted and coalesced, even when the input
952
1091
  # #string is not. <tt>*</tt> will sort last. See #normalize,
953
- # Net::IMAP@Ordered+and+Normalized+Sets.
1092
+ # SequenceSet@Ordered+and+Normalized+sets.
954
1093
  #
955
1094
  # By itself, <tt>*</tt> translates to <tt>:*</tt>. A range containing
956
1095
  # <tt>*</tt> translates to an endless range. Use #limit to translate both
@@ -967,7 +1106,7 @@ module Net
967
1106
  #
968
1107
  # The returned elements are sorted and coalesced, even when the input
969
1108
  # #string is not. <tt>*</tt> will sort last. See #normalize,
970
- # Net::IMAP@Ordered+and+Normalized+Sets.
1109
+ # SequenceSet@Ordered+and+Normalized+sets.
971
1110
  #
972
1111
  # <tt>*</tt> translates to an endless range. By itself, <tt>*</tt>
973
1112
  # translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
@@ -984,7 +1123,7 @@ module Net
984
1123
  # Returns a sorted array of all of the number values in the sequence set.
985
1124
  #
986
1125
  # The returned numbers are sorted and de-duplicated, even when the input
987
- # #string is not. See #normalize, Net::IMAP@Ordered+and+Normalized+Sets.
1126
+ # #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
988
1127
  #
989
1128
  # Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
990
1129
  # #=> [2, 5, 6, 7, 8, 9, 11, 12]
@@ -1016,7 +1155,7 @@ module Net
1016
1155
  # no sorting, deduplication, or coalescing. When #string is in its
1017
1156
  # normalized form, this will yield the same values as #each_element.
1018
1157
  #
1019
- # See Net::IMAP@Ordered+and+Normalized+Sets.
1158
+ # See SequenceSet@Ordered+and+Normalized+sets.
1020
1159
  #
1021
1160
  # Related: #entries, #each_element
1022
1161
  def each_entry(&block) # :yields: integer or range or :*
@@ -1028,7 +1167,7 @@ module Net
1028
1167
  # and returns self. Returns an enumerator when called without a block.
1029
1168
  #
1030
1169
  # The returned numbers are sorted and de-duplicated, even when the input
1031
- # #string is not. See #normalize, Net::IMAP@Ordered+and+Normalized+Sets.
1170
+ # #string is not. See #normalize, SequenceSet@Ordered+and+Normalized+sets.
1032
1171
  #
1033
1172
  # Related: #elements, #each_entry
1034
1173
  def each_element # :yields: integer or range or :*
@@ -1424,6 +1563,7 @@ module Net
1424
1563
  #
1425
1564
  # Related: #limit
1426
1565
  def limit!(max:)
1566
+ modifying! # short-circuit, and normalize the error message for JRuby
1427
1567
  star = include_star?
1428
1568
  max = to_tuple_int(max)
1429
1569
  tuple_subtract [max + 1, STAR_INT]
@@ -1438,6 +1578,7 @@ module Net
1438
1578
  #
1439
1579
  # Related: #complement
1440
1580
  def complement!
1581
+ modifying! # short-circuit, and normalize the error message for JRuby
1441
1582
  return replace(self.class.full) if empty?
1442
1583
  return clear if full?
1443
1584
  flat = @tuples.flat_map { [_1 - 1, _2 + 1] }
@@ -1451,7 +1592,7 @@ module Net
1451
1592
  #
1452
1593
  # The returned set's #string is sorted and deduplicated. Adjacent or
1453
1594
  # overlapping elements will be merged into a single larger range.
1454
- # See Net::IMAP@Ordered+and+Normalized+Sets.
1595
+ # See SequenceSet@Ordered+and+Normalized+sets.
1455
1596
  #
1456
1597
  # Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
1457
1598
  # #=> Net::IMAP::SequenceSet["1:7,9:11"]
@@ -1464,17 +1605,18 @@ module Net
1464
1605
  end
1465
1606
 
1466
1607
  # Resets #string to be sorted, deduplicated, and coalesced. Returns
1467
- # +self+. See Net::IMAP@Ordered+and+Normalized+Sets.
1608
+ # +self+. See SequenceSet@Ordered+and+Normalized+sets.
1468
1609
  #
1469
1610
  # Related: #normalize, #normalized_string
1470
1611
  def normalize!
1612
+ modifying! # redundant check, to normalize the error message for JRuby
1471
1613
  @string = nil
1472
1614
  self
1473
1615
  end
1474
1616
 
1475
1617
  # Returns a normalized +sequence-set+ string representation, sorted
1476
1618
  # and deduplicated. Adjacent or overlapping elements will be merged into
1477
- # a single larger range. See Net::IMAP@Ordered+and+Normalized+Sets.
1619
+ # a single larger range. See SequenceSet@Ordered+and+Normalized+sets.
1478
1620
  #
1479
1621
  # Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
1480
1622
  # #=> "1:7,9:11"
@@ -1496,7 +1638,15 @@ module Net
1496
1638
  end
1497
1639
  end
1498
1640
 
1499
- # Returns self
1641
+ ##
1642
+ # :method: to_sequence_set
1643
+ # :call-seq: to_sequence_set -> self
1644
+ #
1645
+ # Returns +self+
1646
+ #
1647
+ # Related: ::try_convert
1648
+
1649
+ # :nodoc: (work around rdoc bug)
1500
1650
  alias to_sequence_set itself
1501
1651
 
1502
1652
  # Unstable API: currently for internal use only (Net::IMAP#validate_data)
@@ -1537,6 +1687,7 @@ module Net
1537
1687
  end
1538
1688
 
1539
1689
  def initialize_dup(other)
1690
+ modifying! # redundant check, to normalize the error message for JRuby
1540
1691
  @tuples = other.tuples.map(&:dup)
1541
1692
  @string = other.string&.-@
1542
1693
  super
@@ -1563,9 +1714,8 @@ module Net
1563
1714
  when Array then set.flat_map { input_to_tuples _1 }
1564
1715
  when nil then []
1565
1716
  else
1566
- raise DataFormatError,
1567
- "expected nz-number, range, string, or enumerable; " \
1568
- "got %p" % [set]
1717
+ raise DataFormatError, "expected nz-number, range, '*', Set, Array; " \
1718
+ "got %p" % [set]
1569
1719
  end
1570
1720
  end
1571
1721
 
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.9"
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.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda