net-imap 0.4.19 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of net-imap might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/Gemfile +7 -1
- data/lib/net/imap/authenticators.rb +2 -2
- data/lib/net/imap/command_data.rb +11 -0
- data/lib/net/imap/config.rb +36 -79
- data/lib/net/imap/data_encoding.rb +3 -3
- data/lib/net/imap/deprecated_client_options.rb +6 -3
- data/lib/net/imap/errors.rb +6 -0
- data/lib/net/imap/response_data.rb +60 -96
- data/lib/net/imap/response_parser.rb +18 -45
- data/lib/net/imap/sasl/authentication_exchange.rb +52 -20
- data/lib/net/imap/sasl/authenticators.rb +8 -4
- data/lib/net/imap/sasl/client_adapter.rb +77 -26
- data/lib/net/imap/sasl/cram_md5_authenticator.rb +1 -1
- data/lib/net/imap/sasl/digest_md5_authenticator.rb +213 -51
- data/lib/net/imap/sasl/login_authenticator.rb +2 -1
- data/lib/net/imap/sasl/protocol_adapters.rb +60 -4
- data/lib/net/imap/sasl.rb +6 -3
- data/lib/net/imap/sasl_adapter.rb +0 -1
- data/lib/net/imap/sequence_set.rb +70 -213
- data/lib/net/imap.rb +29 -55
- data/net-imap.gemspec +1 -1
- metadata +7 -5
- data/lib/net/imap/uidplus_data.rb +0 -326
@@ -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
|
-
#
|
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
|
-
|
12
|
-
|
13
|
-
|
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
|
118
|
-
#
|
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
|
-
#
|
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,
|
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
|
-
# ==
|
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
|
-
#
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
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
|
-
#
|
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
|
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
|
193
|
-
# - #at: Returns the number at a given offset
|
194
|
-
# - #find_index: Returns the given number's offset in the
|
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
|
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
|
-
|
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
|
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 = -(
|
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
|
-
#
|
878
|
-
#
|
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
|
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
|
-
|
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
|
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
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
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>
|
1046
|
-
#
|
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
|
1057
|
-
#
|
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: #[]
|
993
|
+
# Related: #[]
|
1099
994
|
def find_index(number)
|
1100
995
|
number = to_tuple_int number
|
1101
|
-
each_tuple_with_index
|
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
|
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
|
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
|
1148
|
-
#
|
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
|
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
|
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
|
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
|
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
|
-
#
|
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
|
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
|
1217
|
-
#
|
1075
|
+
# The result is based on the normalized set—sorted and de-duplicated—not
|
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
|
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
|
-
|
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 <<
|
1480
|
-
elsif (max + 1) < lower.first then tuples.insert(lower_idx,
|
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
|
-
|
1555
|
-
|
1556
|
-
|
1557
|
-
|
1558
|
-
|
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
|