net-imap 0.4.19 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of net-imap might be problematic. Click here for more details.

@@ -4,16 +4,72 @@ module Net
4
4
  class IMAP
5
5
  module SASL
6
6
 
7
+ # SASL::ProtocolAdapters modules are meant to be used as mixins for
8
+ # SASL::ClientAdapter and its subclasses. Where the client adapter must
9
+ # be customized for each client library, the protocol adapter mixin
10
+ # handles \SASL requirements that are part of the protocol specification,
11
+ # but not specific to any particular client library. In particular, see
12
+ # {RFC4422 §4}[https://www.rfc-editor.org/rfc/rfc4422.html#section-4]
13
+ #
14
+ # === Interface
15
+ #
16
+ # >>>
17
+ # NOTE: This API is experimental, and may change.
18
+ #
19
+ # - {#command_name}[rdoc-ref:Generic#command_name] -- The name of the
20
+ # command used to to initiate an authentication exchange.
21
+ # - {#service}[rdoc-ref:Generic#service] -- The GSSAPI service name.
22
+ # - {#encode_ir}[rdoc-ref:Generic#encode_ir]--Encodes an initial response.
23
+ # - {#decode}[rdoc-ref:Generic#decode] -- Decodes a server challenge.
24
+ # - {#encode}[rdoc-ref:Generic#encode] -- Encodes a client response.
25
+ # - {#cancel_response}[rdoc-ref:Generic#cancel_response] -- The encoded
26
+ # client response used to cancel an authentication exchange.
27
+ #
28
+ # Other protocol requirements of the \SASL authentication exchange are
29
+ # handled by SASL::ClientAdapter.
30
+ #
31
+ # === Included protocol adapters
32
+ #
33
+ # - Generic -- a basic implementation of all of the methods listed above.
34
+ # - IMAP -- An adapter for the IMAP4 protocol.
35
+ # - SMTP -- An adapter for the \SMTP protocol with the +AUTH+ capability.
36
+ # - POP -- An adapter for the POP3 protocol with the +SASL+ capability.
7
37
  module ProtocolAdapters
8
- # This API is experimental, and may change.
38
+ # See SASL::ProtocolAdapters@Interface.
9
39
  module Generic
40
+ # The name of the protocol command used to initiate a \SASL
41
+ # authentication exchange.
42
+ #
43
+ # The generic implementation returns <tt>"AUTHENTICATE"</tt>.
10
44
  def command_name; "AUTHENTICATE" end
11
- def service; raise "Implement in subclass or module" end
12
- def host; client.host end
13
- def port; client.port end
45
+
46
+ # A service name from the {GSSAPI/Kerberos/SASL Service Names
47
+ # registry}[https://www.iana.org/assignments/gssapi-service-names/gssapi-service-names.xhtml].
48
+ #
49
+ # The generic implementation returns <tt>"host"</tt>, which is the
50
+ # generic GSSAPI host-based service name.
51
+ def service; "host" end
52
+
53
+ # Encodes an initial response string.
54
+ #
55
+ # The generic implementation returns the result of #encode, or returns
56
+ # <tt>"="</tt> when +string+ is empty.
14
57
  def encode_ir(string) string.empty? ? "=" : encode(string) end
58
+
59
+ # Encodes a client response string.
60
+ #
61
+ # The generic implementation returns the Base64 encoding of +string+.
15
62
  def encode(string) [string].pack("m0") end
63
+
64
+ # Decodes a server challenge string.
65
+ #
66
+ # The generic implementation returns the Base64 decoding of +string+.
16
67
  def decode(string) string.unpack1("m0") end
68
+
69
+ # Returns the message used by the client to abort an authentication
70
+ # exchange.
71
+ #
72
+ # The generic implementation returns <tt>"*"</tt>.
17
73
  def cancel_response; "*" end
18
74
  end
19
75
 
data/lib/net/imap/sasl.rb CHANGED
@@ -114,8 +114,8 @@ module Net
114
114
  # messages has not passed integrity checks.
115
115
  AuthenticationFailed = Class.new(Error)
116
116
 
117
- # Indicates that authentication cannot proceed because one of the server's
118
- # ended authentication prematurely.
117
+ # Indicates that authentication cannot proceed because the server ended
118
+ # authentication prematurely.
119
119
  class AuthenticationIncomplete < AuthenticationFailed
