Class: Net::IMAP::SequenceSet
Relationships & Source Files | |
Inherits: | Object |
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 #search, #fetch, and #store.
Creating sequence sets
.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"
.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"
Ordered and Normalized sets
Sometimes the order of the set’s members is significant, such as with the ESORT
, CONTEXT=SORT
, and UIDPLUS
extensions. So, when a sequence set is created by the parser or with a single string value, that #string representation is preserved.
Internally, SequenceSet
stores a normalized representation which sorts all entries, de-duplicates numbers, and coalesces adjacent or overlapping ranges. Most methods use this normalized representation to achieve O(lg n)
porformance. Use #entries or #each_entry to enumerate the set in its original order.
Most modification methods convert #string to its normalized form. 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:
-
Creating a SequenceSet
-
Comparing
-
Querying
-
Iterating
-
Set Operations
-
Assigning
-
Deleting
-
{Net::IMAP
String Formatting}
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?: 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 element (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 in sorted set:
-
#[] (aliased as #slice): Returns the number or consecutive subset at a given offset or range of offsets in the sorted set.
-
#at: Returns the number at a given offset in the sorted set.
-
#find_index: Returns the given number’s offset in the sorted set.
Accessing value by offset in ordered entries
-
#ordered_at: Returns the number at a given offset in the ordered entries.
-
#find_ordered_index: Returns the index of the given number’s first occurrence in entries.
Set cardinality:
-
#count (aliased as #size): Returns the count of numbers in the set. Duplicated numbers are not counted.
-
#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
*
.
Denormalized properties:
-
#has_duplicates?: Returns whether the ordered entries repeat any numbers.
-
#count_duplicates: Returns the count of repeated numbers in the ordered entries.
-
#count_with_duplicates: Returns the count of numbers in the ordered entries, including any repeated numbers.
Methods for Iterating
Normalized (sorted and coalesced):
-
#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_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.
Order preserving:
-
#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_ordered_number: Yields each number in the ordered entries and returns
self
.
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 set. -
#& (aliased as #intersection): Returns a new set containing all members common to
self
and the other set. -
#- (aliased as #difference): Returns a copy of
self
with all members in the other set removed. -
#^ (aliased as #xor): Returns a new set containing all members from
self
and the other set 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
.
Normalized (sorted and coalesced):
These methods always update #string to be fully sorted and coalesced.
-
#add (aliased as #<<): Adds a given element to the set; returns
self
. -
#add?: If the given element is not fully included the set, adds it and returns
self
; otherwise, returnsnil
. -
#merge: Adds all members of the given sets into this set; returns
self
. -
#complement!: Replaces the contents of the set with its own #complement.
Order preserving:
These methods may cause #string to not be sorted or coalesced.
-
#append: Adds the given entry 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.
Methods for Deleting
These methods remove elements from self
, and update #string to be fully sorted and coalesced.
-
#clear: Removes all elements in the set; returns
self
. -
#delete: Removes a given element from the set; returns
self
. -
#delete?: If the given element is included 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 all members of the given sets from this 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
-
EMPTY =
private
intentionally defined after the class implementation
new.freeze
-
FULL =
private
# File 'lib/net/imap/sequence_set.rb', line 1574 -
STARS =
private
valid inputs for “*”
[:*, ?*, -1].freeze
-
STAR_INT =
private
represents “*” internally, to simplify sorting (etc)
UINT32_MAX + 1
-
UINT32_MAX =
The largest possible non-zero unsigned 32-bit integer
2**32 - 1
Class Method Summary
-
.[](*inputs) ⇒ valid frozen sequence set
Returns a frozen
SequenceSet
, constructed frominputs
. -
.empty
Returns a frozen empty set singleton.
-
.full
Returns a frozen full set singleton:
"1:*"
. -
.new(input = nil) ⇒ SequenceSet
constructor
Create a new
SequenceSet
object frominput
, which may be anotherSequenceSet
, an::Net::IMAP
formattedsequence-set
string, a number, a range,:*
, or an enumerable of these. -
.try_convert(obj) ⇒ sequence set?
If
obj
is aSequenceSet
, returnsobj
.
Instance Attribute Summary
-
#empty? ⇒ Boolean
readonly
Returns true if the set contains no elements.
-
#full? ⇒ Boolean
readonly
Returns true if the set contains every possible element.
-
#has_duplicates? ⇒ Boolean
readonly
Returns whether or not the ordered #entries repeat any numbers.
-
#include_star? ⇒ Boolean
readonly
Returns
true
when the set contains*
. -
#string
rw
Returns the IMAP
sequence-set
string representation, ornil
when the set is empty. - #string=(str) rw
-
#valid? ⇒ Boolean
readonly
Returns false when the set is empty.
- #tuples readonly protected Internal use only
Instance Method Summary
-
#&(other) ⇒ sequence set
(also: #intersection)
Returns a new sequence set containing only the numbers common to this set and
other
. -
#+(other)
Alias for #|.
-
#-(other) ⇒ sequence set
(also: #difference)
Returns a new sequence set built by duplicating this set and removing every number that appears in
other
. -
#<<(element)
Alias for #add.
-
#==(other) ⇒ Boolean
Returns true when the other
SequenceSet
represents the same message identifiers. -
#===(other) ⇒ Boolean
Returns whether
other
is contained within the set. -
#[](index) ⇒ Integer, ...
(also: #slice)
Returns a number or a subset from the sorted set, without modifying the set.
-
#^(other) ⇒ sequence set
(also: #xor)
Returns a new sequence set containing numbers that are exclusive between this set and
other
. -
#add(element) ⇒ self
(also: #<<)
Adds a range or number to the set and returns
self
. -
#add?(element) ⇒ self?
Adds a range or number to the set and returns
self
. -
#append(entry)
Adds a range or number to the set and returns
self
. -
#at(index) ⇒ Integer?
Returns the number at the given
index
in the sorted set, without modifying the set. -
#clear
Removes all elements and returns self.
-
#complement
Alias for #~.
-
#complement! ⇒ self
Converts the
SequenceSet
to its own #complement. -
#count
(also: #size)
Returns the count of #numbers in the set.
-
#count_duplicates
Returns the count of repeated numbers in the ordered #entries, the difference between #count_with_duplicates and #count.
-
#count_with_duplicates
Returns the count of numbers in the ordered #entries, including any repeated numbers.
-
#cover?(other) ⇒ Boolean
Returns whether
other
is contained within the set. -
#deconstruct
Returns an array with #normalized_string when valid and an empty array otherwise.
-
#delete(element) ⇒ self
Deletes the given range or number from the set and returns
self
. -
#delete?(number) ⇒ Integer?
Removes a specified value from the set, and returns the removed value.
-
#delete_at(index) ⇒ Numeric, ...
Deletes a number the set, indicated by the given
index
. -
#difference(other)
Alias for #-.
-
#disjoint?(other) ⇒ Boolean
Returns
true
if the set and a given object have no common elements,false
otherwise. -
#each_element
Yields each number or range (or
:*
) in #elements to the block and returns self. -
#each_entry(&block)
Yields each number or range in #string to the block and returns
self
. -
#each_number(&block)
Yields each number in #numbers to the block and returns self.
-
#each_ordered_number(&block)
Yields each number in #entries to the block and returns self.
-
#each_range
Yields each range in #ranges to the block and returns self.
-
#elements
(also: #to_a)
Returns an array of ranges and integers and
:*
. -
#entries
Returns an array of ranges and integers and
:*
. -
#eql?(other) ⇒ Boolean
Hash equality requires the same encoded #string representation.
-
#find_index(number)
Returns the (sorted and deduplicated) index of
number
in the set, ornil
ifnumber
isn’t in the set. -
#find_ordered_index(number)
Returns the first index of
number
in the ordered #entries, ornil
ifnumber
isn’t in the set. -
#freeze
Freezes and returns the set.
-
#hash
See #eql?
-
#include?(element) ⇒ Boolean
(also: #member?)
Returns
true
when a given number or range is inself
, andfalse
otherwise. - #inspect
-
#intersect?(other) ⇒ Boolean
(also: #overlap?)
Returns
true
if the set and a given object have any common elements,false
otherwise. -
#intersection(other)
Alias for #&.
- #limit(max:)
-
#limit!(max:)
Removes all members over #max and returns self.
-
#max(star: :*) ⇒ Integer, ...
Returns the maximum value in
self
,star
when the set includes*
, ornil
when the set is empty. -
#member?(element)
Alias for #include?.
-
#merge(*sets)
Merges all of the elements that appear in any of the
sets
into the set, and returnsself
. -
#min(star: :*) ⇒ Integer, ...
Returns the minimum value in
self
,star
when the only value in the set is*
, ornil
when the set is empty. -
#minmax(star: :*) ⇒ nil, ...
Returns a 2-element array containing the minimum and maximum numbers in
self
, ornil
when the set is empty. -
#normalize
Returns a new
SequenceSet
with a normalized string representation. -
#normalize!
Resets #string to be sorted, deduplicated, and coalesced.
-
#normalized_string
Returns a normalized
sequence-set
string representation, sorted and deduplicated. -
#numbers
Returns a sorted array of all of the number values in the sequence set.
-
#ordered_at(index) ⇒ Integer?
Returns the number at the given
index
in the ordered #entries, without modifying the set. -
#overlap?(other)
Alias for #intersect?.
-
#ranges
Returns an array of ranges.
-
#replace(other)
Replace the contents of the set with the contents of
other
and returnsself
. -
#size
Alias for #count.
-
#slice(index, length = nil)
Alias for #[].
-
#slice!(index) ⇒ Integer, ...
Deletes a number or consecutive numbers from the set, indicated by the given
index
,start
andlength
, orrange
of offsets. -
#subtract(*sets)
Removes all of the elements that appear in any of the given
sets
from the set, and returnsself
. -
#to_a
Alias for #elements.
-
#to_s
Returns the IMAP
sequence-set
string representation, or an empty string when the set is empty. -
#to_sequence_set
Returns self.
-
#to_set
Returns a Set with all of the #numbers in the sequence set.
-
#union(other)
Alias for #|.
-
#valid_string
Returns the IMAP
sequence-set
string representation, or raises aDataFormatError
when the set is empty. -
#xor(other)
Alias for #^.
-
#|(other) ⇒ sequence set
(also: #+, #union)
Returns a new sequence set that has every number in the
other
object added. -
#~(self) ⇒ sequence set
(also: #complement)
Returns the complement of self, a
SequenceSet
which contains all numbers except for those in this set. - #each_entry_tuple(&block) private
- #each_number_in_tuple(min, max, &block) private
- #each_tuple_with_index(tuples) private
- #from_tuple_int(num) private
- #include_tuple?(min, max) ⇒ Boolean private
-
#initialize_clone(other)
private
frozen clones are shallow copied.
- #initialize_dup(other) private
- #input_to_tuple(entry) private
- #input_to_tuples(set) private
-
#input_try_convert(input)
private
unlike
SequenceSet#try_convert
, this returns an Integer, Range, String, Set, Array, or… - #intersect_tuple?(min, max) ⇒ Boolean private
- #lookup_number_by_tuple_index(tuples, index) private
- #modifying! private
- #nz_number(num) private
- #range_gte_to(num) private
- #range_to_tuple(range) private
- #remain_frozen(set) private
- #reverse_each_tuple_with_index(tuples) private
- #slice_length(start, length) private
- #slice_range(range) private
- #str_to_tuple(str) private
- #str_to_tuples(str) private
- #to_tuple_int(obj) private
-
#tuple_add(tuple)
private
–|=====| |=====new tuple=====| append ?????????-|=====new tuple=====|-|===lower===|– insert.
- #tuple_coalesce(lower, lower_idx, min, max) private
- #tuple_gte_with_index(num) private
-
#tuple_subtract(tuple)
private
|====tuple================| –|====| no more 1.
- #tuple_to_entry(min, max) private
- #tuple_to_str(tuple) private
- #tuple_trim_or_split(lower, idx, tmin, tmax) private
- #tuples_add(tuples) private
- #tuples_subtract(tuples) private
- #tuples_trim_or_delete(lower, lower_idx, tmin, tmax) private
-
#encode_with(coder)
Internal use only
For YAML serialization.
-
#init_with(coder)
Internal use only
For YAML deserialization.
-
#send_data(imap, tag)
Internal use only
Unstable API: for internal use only (Net::IMAP#send_data).
-
#validate
Internal use only
Unstable API: currently for internal use only (Net::IMAP#validate_data).
Constructor Details
.new(input = nil) ⇒ SequenceSet
Create a new SequenceSet
object from input
, which may be another SequenceSet
, an ::Net::IMAP
formatted sequence-set
string, a number, a range, :*
, or an enumerable of these.
Use .[] to create a frozen (non-empty) SequenceSet
.
# File 'lib/net/imap/sequence_set.rb', line 375
def initialize(input = nil) input ? replace(input) : clear end
Class Method Details
.[](*inputs) ⇒ valid
frozen
sequence
set
Returns a frozen SequenceSet
, constructed from inputs
.
When only a single valid frozen SequenceSet
is given, that same set is returned.
An empty SequenceSet
is invalid and will raise a DataFormatError
.
Use .new to create a mutable or empty SequenceSet
.
.empty
Returns a frozen empty set singleton. Note that valid IMAP sequence sets cannot be empty, so this set is invalid.
# File 'lib/net/imap/sequence_set.rb', line 363
def empty; EMPTY end
.full
Returns a frozen full set singleton: "1:*"
# File 'lib/net/imap/sequence_set.rb', line 366
def full; FULL end
.try_convert(obj) ⇒ sequence
set
?
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.
# File 'lib/net/imap/sequence_set.rb', line 353
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 Attribute Details
#empty? ⇒ Boolean
(readonly)
Returns true if the set contains no elements
# File 'lib/net/imap/sequence_set.rb', line 596
def empty?; @tuples.empty? end
#full? ⇒ Boolean
(readonly)
Returns true if the set contains every possible element.
# File 'lib/net/imap/sequence_set.rb', line 599
def full?; @tuples == [[1, STAR_INT]] end
#has_duplicates? ⇒ Boolean
(readonly)
Returns whether or not the ordered #entries repeat any numbers.
Always returns false
when #string is normalized.
Related: #entries, #count_with_duplicates, #count_duplicates?
# File 'lib/net/imap/sequence_set.rb', line 1088
def has_duplicates? return false unless @string count_with_duplicates != count end
#include_star? ⇒ Boolean
(readonly)
Returns true
when the set contains *
.
# File 'lib/net/imap/sequence_set.rb', line 545
def include_star?; @tuples.last&.last == STAR_INT end
#string (rw)
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
# File 'lib/net/imap/sequence_set.rb', line 417
def string; @string ||= normalized_string if valid? end
#string=(str) (rw)
# File 'lib/net/imap/sequence_set.rb', line 430
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
#tuples (readonly, protected)
# File 'lib/net/imap/sequence_set.rb', line 1386
attr_reader :tuples # :nodoc:
#valid? ⇒ Boolean
(readonly)
Returns false when the set is empty.
# File 'lib/net/imap/sequence_set.rb', line 593
def valid?; !empty? end
Instance Method Details
#&(other) ⇒ sequence
set
#intersection(other) ⇒ sequence
set
Also known as: #intersection
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)
.
# File 'lib/net/imap/sequence_set.rb', line 654
def &(other) remain_frozen dup.subtract SequenceSet.new(other).complement! end
#+(other)
Alias for #|.
# File 'lib/net/imap/sequence_set.rb', line 618
alias :+ :|
#-(other) ⇒ sequence
set
#difference(other) ⇒ sequence
set
Also known as: #difference
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
# File 'lib/net/imap/sequence_set.rb', line 636
def -(other) remain_frozen dup.subtract other end
#<<(element)
Alias for #add.
# File 'lib/net/imap/sequence_set.rb', line 709
alias << add
#==(other) ⇒ Boolean
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
#===(other) ⇒ Boolean
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?
# File 'lib/net/imap/sequence_set.rb', line 502
def ===(other) cover?(other) rescue nil end
#[](index) ⇒ Integer
, ...
#slice(index) ⇒ Integer
, ...
#[](start, length) ⇒ sequence
set
?
#slice(start, length) ⇒ sequence
set
?
#[](range) ⇒ sequence
set
?
#slice(range) ⇒ sequence
set
?
Also known as: #slice
Integer
, ...
#slice(index) ⇒ Integer
, ...
#[](start, length) ⇒ sequence
set
?
#slice(start, length) ⇒ sequence
set
?
#[](range) ⇒ sequence
set
?
#slice(range) ⇒ sequence
set
?
Returns a number or a subset from the sorted set, without modifying the set.
When an Integer argument index
is given, the number at offset index
in the sorted set 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 the sorted set:
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 sorted and de-duplicated set, not on the ordered #entries in #string.
set = Net::IMAP::SequenceSet["12,20:23,11:16,21"]
set[0] #=> 11
set[-1] #=> 23
Related: #at
# File 'lib/net/imap/sequence_set.rb', line 1222
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) ⇒ sequence
set
#xor(other) ⇒ sequence
set
Also known as: #xor
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))
.
# File 'lib/net/imap/sequence_set.rb', line 675
def ^(other) remain_frozen (self | other).subtract(self & other) end
#add(element) ⇒ self
#<<(other) ⇒ self
Also known as: #<<
self
#<<(other) ⇒ self
# File 'lib/net/imap/sequence_set.rb', line 705
def add(element) tuple_add input_to_tuple element normalize! end
#add?(element) ⇒ self
?
#append(entry)
# File 'lib/net/imap/sequence_set.rb', line 715
def append(entry) tuple = input_to_tuple entry entry = tuple_to_str tuple string unless empty? # write @string before tuple_add tuple_add tuple @string = -(@string ? "#{@string},#{entry}" : entry) self end
#at(index) ⇒ Integer
?
Returns the number at the given index
in the sorted set, without modifying the set.
index
is interpreted the same as in #[], except that #at
only allows a single integer argument.
Related: #[], #slice, #ordered_at
# File 'lib/net/imap/sequence_set.rb', line 1152
def at(index) lookup_number_by_tuple_index(tuples, index) end
#clear
Removes all elements and returns self.
# File 'lib/net/imap/sequence_set.rb', line 378
def clear; @tuples, @string = [], nil; self end
#complement
Alias for #~.
# File 'lib/net/imap/sequence_set.rb', line 694
alias complement :~
#complement! ⇒ self
Converts the SequenceSet
to its own #complement. It will contain all possible values except for those currently in the set.
Related: #complement
# File 'lib/net/imap/sequence_set.rb', line 1302
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 Also known as: #size
Returns the count of #numbers in the set.
*
will be counted as 2**32 - 1
(the maximum 32-bit unsigned integer value).
Related: #count_with_duplicates
# File 'lib/net/imap/sequence_set.rb', line 1047
def count @tuples.sum(@tuples.count) { _2 - _1 } + (include_star? && include?(UINT32_MAX) ? -1 : 0) end
#count_duplicates
Returns the count of repeated numbers in the ordered #entries, the difference between #count_with_duplicates and #count.
When #string is normalized, this is zero.
Related: #entries, #count_with_duplicates, #has_duplicates?
# File 'lib/net/imap/sequence_set.rb', line 1076
def count_duplicates return 0 unless @string count_with_duplicates - count end
#count_with_duplicates
Returns the count of numbers in the ordered #entries, including any repeated numbers.
*
will be counted as 2**32 - 1
(the maximum 32-bit unsigned integer value).
When #string is normalized, this behaves the same as #count.
Related: #entries, #count_duplicates, #has_duplicates?
#cover?(other) ⇒ Boolean
Returns whether other
is contained within the set. other
may be any object that would be accepted by .new.
Related: #===, #include?, #include_star?
# File 'lib/net/imap/sequence_set.rb', line 514
def cover?(other) input_to_tuples(other).none? { !include_tuple?(_1) } end
#deconstruct
Returns an array with #normalized_string when valid and an empty array otherwise.
# File 'lib/net/imap/sequence_set.rb', line 421
def deconstruct; valid? ? [normalized_string] : [] end
#delete(element) ⇒ 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
# File 'lib/net/imap/sequence_set.rb', line 745
def delete(element) tuple_subtract input_to_tuple element normalize! end
#delete?(number) ⇒ Integer
?
#delete?(star) ⇒ :
*?
#delete?(range) ⇒ sequence
set
?
Integer
?
#delete?(star) ⇒ :
*?
#delete?(range) ⇒ sequence
set
?
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?
# File 'lib/net/imap/sequence_set.rb', line 782
def delete?(element) tuple = input_to_tuple element 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) ⇒ Numeric
, ...
# File 'lib/net/imap/sequence_set.rb', line 805
def delete_at(index) slice! Integer(index.to_int) end
#difference(other)
Alias for #-.
# File 'lib/net/imap/sequence_set.rb', line 637
alias difference :-
#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?
# File 'lib/net/imap/sequence_set.rb', line 566
def disjoint?(other) empty? || input_to_tuples(other).none? { intersect_tuple? _1 } end
#each_element
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
# File 'lib/net/imap/sequence_set.rb', line 953
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)
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
# File 'lib/net/imap/sequence_set.rb', line 941
def each_entry(&block) # :yields: integer or range or :* return to_enum(__method__) unless block_given? each_entry_tuple do yield tuple_to_entry _1 end end
#each_entry_tuple(&block) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 961
def each_entry_tuple(&block) return to_enum(__method__) unless block_given? if @string @string.split(",") do block.call str_to_tuple _1 end else @tuples.each(&block) end self end
#each_number(&block)
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, #each_ordered_number
# File 'lib/net/imap/sequence_set.rb', line 1003
def each_number(&block) # :yields: integer return to_enum(__method__) unless block_given? raise RangeError, '%s contains "*"' % [self.class] if include_star? @tuples.each do each_number_in_tuple _1, _2, &block end self end
#each_number_in_tuple(min, max, &block) (private)
[ GitHub ]#each_ordered_number(&block)
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
# File 'lib/net/imap/sequence_set.rb', line 1017
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
#each_range
#each_tuple_with_index(tuples) (private)
[ GitHub ]#elements 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.
The returned elements will be sorted and coalesced, even when the input #string is not. *
will sort last. See #normalize.
Net::IMAP::SequenceSet["2,5:9,6,*,12:11"].elements
#=> [2, 5..9, 11..12, :*]
Related: #each_element, #ranges, #numbers
# File 'lib/net/imap/sequence_set.rb', line 882
def elements; each_element.to_a end
#encode_with(coder)
For YAML serialization
# File 'lib/net/imap/sequence_set.rb', line 1373
def encode_with(coder) # :nodoc: # we can perfectly reconstruct from the string coder['string'] = to_s end
#entries
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 Net::IMAP#sort.
Related: #each_entry, #elements
# File 'lib/net/imap/sequence_set.rb', line 864
def entries; each_entry.to_a end
#eql?(other) ⇒ Boolean
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
#find_index(number)
Returns the (sorted and deduplicated) index of number
in the set, or nil
if number
isn’t in the set.
Related: #[], #at, #find_ordered_index
# File 'lib/net/imap/sequence_set.rb', line 1097
def find_index(number) number = to_tuple_int number each_tuple_with_index(@tuples) do |min, max, idx_min| number < min and return nil number <= max and return from_tuple_int(idx_min + (number - min)) end nil end
#find_ordered_index(number)
Returns the first index of number
in the ordered #entries, or nil
if number
isn’t in the set.
Related: #find_index
# File 'lib/net/imap/sequence_set.rb', line 1110
def find_ordered_index(number) number = to_tuple_int number each_tuple_with_index(each_entry_tuple) do |min, max, idx_min| if min <= number && number <= max return from_tuple_int(idx_min + (number - min)) end end nil end
#freeze
Freezes and returns the set. A frozen SequenceSet
is Ractor-safe.
# File 'lib/net/imap/sequence_set.rb', line 449
def freeze return self if frozen? string @tuples.each(&:freeze).freeze super end
#from_tuple_int(num) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1450
def from_tuple_int(num) num == STAR_INT ? :* : num end
#hash
See #eql?
# File 'lib/net/imap/sequence_set.rb', line 494
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?, #===
# File 'lib/net/imap/sequence_set.rb', line 540
def include?(element) include_tuple? input_to_tuple element end
#include_tuple?(min, max) ⇒ Boolean
(private)
#init_with(coder)
For YAML deserialization
# File 'lib/net/imap/sequence_set.rb', line 1379
def init_with(coder) # :nodoc: @tuples = [] self.string = coder['string'] end
#initialize_clone(other) (private)
frozen clones are shallow copied
# File 'lib/net/imap/sequence_set.rb', line 1393
def initialize_clone(other) other.frozen? ? super : initialize_dup(other) end
#initialize_dup(other) (private)
[ GitHub ]#input_to_tuple(entry) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1403
def input_to_tuple(entry) entry = input_try_convert entry case entry when *STARS, Integer then [int = to_tuple_int(entry), int] when Range then range_to_tuple(entry) when String then str_to_tuple(entry) else raise DataFormatError, "expected number or range, got %p" % [entry] end end
#input_to_tuples(set) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1414
def input_to_tuples(set) set = input_try_convert set case set when *STARS, Integer, Range then [input_to_tuple(set)] when String then str_to_tuples set when SequenceSet then set.tuples when Set then set.map { [to_tuple_int(_1)] * 2 } when Array then set.flat_map { input_to_tuples _1 } when nil then [] else raise DataFormatError, "expected nz-number, range, string, or enumerable; " \ "got %p" % [set] end end
#input_try_convert(input) (private)
unlike SequenceSet#try_convert
, this returns an Integer, Range, String, Set, Array, or… any type of object.
# File 'lib/net/imap/sequence_set.rb', line 1432
def input_try_convert(input) SequenceSet.try_convert(input) || Integer.try_convert(input) || String.try_convert(input) || input end
#inspect
[ GitHub ]
#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?
# File 'lib/net/imap/sequence_set.rb', line 554
def intersect?(other) valid? && input_to_tuples(other).any? { intersect_tuple? _1 } end
#intersect_tuple?(min, max) ⇒ Boolean
(private)
#intersection(other)
Alias for #&.
# File 'lib/net/imap/sequence_set.rb', line 657
alias intersection :&
#limit(max:)
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"]
#limit!(max:)
# File 'lib/net/imap/sequence_set.rb', line 1288
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
#lookup_number_by_tuple_index(tuples, index) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1169
private def lookup_number_by_tuple_index(tuples, index) index = Integer(index.to_int) if index.negative? reverse_each_tuple_with_index(tuples) 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(tuples) do |min, _, idx_min, idx_max| index <= idx_max and return from_tuple_int(min + (index - idx_min)) end end nil end
#max(star: :*) ⇒ Integer
, ...
Returns the maximum value in self
, star
when the set includes *
, or nil
when the set is empty.
# File 'lib/net/imap/sequence_set.rb', line 574
def max(star: :*) (val = @tuples.last&.last) && val == STAR_INT ? star : val end
#member?(element)
Alias for #include?.
# File 'lib/net/imap/sequence_set.rb', line 542
alias member? include?
#merge(*sets)
Merges all of the elements that appear in any of the sets
into the set, and returns self
.
The sets
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 sets have been merged.
# File 'lib/net/imap/sequence_set.rb', line 837
def merge(*sets) tuples_add input_to_tuples sets normalize! end
#min(star: :*) ⇒ Integer
, ...
Returns the minimum value in self
, star
when the only value in the set is *
, or nil
when the set is empty.
# File 'lib/net/imap/sequence_set.rb', line 582
def min(star: :*) (val = @tuples.first&.first) && val == STAR_INT ? star : val end
#minmax(star: :*) ⇒ nil
, ...
Returns a 2-element array containing the minimum and maximum numbers in self
, or nil
when the set is empty.
#modifying! (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1466
def if frozen? raise FrozenError, "can't modify frozen #{self.class}: %p" % [self] end end
#normalize
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
# File 'lib/net/imap/sequence_set.rb', line 1321
def normalize str = normalized_string return self if frozen? && str == string remain_frozen dup.instance_exec { @string = str&.-@; self } end
#normalize!
Resets #string to be sorted, deduplicated, and coalesced. Returns self
.
Related: #normalize, #normalized_string
# File 'lib/net/imap/sequence_set.rb', line 1331
def normalize! @string = nil self end
#normalized_string
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
# File 'lib/net/imap/sequence_set.rb', line 1344
def normalized_string @tuples.empty? ? nil : -@tuples.map { tuple_to_str _1 }.join(",") end
#numbers
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.
# File 'lib/net/imap/sequence_set.rb', line 931
def numbers; each_number.to_a end
#nz_number(num) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1563
def nz_number(num) String === num && !/\A[1-9]\d*\z/.match?(num) and raise DataFormatError, "%p is not a valid nz-number" % [num] NumValidator.ensure_nz_number Integer num rescue TypeError # To catch errors from Integer() raise DataFormatError, $!. end
#ordered_at(index) ⇒ Integer
?
# File 'lib/net/imap/sequence_set.rb', line 1165
def ordered_at(index) lookup_number_by_tuple_index(each_entry_tuple, index) end
#overlap?(other)
Alias for #intersect?.
# File 'lib/net/imap/sequence_set.rb', line 557
alias overlap? intersect?
#range_gte_to(num) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1558
def range_gte_to(num) first, last = tuples.bsearch { _2 >= num } first..last if first end
#range_to_tuple(range) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1439
def range_to_tuple(range) first = to_tuple_int(range.begin || 1) last = to_tuple_int(range.end || :*) last -= 1 if range.exclude_end? && range.end && last != STAR_INT unless first <= last raise DataFormatError, "invalid range for sequence-set: %p" % [range] end [first, last] end
#ranges
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 sorted 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
# File 'lib/net/imap/sequence_set.rb', line 903
def ranges; each_range.to_a end
#remain_frozen(set) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1390
def remain_frozen(set) frozen? ? set.freeze : set end
#replace(other)
Replace the contents of the set with the contents of other
and returns self
.
other
may be another SequenceSet
, or it may be an ::Net::IMAP
sequence-set
string, a number, a range, *
, or an enumerable of these.
# File 'lib/net/imap/sequence_set.rb', line 385
def replace(other) case other when SequenceSet then initialize_dup(other) when String then self.string = other else clear; merge other end self end
#reverse_each_tuple_with_index(tuples) (private)
[ GitHub ]#send_data(imap, tag)
Unstable API: for internal use only (Net::IMAP#send_data)
# File 'lib/net/imap/sequence_set.rb', line 1368
def send_data(imap, tag) # :nodoc: imap.__send__(:put_string, valid_string) end
#size
Alias for #count.
# File 'lib/net/imap/sequence_set.rb', line 1052
alias size count
#slice(index, length = nil)
Alias for #[].
# File 'lib/net/imap/sequence_set.rb', line 1229
alias slice :[]
#slice!(index) ⇒ Integer
, ...
#slice!(start, length) ⇒ sequence
set
?
#slice!(range) ⇒ sequence
set
?
Integer
, ...
#slice!(start, length) ⇒ sequence
set
?
#slice!(range) ⇒ sequence
set
?
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
#slice_length(start, length) (private)
# File 'lib/net/imap/sequence_set.rb', line 1233
def slice_length(start, length) start = Integer(start.to_int) length = Integer(length.to_int) raise ArgumentError, "length must be positive" unless length.positive? last = start + length - 1 unless start.negative? && start.abs <= length slice_range(start..last) end
#slice_range(range) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1241
def slice_range(range) first = range.begin || 0 last = range.end || -1 last -= 1 if range.exclude_end? && range.end && last != STAR_INT if (first * last).positive? && last < first SequenceSet.empty elsif (min = at(first)) max = at(last) if max == :* then self & (min..) elsif min <= max then self & (min..max) else SequenceSet.empty end end end
#str_to_tuple(str) (private)
# File 'lib/net/imap/sequence_set.rb', line 1454
def str_to_tuple(str) raise DataFormatError, "invalid sequence set string" if str.empty? str.split(":", 2).map! { to_tuple_int _1 }.minmax end
#str_to_tuples(str) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1453
def str_to_tuples(str) str.split(",", -1).map! { str_to_tuple _1 } end
#subtract(*sets)
Removes all of the elements that appear in any of the given sets
from the set, and returns self
.
The sets
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
# File 'lib/net/imap/sequence_set.rb', line 850
def subtract(*sets) tuples_subtract input_to_tuples sets normalize! end
#to_a
Alias for #elements.
# File 'lib/net/imap/sequence_set.rb', line 883
alias to_a elements
#to_s
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
# File 'lib/net/imap/sequence_set.rb', line 446
def to_s; string || "" end
#to_sequence_set
Returns self
# File 'lib/net/imap/sequence_set.rb', line 1359
alias to_sequence_set itself
#to_set
#to_tuple_int(obj) (private)
[ GitHub ]#tuple_add(tuple) (private)
–|=====| |=====new tuple=====| append ?????????-|=====new tuple=====|-|===lower===|– insert
|=====new tuple=====|
———??=======lower=======??————— noop
———??===lower==|–|==| join remaining ———??===lower==|–|==|—-|===upper===|– join until upper ———??===lower==|–|==|–|=====upper===|– join to upper
# File 'lib/net/imap/sequence_set.rb', line 1485
def tuple_add(tuple) min, max = tuple lower, lower_idx = tuple_gte_with_index(min - 1) if lower.nil? then tuples << [min, max] elsif (max + 1) < lower.first then tuples.insert(lower_idx, [min, max]) else tuple_coalesce(lower, lower_idx, min, max) end end
#tuple_coalesce(lower, lower_idx, min, max) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1495
def tuple_coalesce(lower, lower_idx, min, max) return if lower.first <= min && max <= lower.last lower[0] = [min, lower.first].min lower[1] = [max, lower.last].max lower_idx += 1 return if lower_idx == tuples.count tmax_adj = lower.last + 1 upper, upper_idx = tuple_gte_with_index(tmax_adj) if upper tmax_adj < upper.first ? (upper_idx -= 1) : (lower[1] = upper.last) end tuples.slice!(lower_idx..upper_idx) end
#tuple_gte_with_index(num) (private)
[ GitHub ]#tuple_subtract(tuple) (private)
|====tuple================| –|====| no more 1. noop –|====|—————————|====lower====|– 2. noop ——-|======lower================|—————- 3. split ——–|=====lower================|—————- 4. trim beginning
——-|======lower====????????????—————– trim lower ——–|=====lower====????????????—————– delete lower
——-??=====lower===============|—————– 5. trim/delete one ——-??=====lower====|–|====| no more 6. delete rest ——-??=====lower====|–|====|—|====upper====|– 7. delete until ——-??=====lower====|–|====|–|=====upper====|– 8. delete and trim
# File 'lib/net/imap/sequence_set.rb', line 1522
def tuple_subtract(tuple) min, max = tuple lower, idx = tuple_gte_with_index(min) if lower.nil? then nil # case 1. elsif max < lower.first then nil # case 2. elsif max < lower.last then tuple_trim_or_split lower, idx, min, max else tuples_trim_or_delete lower, idx, min, max end end
#tuple_to_entry(min, max) (private)
[ GitHub ]#tuple_to_str(tuple) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1452
def tuple_to_str(tuple) tuple.uniq.map{ from_tuple_int _1 }.join(":") end
#tuple_trim_or_split(lower, idx, tmin, tmax) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1533
def tuple_trim_or_split(lower, idx, tmin, tmax) if lower.first < tmin # split tuples.insert(idx, [lower.first, tmin - 1]) end lower[0] = tmax + 1 end
#tuples_add(tuples) (private)
[ GitHub ]#tuples_subtract(tuples) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1473
def tuples_subtract(tuples) tuples.each do tuple_subtract _1 end; self end
#tuples_trim_or_delete(lower, lower_idx, tmin, tmax) (private)
[ GitHub ]# File 'lib/net/imap/sequence_set.rb', line 1540
def tuples_trim_or_delete(lower, lower_idx, tmin, tmax) if lower.first < tmin # trim lower lower[1] = tmin - 1 lower_idx += 1 end if tmax == lower.last # case 5 upper_idx = lower_idx elsif (upper, upper_idx = tuple_gte_with_index(tmax + 1)) upper_idx -= 1 # cases 7 and 8 upper[0] = tmax + 1 if upper.first <= tmax # case 8 (else case 7) end tuples.slice!(lower_idx..upper_idx) end
#union(other)
Alias for #|.
# File 'lib/net/imap/sequence_set.rb', line 619
alias union :|
#valid_string
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
# File 'lib/net/imap/sequence_set.rb', line 401
def valid_string raise DataFormatError, "empty sequence-set" if empty? string end
#validate
Unstable API: currently for internal use only (Net::IMAP#validate_data)
# File 'lib/net/imap/sequence_set.rb', line 1362
def validate # :nodoc: empty? and raise DataFormatError, "empty sequence-set is invalid" self end
#xor(other)
Alias for #^.
# File 'lib/net/imap/sequence_set.rb', line 676
alias xor :^
#+(other) ⇒ sequence
set
#|(other) ⇒ sequence
set
#union(other) ⇒ sequence
set
Also known as: #+, #union
sequence
set
#|(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"]
# File 'lib/net/imap/sequence_set.rb', line 617
def |(other) remain_frozen dup.merge other end
#~(self) ⇒ sequence
set
#complement ⇒ sequence
set
Also known as: #complement
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!
# File 'lib/net/imap/sequence_set.rb', line 693
def ~; remain_frozen dup.complement! end