diff --git a/lib/net/imap/sequence_set.rb b/lib/net/imap/sequence_set.rb
index b83c9f84..34c7c198 100644
--- a/lib/net/imap/sequence_set.rb
+++ b/lib/net/imap/sequence_set.rb
@@ -227,6 +227,8 @@ class IMAP
# without deduplicating numbers or coalescing ranges, and returns +self+.
# - #entries: Returns an Array of every number and range in the set,
# unsorted and without deduplicating numbers or coalescing ranges.
+ # - #each_ordered_number: Yields each number in the ordered entries and
+ # returns +self+.
#
# === Methods for \Set Operations
# These methods do not modify +self+.
@@ -990,19 +992,36 @@ def each_range # :yields: range
# Returns an enumerator when called without a block (even if the set
# contains *).
#
- # Related: #numbers
+ # Related: #numbers, #each_ordered_number
def each_number(&block) # :yields: integer
return to_enum(__method__) unless block_given?
raise RangeError, '%s contains "*"' % [self.class] if include_star?
- each_element do |elem|
- case elem
- when Range then elem.each(&block)
- when Integer then block.(elem)
- end
- end
+ @tuples.each do each_number_in_tuple _1, _2, &block end
self
end
+ # Yields each number in #entries to the block and returns self.
+ # If the set contains a *, RangeError will be raised.
+ #
+ # Returns an enumerator when called without a block (even if the set
+ # contains *).
+ #
+ # Related: #entries, #each_number
+ def each_ordered_number(&block)
+ return to_enum(__method__) unless block_given?
+ raise RangeError, '%s contains "*"' % [self.class] if include_star?
+ each_entry_tuple do each_number_in_tuple _1, _2, &block end
+ end
+
+ private def each_number_in_tuple(min, max, &block)
+ if min == STAR_INT then yield :*
+ elsif min == max then yield min
+ elsif max != STAR_INT then (min..max).each(&block)
+ else
+ raise RangeError, "#{SequenceSet} cannot enumerate range with '*'"
+ end
+ end
+
# Returns a Set with all of the #numbers in the sequence set.
#
# If the set contains a *, RangeError will be raised.
diff --git a/test/net/imap/test_sequence_set.rb b/test/net/imap/test_sequence_set.rb
index 436eef53..71d62997 100644
--- a/test/net/imap/test_sequence_set.rb
+++ b/test/net/imap/test_sequence_set.rb
@@ -683,6 +683,7 @@ def test_inspect((expected, input, freeze))
entries: [46, 6..7, 15, 1..3],
ranges: [1..3, 6..7, 15..15, 46..46],
numbers: [1, 2, 3, 6, 7, 15, 46],
+ ordered: [46, 6, 7, 15, 1, 2, 3],
to_s: "46,7:6,15,3:1",
normalize: "1:3,6:7,15,46",
count: 7,
@@ -707,6 +708,7 @@ def test_inspect((expected, input, freeze))
entries: [1..5, 3..7, 9..10, 10..11],
ranges: [1..7, 9..11],
numbers: [1, 2, 3, 4, 5, 6, 7, 9, 10, 11],
+ ordered: [1,2,3,4,5, 3,4,5,6,7, 9,10, 10,11],
to_s: "1:5,3:7,10:9,10:11",
normalize: "1:7,9:11",
count: 10,
@@ -720,6 +722,7 @@ def test_inspect((expected, input, freeze))
entries: [1..5, 3..4, 9..11, 10],
ranges: [1..5, 9..11],
numbers: [1, 2, 3, 4, 5, 9, 10, 11],
+ ordered: [1,2,3,4,5, 3,4, 9,10,11, 10],
to_s: "1:5,3:4,9:11,10",
normalize: "1:5,9:11",
count: 8,
@@ -832,6 +835,21 @@ def assert_seqset_enum(expected, seqset, enum)
end
end
+ test "#each_ordered_number" do |data|
+ seqset = SequenceSet.new(data[:input])
+ expected = data[:ordered] || data[:numbers]
+ if expected.is_a?(Class) && expected < Exception
+ assert_raise expected do
+ seqset.each_ordered_number do fail "shouldn't get here" end
+ end
+ enum = seqset.each_ordered_number
+ assert_raise expected do enum.to_a end
+ assert_raise expected do enum.each do fail "shouldn't get here" end end
+ else
+ assert_seqset_enum expected, seqset, :each_ordered_number
+ end
+ end
+
test "#numbers" do |data|
expected = data[:numbers]
if expected.is_a?(Class) && expected < Exception