120
120
  # The success response from the server
121
121
  attr_reader :response
@@ -159,7 +159,10 @@ module Net
159
159
  # Returns the default global SASL::Authenticators instance.
160
160
  def self.authenticators; @authenticators ||= Authenticators.new end
161
161
 
162
- # Delegates to <tt>registry.new</tt> See Authenticators#new.
162
+ # Creates a new SASL authenticator, using SASL::Authenticators#new.
163
+ #
164
+ # +registry+ defaults to SASL.authenticators. All other arguments are
165
+ # forwarded to to <tt>registry.new</tt>.
163
166
  def self.authenticator(*args, registry: authenticators, **kwargs, &block)
164
167
  registry.new(*args, **kwargs, &block)
165
168
  end
@@ -12,7 +12,6 @@ module Net
12
12
 
13
13
  def response_errors; RESPONSE_ERRORS end
14
14
  def sasl_ir_capable?; client.capable?("SASL-IR") end
15
- def auth_capable?(mechanism); client.auth_capable?(mechanism) end
16
15
  def drop_connection; client.logout! end
17
16
  def drop_connection!; client.disconnect end
18
17
  end
@@ -14,13 +14,6 @@ module Net
14
14
  # receive a SequenceSet as an argument, for example IMAP#search, IMAP#fetch,
15
15
  # and IMAP#store.
16
16
  #
17
- # == EXPERIMENTAL API
18
- #
19
- # SequenceSet is currently experimental. Only two methods, ::[] and
20
- # #valid_string, are considered stable. Although the API isn't expected to
21
- # change much, any other methods may be removed or changed without
22
- # deprecation.
23
- #
24
17
  # == Creating sequence sets
25
18
  #
26
19
  # SequenceSet.new with no arguments creates an empty sequence set. Note
@@ -37,7 +30,8 @@ module Net
37
30
  #
38
31
  # SequenceSet.new may receive a single optional argument: a non-zero 32 bit
39
32
  # unsigned integer, a range, a <tt>sequence-set</tt> formatted string,
40
- # another sequence set, or an enumerable containing any of these.
33
+ # another sequence set, a Set (containing only numbers or <tt>*</tt>), or an
34
+ # Array containing any of these (array inputs may be nested).
41
35
  #
42
36
  # set = Net::IMAP::SequenceSet.new(1)
43
37
  # set.valid_string #=> "1"
@@ -60,20 +54,18 @@ module Net
60
54
  # set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
61
55
  # set.valid_string #=> "1:10,55,1024:2048"
62
56
  #
63
- # == Ordered and Normalized sets
64
- #
65
- # Sometimes the order of the set's members is significant, such as with the
66
- # +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. So, when a
67
- # sequence set is created by the parser or with a single string value, that
68
- # #string representation is preserved.
57
+ # == Normalized form
69
58
  #
70
- # Internally, SequenceSet stores a normalized representation which sorts all
71
- # entries, de-duplicates numbers, and coalesces adjacent or overlapping
72
- # ranges. Most methods use this normalized representation to achieve
73
- # <tt>O(lg n)</tt> porformance. Use #entries or #each_entry to enumerate
74
- # the set in its original order.
59
+ # When a sequence set is created with a single String value, that #string
60
+ # representation is preserved. SequenceSet's internal representation
61
+ # implicitly sorts all entries, de-duplicates numbers, and coalesces
62
+ # adjacent or overlapping ranges. Most enumeration methods and offset-based
63
+ # methods use this normalized representation. Most modification methods
64
+ # will convert #string to its normalized form.
75
65
  #
76
- # Most modification methods convert #string to its normalized form. To
66
+ # In some cases the order of the string representation is significant, such
67
+ # as the +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. Use
68
+ # #entries or #each_entry to enumerate the set in its original order. To
77
69
  # preserve #string order while modifying a set, use #append, #string=, or
78
70
  # #replace.
79
71
  #
@@ -166,7 +158,7 @@ module Net
166
158
  # - #===:
167
159
  # Returns whether a given object is fully contained within +self+, or
168
160
  # +nil+ if the object cannot be converted to a compatible type.
169
- # - #cover?:
161
+ # - #cover? (aliased as #===):
170
162
  # Returns whether a given object is fully contained within +self+.
