Skip to content

Commit c353d66

Browse files
committed
✨ Add Net::IMAP::SequenceSet() coercion method
This new coercion method combines `::try_convert` with `::new`. Unlike `new`, a new object won't be allocated when the input already is a SequenceSet. Unlike `::[]`, empty sets are allowed and the result might not be frozen.
1 parent 2bc753f commit c353d66

File tree

3 files changed

+74
-3
lines changed

3 files changed

+74
-3
lines changed

lib/net/imap.rb

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,22 @@ class IMAP < Protocol
809809
include SSL
810810
end
811811

812+
# :call-seq:
813+
# Net::IMAP::SequenceSet(set = nil) -> SequenceSet
814+
#
815+
# Coerces +set+ into a SequenceSet, using either SequenceSet.try_convert or
816+
# SequenceSet.new.
817+
#
818+
# * When +set+ is a SequenceSet, that same set is returned.
819+
# * When +set+ responds to +to_sequence_set+, +set.to_sequence_set+ is
820+
# returned.
821+
# * Otherwise, returns the result from calling SequenceSet.new with +set+.
822+
#
823+
# Related: SequenceSet.try_convert, SequenceSet.new, SequenceSet::[]
824+
def self.SequenceSet(set = nil)
825+
SequenceSet.try_convert(set) || SequenceSet.new(set)
826+
end
827+
812828
# Returns the global Config object
813829
def self.config; Config.global end
814830

lib/net/imap/sequence_set.rb

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,28 @@ class IMAP
7070
# copy == input #=> false, different set membership
7171
# copy.eql? input #=> false, different string value
7272
#
73+
# Use Net::IMAP::SequenceSet() to coerce a single (optional) input.
74+
# A SequenceSet input is returned without duplication, even when frozen.
75+
#
76+
# set = Net::IMAP::SequenceSet()
77+
# set.string #=> nil
78+
# set.frozen? #=> false
79+
#
80+
# # String order is preserved
81+
# set = Net::IMAP::SequenceSet("1,2,3:7,5,6:10,2048,1024")
82+
# set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
83+
# set.frozen? #=> false
84+
#
85+
# # Other inputs are normalized
86+
# set = Net::IMAP::SequenceSet([1, 2, [3..7, 5], 6..10, 2048, 1024])
87+
# set.valid_string #=> "1:10,55,1024:2048"
88+
# set.frozen? #=> false
89+
#
90+
# unfrozen = set
91+
# frozen = set.dup.freeze
92+
# unfrozen.equal? Net::IMAP::SequenceSet(unfrozen) #=> true
93+
# frozen.equal? Net::IMAP::SequenceSet(frozen) #=> true
94+
#
7395
# Use ::[] to coerce one or more arguments into a valid frozen SequenceSet.
7496
# A valid frozen SequenceSet is returned directly, without allocating a new
7597
# object. ::[] will not create an invalid (empty) set.
@@ -95,7 +117,7 @@ class IMAP
95117
#
96118
# Objects which respond to +to_sequence_set+ (such as SearchResult and
97119
# ThreadMember) can be coerced to a SequenceSet with ::new, ::try_convert,
98-
# or ::[].
120+
# ::[], or Net::IMAP::SequenceSet.
99121
#
100122
# search = imap.uid_search(["SUBJECT", "hello", "NOT", "SEEN"])
101123
# seqset = Net::IMAP::SequenceSet(search) - already_fetched
@@ -203,6 +225,7 @@ class IMAP
203225
# * ::new: Creates a new mutable sequence set, which may be empty (invalid).
204226
# * ::try_convert: Calls +to_sequence_set+ on an object and verifies that
205227
# the result is a SequenceSet.
228+
# * Net::IMAP::SequenceSet(): Coerce an input using ::try_convert or ::new.
206229
# * ::empty: Returns a frozen empty (invalid) SequenceSet.
207230
# * ::full: Returns a frozen SequenceSet containing every possible number.
208231
#
@@ -387,7 +410,7 @@ class << self
387410
#
388411
# Use ::new to create a mutable or empty SequenceSet.
389412
#
390-
# Related: ::new, ::try_convert
413+
# Related: ::new, Net::IMAP::SequenceSet(), ::try_convert
391414
def [](first, *rest)
392415
if rest.empty?
393416
if first.is_a?(SequenceSet) && first.frozen? && first.valid?
@@ -410,7 +433,7 @@ def [](first, *rest)
410433
# If +obj.to_sequence_set+ doesn't return a SequenceSet, an exception is
411434
# raised.
412435
#
413-
# Related: ::new, ::[]
436+
# Related: Net::IMAP::SequenceSet(), ::new, ::[]
414437
def try_convert(obj)
415438
return obj if obj.is_a?(SequenceSet)
416439
return nil unless obj.respond_to?(:to_sequence_set)
@@ -486,6 +509,8 @@ def full; FULL end
486509
# * ::[] returns a frozen validated (non-empty) SequenceSet, without
487510
# allocating a new object when the input is already a valid frozen
488511
# SequenceSet.
512+
# * Net::IMAP::SequenceSet() coerces an input to SequenceSet, without
513+
# allocating a new object when the input is already a SequenceSet.
489514
# * ::try_convert calls +to_sequence_set+ on inputs that support it and
490515
# returns +nil+ for inputs that don't.
491516
# * ::empty and ::full both return frozen singleton sets which can be

test/net/imap/test_sequence_set.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,36 @@ def obj.to_sequence_set; 192_168.001_255 end
273273
assert_raise DataFormatError do SequenceSet.try_convert(obj) end
274274
end
275275

276+
test "Net::IMAP::SequenceSet(set)" do
277+
assert_equal SequenceSet.empty, Net::IMAP::SequenceSet()
278+
assert_equal SequenceSet.empty, Net::IMAP::SequenceSet(nil)
279+
assert_equal SequenceSet.empty, Net::IMAP::SequenceSet([])
280+
assert_equal SequenceSet.empty, Net::IMAP::SequenceSet([[]])
281+
assert_equal SequenceSet.empty, Net::IMAP::SequenceSet("")
282+
assert_equal SequenceSet[123], Net::IMAP::SequenceSet(123)
283+
assert_equal SequenceSet[12..34], Net::IMAP::SequenceSet(12..34)
284+
assert_equal SequenceSet[12..34], Net::IMAP::SequenceSet("12:34")
285+
286+
refute Net::IMAP::SequenceSet("").frozen?
287+
288+
assert_raise DataFormatError do Net::IMAP::SequenceSet(Object.new) end
289+
290+
set = SequenceSet[123]
291+
assert_same set, Net::IMAP::SequenceSet(set)
292+
293+
set = SequenceSet.new(123)
294+
assert_same set, Net::IMAP::SequenceSet(set)
295+
296+
obj = Object.new
297+
set = SequenceSet[192, 168, 1, 255]
298+
obj.define_singleton_method(:to_sequence_set) { set }
299+
assert_same set, Net::IMAP::SequenceSet(obj)
300+
301+
obj = Object.new
302+
def obj.to_sequence_set; 192_168.001_255 end
303+
assert_raise DataFormatError do Net::IMAP::SequenceSet(obj) end
304+
end
305+
276306
test "#at(non-negative index)" do
277307
assert_nil SequenceSet.empty.at(0)
278308
assert_equal 1, SequenceSet[1..].at(0)

0 commit comments

Comments
 (0)