Skip to content

Commit a01a59f

Browse files
authored
🔀 Merge pull request #376 from ruby/seqset-bugfixes
🐛 Fix `SequenceSet#append` when its `@string` is nil
2 parents b7ed240 + 4c9b1a2 commit a01a59f

File tree

2 files changed

+27
-15
lines changed

2 files changed

+27
-15
lines changed

lib/net/imap/sequence_set.rb

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,20 @@ class IMAP
5656
# set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
5757
# set.valid_string #=> "1:10,55,1024:2048"
5858
#
59-
# == Normalized form
59+
# == Ordered and Normalized sets
6060
#
61-
# When a sequence set is created with a single String value, that #string
62-
# representation is preserved. SequenceSet's internal representation
63-
# implicitly sorts all entries, de-duplicates numbers, and coalesces
64-
# adjacent or overlapping ranges. Most enumeration methods and offset-based
65-
# methods use this normalized representation. Most modification methods
66-
# will convert #string to its normalized form.
61+
# Sometimes the order of the set's members is significant, such as with the
62+
# +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.
6765
#
68-
# In some cases the order of the string representation is significant, such
69-
# as the +ESORT+, <tt>CONTEXT=SORT</tt>, and +UIDPLUS+ extensions. Use
70-
# #entries or #each_entry to enumerate the set in its original order. To
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.
71+
#
72+
# Most modification methods convert #string to its normalized form. To
7173
# preserve #string order while modifying a set, use #append, #string=, or
7274
# #replace.
7375
#
@@ -181,14 +183,15 @@ class IMAP
181183
# - #max: Returns the maximum number in the set.
182184
# - #minmax: Returns the minimum and maximum numbers in the set.
183185
#
184-
# <i>Accessing value by offset:</i>
186+
# <i>Accessing value by (normalized) offset:</i>
185187
# - #[] (aliased as #slice): Returns the number or consecutive subset at a
186188
# given offset or range of offsets.
187189
# - #at: Returns the number at a given offset.
188190
# - #find_index: Returns the given number's offset in the set
189191
#
190192
# <i>Set cardinality:</i>
191193
# - #count (aliased as #size): Returns the count of numbers in the set.
194+
# Duplicated numbers are not counted.
192195
# - #empty?: Returns whether the set has no members. \IMAP syntax does not
193196
# allow empty sequence sets.
194197
# - #valid?: Returns whether the set has any members.
@@ -681,8 +684,9 @@ def append(object)
681684
modifying!
682685
tuple = input_to_tuple object
683686
entry = tuple_to_str tuple
687+
string unless empty? # write @string before tuple_add
684688
tuple_add tuple
685-
@string = -(string ? "#{@string},#{entry}" : entry)
689+
@string = -(@string ? "#{@string},#{entry}" : entry)
686690
self
687691
end
688692

@@ -838,8 +842,8 @@ def entries; each_entry.to_a end
838842
# <tt>*</tt> translates to an endless range. Use #limit to translate both
839843
# cases to a maximum value.
840844
#
841-
# If the original input was unordered or contains overlapping ranges, the
842-
# returned ranges will be ordered and coalesced.
845+
# The returned elements will be sorted and coalesced, even when the input
846+
# #string is not. <tt>*</tt> will sort last. See #normalize.
843847
#
844848
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
845849
# #=> [2, 5..9, 11..12, :*]
@@ -857,7 +861,7 @@ def elements; each_element.to_a end
857861
# translates to <tt>:*..</tt>. Use #limit to set <tt>*</tt> to a maximum
858862
# value.
859863
#
860-
# The returned ranges will be ordered and coalesced, even when the input
864+
# The returned ranges will be sorted and coalesced, even when the input
861865
# #string is not. <tt>*</tt> will sort last. See #normalize.
862866
#
863867
# Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges

test/net/imap/test_sequence_set.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,14 @@ def obj.to_sequence_set; 192_168.001_255 end
344344
assert_equal "1:6,4:9", SequenceSet.new("1:6").append("4:9").string
345345
assert_equal "1:4,5:*", SequenceSet.new("1:4").append(5..).string
346346
assert_equal "5:*,1:4", SequenceSet.new("5:*").append(1..4).string
347+
# also works from empty
348+
assert_equal "5,1", SequenceSet.new.append(5).append(1).string
349+
# also works when *previously* input was non-strings
350+
assert_equal "*,1", SequenceSet.new(:*).append(1).string
351+
assert_equal "1,5", SequenceSet.new(1).append("5").string
352+
assert_equal "1:6,4:9", SequenceSet.new(1..6).append(4..9).string
353+
assert_equal "1:4,5:*", SequenceSet.new(1..4).append(5..).string
354+
assert_equal "5:*,1:4", SequenceSet.new(5..).append(1..4).string
347355
end
348356

349357
test "#merge" do

0 commit comments

Comments
 (0)