171
163
  # - #intersect? (aliased as #overlap?):
172
164
  # Returns whether +self+ and a given object have any common elements.
@@ -187,41 +179,30 @@ module Net
187
179
  # - #max: Returns the maximum number in the set.
188
180
  # - #minmax: Returns the minimum and maximum numbers in the set.
189
181
  #
190
- # <i>Accessing value by offset in sorted set:</i>
182
+ # <i>Accessing value by offset:</i>
191
183
  # - #[] (aliased as #slice): Returns the number or consecutive subset at a
192
- # given offset or range of offsets in the sorted set.
193
- # - #at: Returns the number at a given offset in the sorted set.
194
- # - #find_index: Returns the given number's offset in the sorted set.
195
- #
196
- # <i>Accessing value by offset in ordered entries</i>
197
- # - #ordered_at: Returns the number at a given offset in the ordered entries.
198
- # - #find_ordered_index: Returns the index of the given number's first
199
- # occurrence in entries.
184
+ # given offset or range of offsets.
185
+ # - #at: Returns the number at a given offset.
186
+ # - #find_index: Returns the given number's offset in the set
200
187
  #
201
188
  # <i>Set cardinality:</i>
202
189
  # - #count (aliased as #size): Returns the count of numbers in the set.
203
- # Duplicated numbers are not counted.
204
190
  # - #empty?: Returns whether the set has no members. \IMAP syntax does not
205
191
  # allow empty sequence sets.
206
192
  # - #valid?: Returns whether the set has any members.
207
193
  # - #full?: Returns whether the set contains every possible value, including
208
194
  # <tt>*</tt>.
209
195
  #
210
- # <i>Denormalized properties:</i>
211
- # - #has_duplicates?: Returns whether the ordered entries repeat any
212
- # numbers.
213
- # - #count_duplicates: Returns the count of repeated numbers in the ordered
214
- # entries.
215
- # - #count_with_duplicates: Returns the count of numbers in the ordered
216
- # entries, including any repeated numbers.
217
- #
218
196
  # === Methods for Iterating
219
197
  #
220
- # <i>Normalized (sorted and coalesced):</i>
221
198
  # - #each_element: Yields each number and range in the set, sorted and
222
199
  # coalesced, and returns +self+.
223
200
  # - #elements (aliased as #to_a): Returns an Array of every number and range
224
201
  # in the set, sorted and coalesced.
202
+ # - #each_entry: Yields each number and range in the set, unsorted and
203
+ # without deduplicating numbers or coalescing ranges, and returns +self+.
204
+ # - #entries: Returns an Array of every number and range in the set,
205
+ # unsorted and without deduplicating numbers or coalescing ranges.
225
206
  # - #each_range:
226
207
  # Yields each element in the set as a Range and returns +self+.
227
208
  # - #ranges: Returns an Array of every element in the set, converting
@@ -231,14 +212,6 @@ module Net
231
212
  # ranges into all of their contained numbers.
232
213
  # - #to_set: Returns a Set containing all of the #numbers in the set.
233
214
  #
234
- # <i>Order preserving:</i>
235
- # - #each_entry: Yields each number and range in the set, unsorted and
236
- # without deduplicating numbers or coalescing ranges, and returns +self+.
237
- # - #entries: Returns an Array of every number and range in the set,
238
- # unsorted and without deduplicating numbers or coalescing ranges.
239
- # - #each_ordered_number: Yields each number in the ordered entries and
240
- # returns +self+.
241
- #
242
215
  # === Methods for \Set Operations
243
216
  # These methods do not modify +self+.
244
217
  #
@@ -258,29 +231,19 @@ module Net
258
231
  # === Methods for Assigning
259
232
  # These methods add or replace elements in +self+.
260
233
  #
261
- # <i>Normalized (sorted and coalesced):</i>
262
- #
263
- # These methods always update #string to be fully sorted and coalesced.
264
- #
265
234
  # - #add (aliased as #<<): Adds a given object to the set; returns +self+.
266
235
  # - #add?: If the given object is not an element in the set, adds it and
267
236
  # returns +self+; otherwise, returns +nil+.
268
237
  # - #merge: Merges multiple elements into the set; returns +self+.
