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.
- 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
|