Skip to content

Commit

Permalink
✨ Add SequenceSet#each_ordered_number
Browse files Browse the repository at this point in the history
Yields each number in the ordered entries and returns +self+.
  • Loading branch information
nevans committed Jan 24, 2025
1 parent c26dc51 commit 3428864
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 7 deletions.
33 changes: 26 additions & 7 deletions lib/net/imap/sequence_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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+.
Expand Down Expand Up @@ -990,19 +992,36 @@ def each_range # :yields: range
# Returns an enumerator when called without a block (even if the set
# contains <tt>*</tt>).
#
# 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 <tt>*</tt>, RangeError will be raised.
#
# Returns an enumerator when called without a block (even if the set
# contains <tt>*</tt>).
#
# 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 <tt>*</tt>, RangeError will be raised.
Expand Down
18 changes: 18 additions & 0 deletions test/net/imap/test_sequence_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 3428864

Please sign in to comment.