269
- # - #complement!: Replaces the contents of the set with its own #complement.
270
- #
271
- # <i>Order preserving:</i>
272
- #
273
- # These methods _may_ cause #string to not be sorted or coalesced.
274
- #
275
238
  # - #append: Adds a given object to the set, appending it to the existing
276
239
  # string, and returns +self+.
277
240
  # - #string=: Assigns a new #string value and replaces #elements to match.
278
241
  # - #replace: Replaces the contents of the set with the contents
279
242
  # of a given object.
243
+ # - #complement!: Replaces the contents of the set with its own #complement.
280
244
  #
281
245
  # === Methods for Deleting
282
- # These methods remove elements from +self+, and update #string to be fully
283
- # sorted and coalesced.
246
+ # These methods remove elements from +self+.
284
247
  #
285
248
  # - #clear: Removes all elements in the set; returns +self+.
286
249
  # - #delete: Removes a given object from the set; returns +self+.
@@ -320,8 +283,7 @@ module Net
320
283
  private_constant :STAR_INT, :STARS
321
284
 
322
285
  COERCIBLE = ->{ _1.respond_to? :to_sequence_set }
323
- ENUMABLE = ->{ _1.respond_to?(:each) && _1.respond_to?(:empty?) }
324
- private_constant :COERCIBLE, :ENUMABLE
286
+ private_constant :COERCIBLE
325
287
 
326
288
  class << self
327
289
 
@@ -356,7 +318,7 @@ module Net
356
318
  # raised.
357
319
  def try_convert(obj)
358
320
  return obj if obj.is_a?(SequenceSet)
359
- return nil unless obj.respond_to?(:to_sequence_set)
321
+ return nil unless respond_to?(:to_sequence_set)
360
322
  obj = obj.to_sequence_set
361
323
  return obj if obj.is_a?(SequenceSet)
362
324
  raise DataFormatError, "invalid object returned from to_sequence_set"
@@ -716,9 +678,8 @@ module Net
716
678
  modifying!
717
679
  tuple = input_to_tuple object
718
680
  entry = tuple_to_str tuple
719
- string unless empty? # write @string before tuple_add
720
681
  tuple_add tuple
721
- @string = -(@string ? "#{@string},#{entry}" : entry)
682
+ @string = -(string ? "#{@string},#{entry}" : entry)
722
683
  self
723
684
  end
724
685
 
@@ -874,8 +835,8 @@ module Net
874
835
  # <tt>*</tt> translates to an endless range. Use #limit to translate both
875
836
  # cases to a maximum value.
876
837
  #
877
- # The returned elements will be sorted and coalesced, even when the input
878
- # #string is not. <tt>*</tt> will sort last. See #normalize.
838
+ # If the original input was unordered or contains overlapping ranges, the
839
+ # returned ranges will be ordered and coalesced.
879
840
  #
880
841
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
881
842
  # #=> [2, 5..9, 11..12, :*]
@@ -893,7 +854,7 @@ module Net
893
854
  # translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
894
855
  # value.
895
856
  #
896
- # The returned ranges will be sorted and coalesced, even when the input
857
+ # The returned ranges will be ordered and coalesced, even when the input
897
858
  # #string is not. <tt>*</tt> will sort last. See #normalize.
898
859
  #
899
860
  # Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
@@ -942,7 +903,9 @@ module Net
942
903
  # Related: #entries, #each_element
943
904
  def each_entry(&block) # :yields: integer or range or :*
944
905
  return to_enum(__method__) unless block_given?
945
- each_entry_tuple do yield tuple_to_entry _1 end
906
+ return each_element(&block) unless @string
907
+ @string.split(",").each do yield tuple_to_entry str_to_tuple _1 end
908
+ self
946
909
  end
947
910
 
948
911
  # Yields each number or range (or <tt>:*</tt>) in #elements to the block
@@ -960,16 +923,6 @@ module Net
960
923
 
961
924
  private
962
925
 
963
- def each_entry_tuple(&block)
964
- return to_enum(__method__) unless block_given?
965
- if @string
966
- @string.split(",") do block.call str_to_tuple _1 end
967
- else
968
- @tuples.each(&block)
969
- end
970
- self
971
- end
972
-
973
926
  def tuple_to_entry((min, max))
974
927
  if min == STAR_INT then :*
