Class: Net::IMAP::SequenceSet
- Inherits:
-
Object
- Object
- Net::IMAP::SequenceSet
- Defined in:
- lib/net/imap/sequence_set.rb
Overview
An IMAP sequence set is a set of message sequence numbers or unique identifier numbers (“UIDs”). It contains numbers and ranges of numbers. The numbers are all non-zero unsigned 32-bit integers and one special value ("*"
) that represents the largest value in the mailbox.
Certain types of IMAP responses will contain a SequenceSet, for example the data for a "MODIFIED"
ResponseCode. Some IMAP commands may receive a SequenceSet as an argument, for example IMAP#search, IMAP#fetch, and IMAP#store.
Creating sequence sets
SequenceSet.new with no arguments creates an empty sequence set. Note that an empty sequence set is invalid in the IMAP grammar.
set = Net::IMAP::SequenceSet.new
set.empty? #=> true
set.valid? #=> false
set.valid_string #!> raises DataFormatError
set << 1..10
set.empty? #=> false
set.valid? #=> true
set.valid_string #=> "1:10"
SequenceSet.new may receive a single optional argument: a non-zero 32 bit unsigned integer, a range, a sequence-set
formatted string, another sequence set, a Set (containing only numbers or *
), or an Array containing any of these (array inputs may be nested).
set = Net::IMAP::SequenceSet.new(1)
set.valid_string #=> "1"
set = Net::IMAP::SequenceSet.new(1..100)
set.valid_string #=> "1:100"
set = Net::IMAP::SequenceSet.new(1...100)
set.valid_string #=> "1:99"
set = Net::IMAP::SequenceSet.new([1, 2, 5..])
set.valid_string #=> "1:2,5:*"
set = Net::IMAP::SequenceSet.new("1,2,3:7,5,6:10,2048,1024")
set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
set = Net::IMAP::SequenceSet.new(1, 2, 3..7, 5, 6..10, 2048, 1024)
set.valid_string #=> "1:10,55,1024:2048"
Use ::[] with one or more arguments to create a frozen SequenceSet. An invalid (empty) set cannot be created with ::[].
set = Net::IMAP::SequenceSet["1,2,3:7,5,6:10,2048,1024"]
set.valid_string #=> "1,2,3:7,5,6:10,2048,1024"
set = Net::IMAP::SequenceSet[1, 2, [3..7, 5], 6..10, 2048, 1024]
set.valid_string #=> "1:10,55,1024:2048"
Normalized form
When a sequence set is created with a single String value, that #string representation is preserved. SequenceSet’s internal representation implicitly sorts all entries, de-duplicates numbers, and coalesces adjacent or overlapping ranges. Most enumeration methods and offset-based methods use this normalized representation. Most modification methods will convert #string to its normalized form.
In some cases the order of the string representation is significant, such as the ESORT
, CONTEXT=SORT
, and UIDPLUS
extensions. Use #entries or #each_entry to enumerate the set in its original order. To preserve #string order while modifying a set, use #append, #string=, or #replace.
Using *
IMAP sequence sets may contain a special value "*"
, which represents the largest number in use. From seq-number
in RFC9051 §9:
In the case of message sequence numbers, it is the number of messages in a non-empty mailbox. In the case of unique identifiers, it is the unique identifier of the last message in the mailbox or, if the mailbox is empty, the mailbox’s current UIDNEXT value.
When creating a SequenceSet, *
may be input as -1
, "*"
, :*
, an endless range, or a range ending in -1
. When converting to #elements, #ranges, or #numbers, it will output as either :*
or an endless range. For example:
Net::IMAP::SequenceSet["1,3,*"].to_a #=> [1, 3, :*]
Net::IMAP::SequenceSet["1,234:*"].to_a #=> [1, 234..]
Net::IMAP::SequenceSet[1234..-1].to_a #=> [1234..]
Net::IMAP::SequenceSet[1234..].to_a #=> [1234..]
Net::IMAP::SequenceSet[1234..].to_s #=> "1234:*"
Net::IMAP::SequenceSet[1234..-1].to_s #=> "1234:*"
Use #limit to convert "*"
to a maximum value. When a range includes "*"
, the maximum value will always be matched:
Net::IMAP::SequenceSet["9999:*"].limit(max: 25)
#=> Net::IMAP::SequenceSet["25"]
Surprising *
behavior
When a set includes *
, some methods may have surprising behavior.
For example, #complement treats *
as its own number. This way, the #intersection of a set and its #complement will always be empty. This is not how an IMAP server interprets the set: it will convert *
to either the number of messages in the mailbox or UIDNEXT
, as appropriate. And there will be overlap between a set and its complement after #limit is applied to each:
~Net::IMAP::SequenceSet["*"] == Net::IMAP::SequenceSet[1..(2**32-1)]
~Net::IMAP::SequenceSet[1..5] == Net::IMAP::SequenceSet["6:*"]
set = Net::IMAP::SequenceSet[1..5]
(set & ~set).empty? => true
(set.limit(max: 4) & (~set).limit(max: 4)).to_a => [4]
When counting the number of numbers in a set, *
will be counted except when UINT32_MAX is also in the set:
UINT32_MAX = 2**32 - 1
Net::IMAP::SequenceSet["*"].count => 1
Net::IMAP::SequenceSet[1..UINT32_MAX - 1, :*].count => UINT32_MAX
Net::IMAP::SequenceSet["1:*"].count => UINT32_MAX
Net::IMAP::SequenceSet[UINT32_MAX, :*].count => 1
Net::IMAP::SequenceSet[UINT32_MAX..].count => 1
What’s here?
SequenceSet provides methods for:
Methods for Creating a SequenceSet
-
::[]: Creates a validated frozen sequence set from one or more inputs.
-
::new: Creates a new mutable sequence set, which may be empty (invalid).
-
::try_convert: Calls
to_sequence_set
on an object and verifies that the result is a SequenceSet. -
::empty: Returns a frozen empty (invalid) SequenceSet.
-
::full: Returns a frozen SequenceSet containing every possible number.
Methods for Comparing
Comparison to another SequenceSet:
-
#==: Returns whether a given set contains the same numbers as
self
. -
#eql?: Returns whether a given set uses the same #string as
self
.
Comparison to objects which are convertible to SequenceSet:
-
#===: Returns whether a given object is fully contained within
self
, ornil
if the object cannot be converted to a compatible type. -
#cover? (aliased as #===): Returns whether a given object is fully contained within
self
. -
#intersect? (aliased as #overlap?): Returns whether
self
and a given object have any common elements. -
#disjoint?: Returns whether
self
and a given object have no common elements.
Methods for Querying
These methods do not modify self
.
Set membership:
-
#include? (aliased as #member?): Returns whether a given object (nz-number, range, or
*
) is contained by the set. -
#include_star?: Returns whether the set contains
*
.
Minimum and maximum value elements:
-
#min: Returns the minimum number in the set.
-
#max: Returns the maximum number in the set.
-
#minmax: Returns the minimum and maximum numbers in the set.
Accessing value by offset:
-
#[] (aliased as #slice): Returns the number or consecutive subset at a given offset or range of offsets.
-
#at: Returns the number at a given offset.
-
#find_index: Returns the given number’s offset in the set
Set cardinality:
-
#count (aliased as #size): Returns the count of numbers in the set.
-
#empty?: Returns whether the set has no members. IMAP syntax does not allow empty sequence sets.
-
#valid?: Returns whether the set has any members.
-
#full?: Returns whether the set contains every possible value, including
*
.
Methods for Iterating
-
#each_element: Yields each number and range in the set, sorted and coalesced, and returns
self
. -
#elements (aliased as #to_a): Returns an Array of every number and range in the set, sorted and coalesced.
-
#each_entry: Yields each number and range in the set, unsorted and 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_range: Yields each element in the set as a Range and returns
self
. -
#ranges: Returns an Array of every element in the set, converting numbers into ranges of a single value.
-
#each_number: Yields each number in the set and returns
self
. -
#numbers: Returns an Array with every number in the set, expanding ranges into all of their contained numbers.
-
#to_set: Returns a Set containing all of the #numbers in the set.
Methods for Set Operations
These methods do not modify self
.
-
#| (aliased as #union and #+): Returns a new set combining all members from
self
with all members from the other object. -
#& (aliased as #intersection): Returns a new set containing all members common to
self
and the other object. -
#- (aliased as #difference): Returns a copy of
self
with all members in the other object removed. -
#^ (aliased as #xor): Returns a new set containing all members from
self
and the other object except those common to both. -
#~ (aliased as #complement): Returns a new set containing all members that are not in
self
-
#limit: Returns a copy of
self
which has replaced*
with a given maximum value and removed all members over that maximum.
Methods for Assigning
These methods add or replace elements in self
.
-
#add (aliased as #<<): Adds a given object to the set; returns
self
. -
#add?: If the given object is not an element in the set, adds it and returns
self
; otherwise, returnsnil
. -
#merge: Merges multiple elements into the set; returns
self
. -
#append: Adds a given object to the set, appending it to the existing string, and returns
self
. -
#string=: Assigns a new #string value and replaces #elements to match.
-
#replace: Replaces the contents of the set with the contents of a given object.
-
#complement!: Replaces the contents of the set with its own #complement.
Methods for Deleting
These methods remove elements from self
.
-
#clear: Removes all elements in the set; returns
self
. -
#delete: Removes a given object from the set; returns
self
. -
#delete?: If the given object is an element in the set, removes it and returns it; otherwise, returns
nil
. -
#delete_at: Removes the number at a given offset.
-
#slice!: Removes the number or consecutive numbers at a given offset or range of offsets.
-
#subtract: Removes each given object from the set; returns
self
. -
#limit!: Replaces
*
with a given maximum value and removes all members over that maximum; returnsself
.
Methods for IMAP String Formatting
-
#to_s: Returns the
sequence-set
string, or an empty string when the set is empty. -
#string: Returns the
sequence-set
string, or nil when empty. -
#valid_string: Returns the
sequence-set
string, or raises DataFormatError when the set is empty. -
#normalized_string: Returns a
sequence-set
string with its elements sorted and coalesced, or nil when the set is empty. -
#normalize: Returns a new set with this set’s normalized
sequence-set
representation. -
#normalize!: Updates #string to its normalized
sequence-set
representation and returnsself
.
Constant Summary collapse
- UINT32_MAX =
The largest possible non-zero unsigned 32-bit integer
2**32 - 1
Class Method Summary collapse
-
.[](first, *rest) ⇒ Object
:call-seq: SequenceSet -> valid frozen sequence set.
-
.empty ⇒ Object
Returns a frozen empty set singleton.
-
.full ⇒ Object
Returns a frozen full set singleton:
"1:*"
. -
.try_convert(obj) ⇒ Object
:call-seq: SequenceSet.try_convert(obj) -> sequence set or nil.
Instance Method Summary collapse
-
#&(other) ⇒ Object
(also: #intersection)
:call-seq: self & other -> sequence set intersection(other) -> sequence set.
-
#-(other) ⇒ Object
(also: #difference)
:call-seq: self - other -> sequence set difference(other) -> sequence set.
-
#==(other) ⇒ Object
:call-seq: self == other -> true or false.
-
#===(other) ⇒ Object
:call-seq: self === other -> true | false | nil.
- #[](index, length = nil) ⇒ Object (also: #slice)
-
#^(other) ⇒ Object
(also: #xor)
:call-seq: self ^ other -> sequence set xor(other) -> sequence set.
-
#add(object) ⇒ Object
(also: #<<)
:call-seq: add(object) -> self self << other -> self.
-
#add?(object) ⇒ Boolean
:call-seq: add?(object) -> self or nil.
-
#append(object) ⇒ Object
Adds a range or number to the set and returns
self
. -
#at(index) ⇒ Object
:call-seq: at(index) -> integer or nil.
-
#clear ⇒ Object
Removes all elements and returns self.
-
#complement! ⇒ Object
:call-seq: complement! -> self.
-
#count ⇒ Object
(also: #size)
Returns the count of #numbers in the set.
-
#cover?(other) ⇒ Boolean
:call-seq: cover?(other) -> true | false | nil.
-
#deconstruct ⇒ Object
Returns an array with #normalized_string when valid and an empty array otherwise.
-
#delete(object) ⇒ Object
:call-seq: delete(object) -> self.
-
#delete?(object) ⇒ Boolean
:call-seq: delete?(number) -> integer or nil delete?(star) -> :* or nil delete?(range) -> sequence set or nil.
-
#delete_at(index) ⇒ Object
:call-seq: delete_at(index) -> number or :* or nil.
-
#disjoint?(other) ⇒ Boolean
Returns
true
if the set and a given object have no common elements,false
otherwise. -
#each_element ⇒ Object
Yields each number or range (or
:*
) in #elements to the block and returns self. -
#each_entry(&block) ⇒ Object
Yields each number or range in #string to the block and returns
self
. -
#each_number(&block) ⇒ Object
Yields each number in #numbers to the block and returns self.
-
#each_range ⇒ Object
Yields each range in #ranges to the block and returns self.
-
#elements ⇒ Object
(also: #to_a)
Returns an array of ranges and integers and
:*
. -
#empty? ⇒ Boolean
Returns true if the set contains no elements.
-
#entries ⇒ Object
Returns an array of ranges and integers and
:*
. -
#eql?(other) ⇒ Boolean
:call-seq: eql?(other) -> true or false.
-
#find_index(number) ⇒ Object
Returns the index of
number
in the set, ornil
ifnumber
isn’t in the set. -
#freeze ⇒ Object
Freezes and returns the set.
-
#full? ⇒ Boolean
Returns true if the set contains every possible element.
-
#hash ⇒ Object
See #eql?.
-
#include?(element) ⇒ Boolean
(also: #member?)
Returns
true
when a given number or range is inself
, andfalse
otherwise. -
#include_star? ⇒ Boolean
Returns
true
when the set contains*
. -
#initialize(input = nil) ⇒ SequenceSet
constructor
Create a new SequenceSet object from
input
, which may be another SequenceSet, an IMAP formattedsequence-set
string, a number, a range,:*
, or an enumerable of these. - #inspect ⇒ Object
-
#intersect?(other) ⇒ Boolean
(also: #overlap?)
Returns
true
if the set and a given object have any common elements,false
otherwise. -
#limit(max:) ⇒ Object
Returns a frozen SequenceSet with
*
converted tomax
, numbers and ranges overmax
removed, and ranges containingmax
converted to end atmax
. -
#limit!(max:) ⇒ Object
Removes all members over
max
and returns self. -
#max(star: :*) ⇒ Object
:call-seq: max(star: :*) => integer or star or nil.
-
#merge(*inputs) ⇒ Object
Merges all of the elements that appear in any of the
inputs
into the set, and returnsself
. -
#min(star: :*) ⇒ Object
:call-seq: min(star: :*) => integer or star or nil.
-
#minmax(star: :*) ⇒ Object
:call-seq: minmax(star: :*) => nil or [integer, integer or star].
-
#normalize ⇒ Object
Returns a new SequenceSet with a normalized string representation.
-
#normalize! ⇒ Object
Resets #string to be sorted, deduplicated, and coalesced.
-
#normalized_string ⇒ Object
Returns a normalized
sequence-set
string representation, sorted and deduplicated. -
#numbers ⇒ Object
Returns a sorted array of all of the number values in the sequence set.
-
#ranges ⇒ Object
Returns an array of ranges.
-
#replace(other) ⇒ Object
Replace the contents of the set with the contents of
other
and returnsself
. -
#send_data(imap, tag) ⇒ Object
Unstable API: for internal use only (Net::IMAP#send_data).
-
#slice!(index, length = nil) ⇒ Object
:call-seq: slice!(index) -> integer or :* or nil slice!(start, length) -> sequence set or nil slice!(range) -> sequence set or nil.
-
#string ⇒ Object
Returns the IMAP
sequence-set
string representation, ornil
when the set is empty. -
#string=(str) ⇒ Object
Assigns a new string to #string and resets #elements to match.
-
#subtract(*objects) ⇒ Object
Removes all of the elements that appear in any of the given
objects
from the set, and returnsself
. -
#to_s ⇒ Object
Returns the IMAP
sequence-set
string representation, or an empty string when the set is empty. -
#to_set ⇒ Object
Returns a Set with all of the #numbers in the sequence set.
-
#valid? ⇒ Boolean
Returns false when the set is empty.
-
#valid_string ⇒ Object
Returns the IMAP
sequence-set
string representation, or raises a DataFormatError when the set is empty. -
#validate ⇒ Object
Unstable API: currently for internal use only (Net::IMAP#validate_data).
-
#|(other) ⇒ Object
(also: #+, #union)
:call-seq: self + other -> sequence set self | other -> sequence set union(other) -> sequence set.
-
#~ ⇒ Object
(also: #complement)
:call-seq: ~ self -> sequence set complement -> sequence set.
Constructor Details
#initialize(input = nil) ⇒ SequenceSet
Create a new SequenceSet object from input
, which may be another SequenceSet, an IMAP formatted sequence-set
string, a number, a range, :*
, or an enumerable of these.
Use ::[] to create a frozen (non-empty) SequenceSet.
340 |
# File 'lib/net/imap/sequence_set.rb', line 340 def initialize(input = nil) input ? replace(input) : clear end |
Class Method Details
.[](first, *rest) ⇒ Object
:call-seq:
SequenceSet[*values] -> valid frozen sequence set
Returns a frozen SequenceSet, constructed from values
.
An empty SequenceSet is invalid and will raise a DataFormatError.
Use ::new to create a mutable or empty SequenceSet.
297 298 299 300 301 302 303 304 305 306 307 |
# File 'lib/net/imap/sequence_set.rb', line 297 def [](first, *rest) if rest.empty? if first.is_a?(SequenceSet) && first.frozen? && first.valid? first else new(first).validate.freeze end else new(first).merge(*rest).validate.freeze end end |
.empty ⇒ Object
Returns a frozen empty set singleton. Note that valid IMAP sequence sets cannot be empty, so this set is invalid.
328 |
# File 'lib/net/imap/sequence_set.rb', line 328 def empty; EMPTY end |
.full ⇒ Object
Returns a frozen full set singleton: "1:*"
331 |
# File 'lib/net/imap/sequence_set.rb', line 331 def full; FULL end |
.try_convert(obj) ⇒ Object
:call-seq:
SequenceSet.try_convert(obj) -> sequence set or nil
If obj
is a SequenceSet, returns obj
. If obj
responds_to to_sequence_set
, calls obj.to_sequence_set
and returns the result. Otherwise returns nil
.
If obj.to_sequence_set
doesn’t return a SequenceSet, an exception is raised.
318 319 320 321 322 323 324 |
# File 'lib/net/imap/sequence_set.rb', line 318 def try_convert(obj) return obj if obj.is_a?(SequenceSet) return nil unless obj.respond_to?(:to_sequence_set) obj = obj.to_sequence_set return obj if obj.is_a?(SequenceSet) raise DataFormatError, "invalid object returned from to_sequence_set" end |
Instance Method Details
#&(other) ⇒ Object Also known as: intersection
:call-seq:
self & other -> sequence set
intersection(other) -> sequence set
Returns a new sequence set containing only the numbers common to this set and other
.
other
may be any object that would be accepted by ::new: a non-zero 32 bit unsigned integer, range, sequence-set
formatted string, another sequence set, or an enumerable containing any of these.
Net::IMAP::SequenceSet[1..5] & [2, 4, 6]
#=> Net::IMAP::SequenceSet["2,4"]
(seqset & other)
is equivalent to (seqset - ~other)
.
619 620 621 |
# File 'lib/net/imap/sequence_set.rb', line 619 def &(other) remain_frozen dup.subtract SequenceSet.new(other).complement! end |
#-(other) ⇒ Object Also known as: difference
:call-seq:
self - other -> sequence set
difference(other) -> sequence set
Returns a new sequence set built by duplicating this set and removing every number that appears in other
.
other
may be any object that would be accepted by ::new: a non-zero 32 bit unsigned integer, range, sequence-set
formatted string, another sequence set, or an enumerable containing any of these.
Net::IMAP::SequenceSet[1..5] - 2 - 4 - 6
#=> Net::IMAP::SequenceSet["1,3,5"]
Related: #subtract
601 |
# File 'lib/net/imap/sequence_set.rb', line 601 def -(other) remain_frozen dup.subtract other end |
#==(other) ⇒ Object
:call-seq: self == other -> true or false
Returns true when the other SequenceSet represents the same message identifiers. Encoding difference—such as order, overlaps, or duplicates—are ignored.
Net::IMAP::SequenceSet["1:3"] == Net::IMAP::SequenceSet["1:3"]
#=> true
Net::IMAP::SequenceSet["1,2,3"] == Net::IMAP::SequenceSet["1:3"]
#=> true
Net::IMAP::SequenceSet["1,3"] == Net::IMAP::SequenceSet["3,1"]
#=> true
Net::IMAP::SequenceSet["9,1:*"] == Net::IMAP::SequenceSet["1:*"]
#=> true
Related: #eql?, #normalize
437 438 439 440 |
# File 'lib/net/imap/sequence_set.rb', line 437 def ==(other) self.class == other.class && (to_s == other.to_s || tuples == other.tuples) end |
#===(other) ⇒ Object
:call-seq: self === other -> true | false | nil
Returns whether other
is contained within the set. Returns nil
if a StandardError is raised while converting other
to a comparable type.
Related: #cover?, #include?, #include_star?
467 468 469 470 471 |
# File 'lib/net/imap/sequence_set.rb', line 467 def ===(other) cover?(other) rescue nil end |
#[](index, length = nil) ⇒ Object Also known as: slice
:call-seq:
seqset[index] -> integer or :* or nil
slice(index) -> integer or :* or nil
seqset[start, length] -> sequence set or nil
slice(start, length) -> sequence set or nil
seqset[range] -> sequence set or nil
slice(range) -> sequence set or nil
Returns a number or a subset from self
, without modifying the set.
When an Integer argument index
is given, the number at offset index
is returned:
set = Net::IMAP::SequenceSet["10:15,20:23,26"]
set[0] #=> 10
set[5] #=> 15
set[10] #=> 26
If index
is negative, it counts relative to the end of self
:
set = Net::IMAP::SequenceSet["10:15,20:23,26"]
set[-1] #=> 26
set[-3] #=> 22
set[-6] #=> 15
If index
is out of range, nil
is returned.
set = Net::IMAP::SequenceSet["10:15,20:23,26"]
set[11] #=> nil
set[-12] #=> nil
The result is based on the normalized set—sorted and de-duplicated—not on the assigned value of #string.
set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
set[0] #=> 11
set[-1] #=> 23
1085 1086 1087 1088 1089 1090 |
# File 'lib/net/imap/sequence_set.rb', line 1085 def [](index, length = nil) if length then slice_length(index, length) elsif index.is_a?(Range) then slice_range(index) else at(index) end end |
#^(other) ⇒ Object Also known as: xor
:call-seq:
self ^ other -> sequence set
xor(other) -> sequence set
Returns a new sequence set containing numbers that are exclusive between this set and other
.
other
may be any object that would be accepted by ::new: a non-zero 32 bit unsigned integer, range, sequence-set
formatted string, another sequence set, or an enumerable containing any of these.
Net::IMAP::SequenceSet[1..5] ^ [2, 4, 6]
#=> Net::IMAP::SequenceSet["1,3,5:6"]
(seqset ^ other)
is equivalent to ((seqset | other) - (seqset & other))
.
640 |
# File 'lib/net/imap/sequence_set.rb', line 640 def ^(other) remain_frozen (self | other).subtract(self & other) end |
#add(object) ⇒ Object Also known as: <<
:call-seq:
add(object) -> self
self << other -> self
Adds a range or number to the set and returns self
.
#string will be regenerated. Use #merge to add many elements at once.
Related: #add?, #merge, #union
670 671 672 673 |
# File 'lib/net/imap/sequence_set.rb', line 670 def add(object) tuple_add input_to_tuple object normalize! end |
#add?(object) ⇒ Boolean
:call-seq: add?(object) -> self or nil
Adds a range or number to the set and returns self
. Returns nil
when the object is already included in the set.
#string will be regenerated. Use #merge to add many elements at once.
Related: #add, #merge, #union, #include?
697 698 699 |
# File 'lib/net/imap/sequence_set.rb', line 697 def add?(object) add object unless include? object end |
#append(object) ⇒ Object
Adds a range or number to the set and returns self
.
Unlike #add, #merge, or #union, the new value is appended to #string. This may result in a #string which has duplicates or is out-of-order.
680 681 682 683 684 685 686 687 |
# File 'lib/net/imap/sequence_set.rb', line 680 def append(object) tuple = input_to_tuple object entry = tuple_to_str tuple tuple_add tuple @string = -(string ? "#{@string},#{entry}" : entry) self end |
#at(index) ⇒ Object
:call-seq: at(index) -> integer or nil
Returns a number from self
, without modifying the set. Behaves the same as #[], except that #at only allows a single integer argument.
Related: #[], #slice
1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 |
# File 'lib/net/imap/sequence_set.rb', line 1034 def at(index) index = Integer(index.to_int) if index.negative? reverse_each_tuple_with_index do |min, max, idx_min, idx_max| idx_min <= index and return from_tuple_int(min + (index - idx_min)) end else each_tuple_with_index do |min, _, idx_min, idx_max| index <= idx_max and return from_tuple_int(min + (index - idx_min)) end end nil end |
#clear ⇒ Object
Removes all elements and returns self.
343 |
# File 'lib/net/imap/sequence_set.rb', line 343 def clear; @tuples, @string = [], nil; self end |
#complement! ⇒ Object
:call-seq: complement! -> self
Converts the SequenceSet to its own #complement. It will contain all possible values except for those currently in the set.
Related: #complement
1165 1166 1167 1168 1169 1170 1171 1172 1173 |
# File 'lib/net/imap/sequence_set.rb', line 1165 def complement! return replace(self.class.full) if empty? return clear if full? flat = @tuples.flat_map { [_1 - 1, _2 + 1] } if flat.first < 1 then flat.shift else flat.unshift 1 end if STAR_INT < flat.last then flat.pop else flat.push STAR_INT end @tuples = flat.each_slice(2).to_a normalize! end |
#count ⇒ Object Also known as: size
Returns the count of #numbers in the set.
If *
and 2**32 - 1
(the maximum 32-bit unsigned integer value) are both in the set, they will only be counted once.
986 987 988 989 |
# File 'lib/net/imap/sequence_set.rb', line 986 def count @tuples.sum(@tuples.count) { _2 - _1 } + (include_star? && include?(UINT32_MAX) ? -1 : 0) end |
#cover?(other) ⇒ Boolean
:call-seq: cover?(other) -> true | false | nil
Returns whether other
is contained within the set. other
may be any object that would be accepted by ::new.
Related: #===, #include?, #include_star?
479 |
# File 'lib/net/imap/sequence_set.rb', line 479 def cover?(other) input_to_tuples(other).none? { !include_tuple?(_1) } end |
#deconstruct ⇒ Object
Returns an array with #normalized_string when valid and an empty array otherwise.
386 |
# File 'lib/net/imap/sequence_set.rb', line 386 def deconstruct; valid? ? [normalized_string] : [] end |
#delete(object) ⇒ Object
:call-seq: delete(object) -> self
Deletes the given range or number from the set and returns self
.
#string will be regenerated after deletion. Use #subtract to remove many elements at once.
Related: #delete?, #delete_at, #subtract, #difference
709 710 711 712 |
# File 'lib/net/imap/sequence_set.rb', line 709 def delete(object) tuple_subtract input_to_tuple object normalize! end |
#delete?(object) ⇒ Boolean
:call-seq:
delete?(number) -> integer or nil
delete?(star) -> :* or nil
delete?(range) -> sequence set or nil
Removes a specified value from the set, and returns the removed value. Returns nil
if nothing was removed.
Returns an integer when the specified number
argument was removed:
set = Net::IMAP::SequenceSet.new [5..10, 20]
set.delete?(7) #=> 7
set #=> #<Net::IMAP::SequenceSet "5:6,8:10,20">
set.delete?("20") #=> 20
set #=> #<Net::IMAP::SequenceSet "5:6,8:10">
set.delete?(30) #=> nil
Returns :*
when *
or -1
is specified and removed:
set = Net::IMAP::SequenceSet.new "5:9,20,35,*"
set.delete?(-1) #=> :*
set #=> #<Net::IMAP::SequenceSet "5:9,20,35">
And returns a new SequenceSet when a range is specified:
set = Net::IMAP::SequenceSet.new [5..10, 20]
set.delete?(9..) #=> #<Net::IMAP::SequenceSet "9:10,20">
set #=> #<Net::IMAP::SequenceSet "5:8">
set.delete?(21..) #=> nil
#string will be regenerated after deletion.
Related: #delete, #delete_at, #subtract, #difference, #disjoint?
746 747 748 749 750 751 752 753 754 755 756 757 758 759 |
# File 'lib/net/imap/sequence_set.rb', line 746 def delete?(object) tuple = input_to_tuple object if tuple.first == tuple.last return unless include_tuple? tuple tuple_subtract tuple normalize! from_tuple_int tuple.first else copy = dup tuple_subtract tuple normalize! copy if copy.subtract(self).valid? end end |
#delete_at(index) ⇒ Object
:call-seq: delete_at(index) -> number or :* or nil
Deletes a number the set, indicated by the given index
. Returns the number that was removed, or nil
if nothing was removed.
#string will be regenerated after deletion.
Related: #delete, #delete?, #slice!, #subtract, #difference
769 770 771 |
# File 'lib/net/imap/sequence_set.rb', line 769 def delete_at(index) slice! Integer(index.to_int) end |
#disjoint?(other) ⇒ Boolean
Returns true
if the set and a given object have no common elements, false
otherwise.
Net::IMAP::SequenceSet["5:10"].disjoint? "7,9,11" #=> false
Net::IMAP::SequenceSet["5:10"].disjoint? "11:33" #=> true
Related: #intersection, #intersect?
531 532 533 |
# File 'lib/net/imap/sequence_set.rb', line 531 def disjoint?(other) empty? || input_to_tuples(other).none? { intersect_tuple? _1 } end |
#each_element ⇒ Object
Yields each number or range (or :*
) in #elements to the block and returns self. Returns an enumerator when called without a block.
The returned numbers are sorted and de-duplicated, even when the input #string is not. See #normalize.
Related: #elements, #each_entry
921 922 923 924 925 |
# File 'lib/net/imap/sequence_set.rb', line 921 def each_element # :yields: integer or range or :* return to_enum(__method__) unless block_given? @tuples.each do yield tuple_to_entry _1 end self end |
#each_entry(&block) ⇒ Object
Yields each number or range in #string to the block and returns self
. Returns an enumerator when called without a block.
The entries are yielded in the same order they appear in #string, with no sorting, deduplication, or coalescing. When #string is in its normalized form, this will yield the same values as #each_element.
Related: #entries, #each_element
907 908 909 910 911 912 |
# File 'lib/net/imap/sequence_set.rb', line 907 def each_entry(&block) # :yields: integer or range or :* return to_enum(__method__) unless block_given? return each_element(&block) unless @string @string.split(",").each do yield tuple_to_entry str_to_tuple _1 end self end |
#each_number(&block) ⇒ Object
Yields each number in #numbers 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: #numbers
961 962 963 964 965 966 967 968 969 970 971 |
# File 'lib/net/imap/sequence_set.rb', line 961 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 self end |
#each_range ⇒ Object
Yields each range in #ranges to the block and returns self. Returns an enumerator when called without a block.
Related: #ranges
943 944 945 946 947 948 949 950 951 952 |
# File 'lib/net/imap/sequence_set.rb', line 943 def each_range # :yields: range return to_enum(__method__) unless block_given? @tuples.each do |min, max| if min == STAR_INT then yield :*.. elsif max == STAR_INT then yield min.. else yield min..max end end self end |
#elements ⇒ Object Also known as: to_a
Returns an array of ranges and integers and :*
.
The returned elements are sorted and coalesced, even when the input #string is not. *
will sort last. See #normalize.
By itself, *
translates to :*
. A range containing *
translates to an endless range. Use #limit to translate both cases to a maximum value.
If the original input was unordered or contains overlapping ranges, the returned ranges will be ordered and coalesced.
Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
#=> [2, 5..9, 11..12, :*]
Related: #each_element, #ranges, #numbers
848 |
# File 'lib/net/imap/sequence_set.rb', line 848 def elements; each_element.to_a end |
#empty? ⇒ Boolean
Returns true if the set contains no elements
561 |
# File 'lib/net/imap/sequence_set.rb', line 561 def empty?; @tuples.empty? end |
#entries ⇒ Object
Returns an array of ranges and integers and :*
.
The entries are in the same order they appear in #string, with no sorting, deduplication, or coalescing. When #string is in its normalized form, this will return the same result as #elements. This is useful when the given order is significant, for example in a ESEARCH response to IMAP#sort.
Related: #each_entry, #elements
830 |
# File 'lib/net/imap/sequence_set.rb', line 830 def entries; each_entry.to_a end |
#eql?(other) ⇒ Boolean
:call-seq: eql?(other) -> true or false
Hash equality requires the same encoded #string representation.
Net::IMAP::SequenceSet["1:3"] .eql? Net::IMAP::SequenceSet["1:3"]
#=> true
Net::IMAP::SequenceSet["1,2,3"].eql? Net::IMAP::SequenceSet["1:3"]
#=> false
Net::IMAP::SequenceSet["1,3"] .eql? Net::IMAP::SequenceSet["3,1"]
#=> false
Net::IMAP::SequenceSet["9,1:*"].eql? Net::IMAP::SequenceSet["1:*"]
#=> false
Related: #==, #normalize
456 |
# File 'lib/net/imap/sequence_set.rb', line 456 def eql?(other) self.class == other.class && string == other.string end |
#find_index(number) ⇒ Object
Returns the index of number
in the set, or nil
if number
isn’t in the set.
Related: #[]
997 998 999 1000 1001 1002 1003 1004 |
# File 'lib/net/imap/sequence_set.rb', line 997 def find_index(number) number = to_tuple_int number each_tuple_with_index do |min, max, idx_min| number < min and return nil number <= max and return from_tuple_int(idx_min + (number - min)) end nil end |
#freeze ⇒ Object
Freezes and returns the set. A frozen SequenceSet is Ractor-safe.
414 415 416 417 418 419 |
# File 'lib/net/imap/sequence_set.rb', line 414 def freeze return self if frozen? string @tuples.each(&:freeze).freeze super end |
#full? ⇒ Boolean
Returns true if the set contains every possible element.
564 |
# File 'lib/net/imap/sequence_set.rb', line 564 def full?; @tuples == [[1, STAR_INT]] end |
#hash ⇒ Object
See #eql?
459 |
# File 'lib/net/imap/sequence_set.rb', line 459 def hash; [self.class, string].hash end |
#include?(element) ⇒ Boolean Also known as: member?
Returns true
when a given number or range is in self
, and false
otherwise. Returns false
unless number
is an Integer, Range, or *
.
set = Net::IMAP::SequenceSet["5:10,100,111:115"]
set.include? 1 #=> false
set.include? 5..10 #=> true
set.include? 11..20 #=> false
set.include? 100 #=> true
set.include? 6 #=> true, covered by "5:10"
set.include? 4..9 #=> true, covered by "5:10"
set.include? "4:9" #=> true, strings are parsed
set.include? 4..9 #=> false, intersection is not sufficient
set.include? "*" #=> false, use #limit to re-interpret "*"
set.include? -1 #=> false, -1 is interpreted as "*"
set = Net::IMAP::SequenceSet["5:10,100,111:*"]
set.include? :* #=> true
set.include? "*" #=> true
set.include? -1 #=> true
set.include? 200.. #=> true
set.include? 100.. #=> false
Related: #include_star?, #cover?, #===
505 |
# File 'lib/net/imap/sequence_set.rb', line 505 def include?(element) include_tuple? input_to_tuple element end |
#include_star? ⇒ Boolean
Returns true
when the set contains *
.
510 |
# File 'lib/net/imap/sequence_set.rb', line 510 def include_star?; @tuples.last&.last == STAR_INT end |
#inspect ⇒ Object
1211 1212 1213 1214 1215 1216 1217 1218 1219 |
# File 'lib/net/imap/sequence_set.rb', line 1211 def inspect if empty? (frozen? ? "%s.empty" : "#<%s empty>") % [self.class] elsif frozen? "%s[%p]" % [self.class, to_s] else "#<%s %p>" % [self.class, to_s] end end |
#intersect?(other) ⇒ Boolean Also known as: overlap?
Returns true
if the set and a given object have any common elements, false
otherwise.
Net::IMAP::SequenceSet["5:10"].intersect? "7,9,11" #=> true
Net::IMAP::SequenceSet["5:10"].intersect? "11:33" #=> false
Related: #intersection, #disjoint?
519 520 521 |
# File 'lib/net/imap/sequence_set.rb', line 519 def intersect?(other) valid? && input_to_tuples(other).any? { intersect_tuple? _1 } end |
#limit(max:) ⇒ Object
Returns a frozen SequenceSet with *
converted to max
, numbers and ranges over max
removed, and ranges containing max
converted to end at max
.
Net::IMAP::SequenceSet["5,10:22,50"].limit(max: 20).to_s
#=> "5,10:20"
*
is always interpreted as the maximum value. When the set contains *
, it will be set equal to the limit.
Net::IMAP::SequenceSet["*"].limit(max: 37)
#=> Net::IMAP::SequenceSet["37"]
Net::IMAP::SequenceSet["5:*"].limit(max: 37)
#=> Net::IMAP::SequenceSet["5:37"]
Net::IMAP::SequenceSet["500:*"].limit(max: 37)
#=> Net::IMAP::SequenceSet["37"]
1138 1139 1140 1141 1142 1143 1144 1145 |
# File 'lib/net/imap/sequence_set.rb', line 1138 def limit(max:) max = to_tuple_int(max) if empty? then self.class.empty elsif !include_star? && max < min then self.class.empty elsif max(star: STAR_INT) <= max then frozen? ? self : dup.freeze else dup.limit!(max: max).freeze end end |
#limit!(max:) ⇒ Object
Removes all members over max
and returns self. If *
is a member, it will be converted to max
.
Related: #limit
1151 1152 1153 1154 1155 1156 1157 |
# File 'lib/net/imap/sequence_set.rb', line 1151 def limit!(max:) star = include_star? max = to_tuple_int(max) tuple_subtract [max + 1, STAR_INT] tuple_add [max, max ] if star normalize! end |
#max(star: :*) ⇒ Object
:call-seq: max(star: :*) => integer or star or nil
Returns the maximum value in self
, star
when the set includes *
, or nil
when the set is empty.
539 540 541 |
# File 'lib/net/imap/sequence_set.rb', line 539 def max(star: :*) (val = @tuples.last&.last) && val == STAR_INT ? star : val end |
#merge(*inputs) ⇒ Object
Merges all of the elements that appear in any of the inputs
into the set, and returns self
.
The inputs
may be any objects that would be accepted by ::new: non-zero 32 bit unsigned integers, ranges, sequence-set
formatted strings, other sequence sets, or enumerables containing any of these.
#string will be regenerated after all inputs have been merged.
Related: #add, #add?, #union
802 803 804 805 |
# File 'lib/net/imap/sequence_set.rb', line 802 def merge(*inputs) tuples_add input_to_tuples inputs normalize! end |
#min(star: :*) ⇒ Object
:call-seq: min(star: :*) => integer or star or nil
Returns the minimum value in self
, star
when the only value in the set is *
, or nil
when the set is empty.
547 548 549 |
# File 'lib/net/imap/sequence_set.rb', line 547 def min(star: :*) (val = @tuples.first&.first) && val == STAR_INT ? star : val end |
#minmax(star: :*) ⇒ Object
:call-seq: minmax(star: :*) => nil or [integer, integer or star]
Returns a 2-element array containing the minimum and maximum numbers in self
, or nil
when the set is empty.
555 |
# File 'lib/net/imap/sequence_set.rb', line 555 def minmax(star: :*); [min(star: star), max(star: star)] unless empty? end |
#normalize ⇒ Object
Returns a new SequenceSet with a normalized string representation.
The returned set’s #string is sorted and deduplicated. Adjacent or overlapping elements will be merged into a single larger range.
Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalize
#=> Net::IMAP::SequenceSet["1:7,9:11"]
Related: #normalize!, #normalized_string
1184 1185 1186 1187 1188 |
# File 'lib/net/imap/sequence_set.rb', line 1184 def normalize str = normalized_string return self if frozen? && str == string remain_frozen dup.instance_exec { @string = str&.-@; self } end |
#normalize! ⇒ Object
Resets #string to be sorted, deduplicated, and coalesced. Returns self
.
Related: #normalize, #normalized_string
1194 1195 1196 1197 |
# File 'lib/net/imap/sequence_set.rb', line 1194 def normalize! @string = nil self end |
#normalized_string ⇒ Object
Returns a normalized sequence-set
string representation, sorted and deduplicated. Adjacent or overlapping elements will be merged into a single larger range. Returns nil
when the set is empty.
Net::IMAP::SequenceSet["1:5,3:7,10:9,10:11"].normalized_string
#=> "1:7,9:11"
Related: #normalize!, #normalize
1207 1208 1209 |
# File 'lib/net/imap/sequence_set.rb', line 1207 def normalized_string @tuples.empty? ? nil : -@tuples.map { tuple_to_str _1 }.join(",") end |
#numbers ⇒ Object
Returns a sorted array of all of the number values in the sequence set.
The returned numbers are sorted and de-duplicated, even when the input #string is not. See #normalize.
Net::IMAP::SequenceSet["2,5:9,6,12:11"].numbers
#=> [2, 5, 6, 7, 8, 9, 11, 12]
If the set contains a *
, RangeError is raised. See #limit.
Net::IMAP::SequenceSet["10000:*"].numbers
#!> RangeError
WARNING: Even excluding sets with *
, an enormous result can easily be created. An array with over 4 billion integers could be returned, requiring up to 32GiB of memory on a 64-bit architecture.
Net::IMAP::SequenceSet[10000..2**32-1].numbers
# ...probably freezes the process for a while...
#!> NoMemoryError (probably)
For safety, consider using #limit or #intersection to set an upper bound. Alternatively, use #each_element, #each_range, or even #each_number to avoid allocation of a result array.
Related: #elements, #ranges, #to_set
897 |
# File 'lib/net/imap/sequence_set.rb', line 897 def numbers; each_number.to_a end |
#ranges ⇒ Object
Returns an array of ranges
The returned elements are sorted and coalesced, even when the input #string is not. *
will sort last. See #normalize.
*
translates to an endless range. By itself, *
translates to :*..
. Use #limit to set *
to a maximum value.
The returned ranges will be ordered and coalesced, even when the input #string is not. *
will sort last. See #normalize.
Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].ranges
#=> [2..2, 5..9, 11..12, :*..]
Net::IMAP::SequenceSet["123,999:*,456:789"].ranges
#=> [123..123, 456..789, 999..]
Related: #each_range, #elements, #numbers, #to_set
869 |
# File 'lib/net/imap/sequence_set.rb', line 869 def ranges; each_range.to_a end |
#replace(other) ⇒ Object
Replace the contents of the set with the contents of other
and returns self
.
other
may be another SequenceSet, or it may be an IMAP sequence-set
string, a number, a range, *
, or an enumerable of these.
350 351 352 353 354 355 356 357 |
# File 'lib/net/imap/sequence_set.rb', line 350 def replace(other) case other when SequenceSet then initialize_dup(other) when String then self.string = other else clear; merge other end self end |
#send_data(imap, tag) ⇒ Object
Unstable API: for internal use only (Net::IMAP#send_data)
1231 1232 1233 |
# File 'lib/net/imap/sequence_set.rb', line 1231 def send_data(imap, tag) # :nodoc: imap.__send__(:put_string, valid_string) end |
#slice!(index, length = nil) ⇒ Object
:call-seq:
slice!(index) -> integer or :* or nil
slice!(start, length) -> sequence set or nil
slice!(range) -> sequence set or nil
Deletes a number or consecutive numbers from the set, indicated by the given index
, start
and length
, or range
of offsets. Returns the number or sequence set that was removed, or nil
if nothing was removed. Arguments are interpreted the same as for #slice or #[].
#string will be regenerated after deletion.
Related: #slice, #delete_at, #delete, #delete?, #subtract, #difference
786 787 788 789 |
# File 'lib/net/imap/sequence_set.rb', line 786 def slice!(index, length = nil) deleted = slice(index, length) and subtract deleted deleted end |
#string ⇒ Object
Returns the IMAP sequence-set
string representation, or nil
when the set is empty. Note that an empty set is invalid in the IMAP syntax.
Use #valid_string to raise an exception when the set is empty, or #to_s to return an empty string.
If the set was created from a single string, it is not normalized. If the set is updated the string will be normalized.
Related: #valid_string, #normalized_string, #to_s
382 |
# File 'lib/net/imap/sequence_set.rb', line 382 def string; @string ||= normalized_string if valid? end |
#string=(str) ⇒ Object
Assigns a new string to #string and resets #elements to match. It cannot be set to an empty string—assign nil
or use #clear instead. The string is validated but not normalized.
Use #add or #merge to add a string to an existing set.
Related: #replace, #clear
395 396 397 398 399 400 401 402 403 404 |
# File 'lib/net/imap/sequence_set.rb', line 395 def string=(str) if str.nil? clear else str = String.try_convert(str) or raise ArgumentError, "not a string" tuples = str_to_tuples str @tuples, @string = [], -str tuples_add tuples end end |
#subtract(*objects) ⇒ Object
Removes all of the elements that appear in any of the given objects
from the set, and returns self
.
The objects
may be any objects that would be accepted by ::new: non-zero 32 bit unsigned integers, ranges, sequence-set
formatted strings, other sequence sets, or enumerables containing any of these.
Related: #difference
816 817 818 819 |
# File 'lib/net/imap/sequence_set.rb', line 816 def subtract(*objects) tuples_subtract input_to_tuples objects normalize! end |
#to_s ⇒ Object
Returns the IMAP sequence-set
string representation, or an empty string when the set is empty. Note that an empty set is invalid in the IMAP syntax.
Related: #valid_string, #normalized_string, #to_s
411 |
# File 'lib/net/imap/sequence_set.rb', line 411 def to_s; string || "" end |
#to_set ⇒ Object
Returns a Set with all of the #numbers in the sequence set.
If the set contains a *
, RangeError will be raised.
See #numbers for the warning about very large sets.
Related: #elements, #ranges, #numbers
980 |
# File 'lib/net/imap/sequence_set.rb', line 980 def to_set; Set.new(numbers) end |
#valid? ⇒ Boolean
Returns false when the set is empty.
558 |
# File 'lib/net/imap/sequence_set.rb', line 558 def valid?; !empty? end |
#valid_string ⇒ Object
Returns the IMAP sequence-set
string representation, or raises a DataFormatError when the set is empty.
Use #string to return nil
or #to_s to return an empty string without error.
Related: #string, #normalized_string, #to_s
366 367 368 369 |
# File 'lib/net/imap/sequence_set.rb', line 366 def valid_string raise DataFormatError, "empty sequence-set" if empty? string end |
#validate ⇒ Object
Unstable API: currently for internal use only (Net::IMAP#validate_data)
1225 1226 1227 1228 |
# File 'lib/net/imap/sequence_set.rb', line 1225 def validate # :nodoc: empty? and raise DataFormatError, "empty sequence-set is invalid" self end |
#|(other) ⇒ Object Also known as: +, union
:call-seq:
self + other -> sequence set
self | other -> sequence set
union(other) -> sequence set
Returns a new sequence set that has every number in the other
object added.
other
may be any object that would be accepted by ::new: a non-zero 32 bit unsigned integer, range, sequence-set
formatted string, another sequence set, or an enumerable containing any of these.
Net::IMAP::SequenceSet["1:5"] | 2 | [4..6, 99]
#=> Net::IMAP::SequenceSet["1:6,99"]
Related: #add, #merge
582 |
# File 'lib/net/imap/sequence_set.rb', line 582 def |(other) remain_frozen dup.merge other end |
#~ ⇒ Object Also known as: complement
:call-seq:
~ self -> sequence set
complement -> sequence set
Returns the complement of self, a SequenceSet which contains all numbers except for those in this set.
~Net::IMAP::SequenceSet.full #=> Net::IMAP::SequenceSet.empty
~Net::IMAP::SequenceSet.empty #=> Net::IMAP::SequenceSet.full
~Net::IMAP::SequenceSet["1:5,100:222"]
#=> Net::IMAP::SequenceSet["6:99,223:*"]
~Net::IMAP::SequenceSet["6:99,223:*"]
#=> Net::IMAP::SequenceSet["1:5,100:222"]
Related: #complement!
658 |
# File 'lib/net/imap/sequence_set.rb', line 658 def ~; remain_frozen dup.complement! end |