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