975
928
  elsif max == STAR_INT then min..
@@ -1001,34 +954,17 @@ module Net
1001
954
  # Returns an enumerator when called without a block (even if the set
1002
955
  # contains <tt>*</tt>).
1003
956
  #
1004
- # Related: #numbers, #each_ordered_number
957
+ # Related: #numbers
1005
958
  def each_number(&block) # :yields: integer
1006
959
  return to_enum(__method__) unless block_given?
1007
960
  raise RangeError, '%s contains "*"' % [self.class] if include_star?
1008
- @tuples.each do each_number_in_tuple _1, _2, &block end
1009
- self
1010
- end
1011
-
1012
- # Yields each number in #entries to the block and returns self.
1013
- # If the set contains a <tt>*</tt>, RangeError will be raised.
1014
- #
1015
- # Returns an enumerator when called without a block (even if the set
1016
- # contains <tt>*</tt>).
1017
- #
1018
- # Related: #entries, #each_number
1019
- def each_ordered_number(&block)
1020
- return to_enum(__method__) unless block_given?
1021
- raise RangeError, '%s contains "*"' % [self.class] if include_star?
1022
- each_entry_tuple do each_number_in_tuple _1, _2, &block end
1023
- end
1024
-
1025
- private def each_number_in_tuple(min, max, &block)
1026
- if min == STAR_INT then yield :*
1027
- elsif min == max then yield min
1028
- elsif max != STAR_INT then (min..max).each(&block)
1029
- else
1030
- raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
961
+ each_element do |elem|
962
+ case elem
963
+ when Range then elem.each(&block)
964
+ when Integer then block.(elem)
965
+ end
1031
966
  end
967
+ self
1032
968
  end
1033
969
 
1034
970
  # Returns a Set with all of the #numbers in the sequence set.
@@ -1042,10 +978,8 @@ module Net
1042
978
 
1043
979
  # Returns the count of #numbers in the set.
1044
980
  #
1045
- # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1046
- # unsigned integer value).
1047
- #
1048
- # Related: #count_with_duplicates
981
+ # If <tt>*</tt> and <tt>2**32 - 1</tt> (the maximum 32-bit unsigned
982
+ # integer value) are both in the set, they will only be counted once.
1049
983
  def count
1050
984
  @tuples.sum(@tuples.count) { _2 - _1 } +
1051
985
  (include_star? && include?(UINT32_MAX) ? -1 : 0)
@@ -1053,87 +987,33 @@ module Net
1053
987
 
1054
988
  alias size count
1055
989
 
1056
- # Returns the count of numbers in the ordered #entries, including any
1057
- # repeated numbers.
1058
- #
1059
- # <tt>*</tt> will be counted as <tt>2**32 - 1</tt> (the maximum 32-bit
1060
- # unsigned integer value).
1061
- #
1062
- # When #string is normalized, this behaves the same as #count.
1063
- #
1064
- # Related: #entries, #count_duplicates, #has_duplicates?
1065
- def count_with_duplicates
1066
- return count unless @string
1067
- each_entry_tuple.sum {|min, max|
1068
- max - min + ((max == STAR_INT && min != STAR_INT) ? 0 : 1)
1069
- }
1070
- end
1071
-
1072
- # Returns the count of repeated numbers in the ordered #entries, the
1073
- # difference between #count_with_duplicates and #count.
1074
- #
1075
- # When #string is normalized, this is zero.
1076
- #
1077
- # Related: #entries, #count_with_duplicates, #has_duplicates?
1078
- def count_duplicates
1079
- return 0 unless @string
1080
- count_with_duplicates - count
1081
- end
1082
-
1083
- # :call-seq: has_duplicates? -> true | false
1084
- #
1085
- # Returns whether or not the ordered #entries repeat any numbers.
1086
- #
1087
- # Always returns +false+ when #string is normalized.
1088
- #
1089
- # Related: #entries, #count_with_duplicates, #count_duplicates?
1090
- def has_duplicates?
1091
- return false unless @string
1092
- count_with_duplicates != count
1093
- end
1094
-
1095
- # Returns the (sorted and deduplicated) index of +number+ in the set, or
1096
- # +nil+ if +number+ isn't in the set.
990
+ # Returns the index of +number+ in the set, or +nil+ if +number+ isn't in
991
+ # the set.
1097
992
  #
1098
- # Related: #[], #at, #find_ordered_index
993
+ # Related: #[]
1099
994
  def find_index(number)
1100
995
  number = to_tuple_int number
1101
- each_tuple_with_index(@tuples) do |min, max, idx_min|
996
+ each_tuple_with_index do |min, max, idx_min|
1102
997
  number < min and return nil
1103
998
  number <= max and return from_tuple_int(idx_min + (number - min))
1104
999
  end
1105
1000
  nil
1106
1001
  end
1107
1002
 
1108
- # Returns the first index of +number+ in the ordered #entries, or
1109
- # +nil+ if +number+ isn't in the set.
1110
- #
1111
- # Related: #find_index
1112
- def find_ordered_index(number)
1113
- number = to_tuple_int number
1114
- each_tuple_with_index(each_entry_tuple) do |min, max, idx_min|
1115
- if min <= number && number <= max
1116
- return from_tuple_int(idx_min + (number - min))
1117
- end
1118
- end
1119
- nil
1120
- end
1121
-
1122
1003
  private
1123
1004
 
1124
- def each_tuple_with_index(tuples)
1005
+ def each_tuple_with_index
1125
1006
  idx_min = 0
1126
- tuples.each do |min, max|
1127
- idx_max = idx_min + (max - min)
1128
- yield min, max, idx_min, idx_max
1007
+ @tuples.each do |min, max|
1008
+ yield min, max, idx_min, (idx_max = idx_min + (max - min))
1129
1009
  idx_min = idx_max + 1
1130
1010
  end
1131
1011
  idx_min
1132
1012
  end
1133
1013
 
1134
- def reverse_each_tuple_with_index(tuples)
1014
+ def reverse_each_tuple_with_index
1135
1015
  idx_max = -1
1136
- tuples.reverse_each do |min, max|
1016
+ @tuples.reverse_each do |min, max|
1137
1017
  yield min, max, (idx_min = idx_max - (max - min)), idx_max
1138
1018
  idx_max = idx_min - 1
1139
1019
  end
@@ -1144,38 +1024,18 @@ module Net
1144
1024
 
1145
1025
  # :call-seq: at(index) -> integer or nil
1146
1026
  #
1147
- # Returns the number at the given +index+ in the sorted set, without
1148
- # modifying the set.
1149
- #
1150
- # +index+ is interpreted the same as in #[], except that #at only allows a
1151
- # single integer argument.
1027
+ # Returns a number from +self+, without modifying the set. Behaves the
1028
+ # same as #[], except that #at only allows a single integer argument.
1152
1029
  #
1153
- # Related: #[], #slice, #ordered_at
1030
+ # Related: #[], #slice
1154
1031
  def at(index)
1155
- lookup_number_by_tuple_index(tuples, index)
1156
- end
1157
-
1158
- # :call-seq: ordered_at(index) -> integer or nil
1159
- #
1160
- # Returns the number at the given +index+ in the ordered #entries, without
1161
- # modifying the set.
1162
- #
1163
- # +index+ is interpreted the same as in #at (and #[]), except that
1164
- # #ordered_at applies to the ordered #entries, not the sorted set.
1165
- #
1166
- # Related: #[], #slice, #ordered_at
1167
- def ordered_at(index)
1168
- lookup_number_by_tuple_index(each_entry_tuple, index)
1169
- end
1170
-
1171
- private def lookup_number_by_tuple_index(tuples, index)
1172
1032
  index = Integer(index.to_int)
1173
1033
  if index.negative?
1174
- reverse_each_tuple_with_index(tuples) do |min, max, idx_min, idx_max|
1034
+ reverse_each_tuple_with_index do |min, max, idx_min, idx_max|
1175
1035
  idx_min <= index and return from_tuple_int(min + (index - idx_min))
1176
1036
  end
1177
1037
  else
1178
- each_tuple_with_index(tuples) do |min, _, idx_min, idx_max|
1038
+ each_tuple_with_index do |min, _, idx_min, idx_max|
1179
1039
  index <= idx_max and return from_tuple_int(min + (index - idx_min))
1180
1040
  end
1181
1041
  end
@@ -1190,18 +1050,17 @@ module Net
1190
1050
  # seqset[range] -> sequence set or nil
1191
1051
  # slice(range) -> sequence set or nil
1192
1052
  #
1193
- # Returns a number or a subset from the _sorted_ set, without modifying
1194
- # the set.
1053
+ # Returns a number or a subset from +self+, without modifying the set.
1195
1054
  #
1196
1055
  # When an Integer argument +index+ is given, the number at offset +index+
1197
- # in the sorted set is returned:
1056
+ # is returned:
1198
1057
  #
1199
1058
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1200
1059
  # set[0] #=> 10
1201
1060
  # set[5] #=> 15
1202
1061
  # set[10] #=> 26
1203
1062
  #
1204
- # If +index+ is negative, it counts relative to the end of the sorted set:
1063
+ # If +index+ is negative, it counts relative to the end of +self+:
1205
1064
  # set = Net::IMAP::SequenceSet["10:15,20:23,26"]
1206
1065
  # set[-1] #=> 26
1207
1066
  # set[-3] #=> 22
@@ -1213,14 +1072,13 @@ module Net
1213
1072
  # set[11] #=> nil
1214
1073
  # set[-12] #=> nil
1215
1074
  #
1216
- # The result is based on the sorted and de-duplicated set, not on the
1217
- # ordered #entries in #string.
1075
+ # The result is based on the normalized set—sorted and de-duplicatednot
1076
+ # on the assigned value of #string.
1218
1077
  #
1219
1078
  # set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
1220
1079
  # set[0] #=> 11
1221
1080
  # set[-1] #=> 23
1222
1081
  #
1223
- # Related: #at
1224
1082
  def [](index, length = nil)
1225
1083
  if length then slice_length(index, length)
1226
1084
  elsif index.is_a?(Range) then slice_range(index)
@@ -1407,7 +1265,8 @@ module Net
1407
1265
  when *STARS, Integer, Range then [input_to_tuple(obj)]
1408
1266
  when String then str_to_tuples obj
1409
1267
  when SequenceSet then obj.tuples
1410
- when ENUMABLE then obj.flat_map { input_to_tuples _1 }
1268
+ when Set then obj.map { [to_tuple_int(_1)] * 2 }
1269
+ when Array then obj.flat_map { input_to_tuples _1 }
1411
1270
  when nil then []
1412
1271
  else
1413
1272
  raise DataFormatError,
@@ -1420,8 +1279,7 @@ module Net
1420
1279
  # String, Set, Array, or... any type of object.
1421
1280
  def input_try_convert(input)
1422
1281
  SequenceSet.try_convert(input) ||
1423
- # Integer.try_convert(input) || # ruby 3.1+
1424
- input.respond_to?(:to_int) && Integer(input.to_int) ||
1282
+ Integer.try_convert(input) ||
1425
1283
  String.try_convert(input) ||
1426
1284
  input
1427
1285
  end
@@ -1476,8 +1334,8 @@ module Net
1476
1334
  modifying!
1477
1335
  min, max = tuple
1478
1336
  lower, lower_idx = tuple_gte_with_index(min - 1)
1479
- if lower.nil? then tuples << [min, max]
1480
- elsif (max + 1) < lower.first then tuples.insert(lower_idx, [min, max])
1337
+ if lower.nil? then tuples << tuple
1338
+ elsif (max + 1) < lower.first then tuples.insert(lower_idx, tuple)
1481
1339
  else tuple_coalesce(lower, lower_idx, min, max)
1482
1340
  end
1483
1341
  end
@@ -1551,12 +1409,11 @@ module Net
1551
1409
  end
1552
1410
 
1553
1411
  def nz_number(num)
1554
- case num
1555
- when Integer, /\A[1-9]\d*\z/ then num = Integer(num)
1556
- else raise DataFormatError, "%p is not a valid nz-number" % [num]
1557
- end
1558
- NumValidator.ensure_nz_number(num)
1559
- num
1412
+ String === num && !/\A[1-9]\d*\z/.match?(num) and
1413
+ raise DataFormatError, "%p is not a valid nz-number" % [num]
1414
+ NumValidator.ensure_nz_number Integer num
1415
+ rescue TypeError # To catch errors from Integer()
1416
+ raise DataFormatError, $!.message
1560
1417
  end
1561
1418
 
1562
1419
  # intentionally defined after the class implementation