net-imap 0.3.4 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/BSDL +22 -0
  3. data/COPYING +56 -0
  4. data/Gemfile +14 -0
  5. data/LICENSE.txt +3 -22
  6. data/README.md +25 -8
  7. data/Rakefile +0 -7
  8. data/docs/styles.css +72 -23
  9. data/lib/net/imap/authenticators.rb +26 -57
  10. data/lib/net/imap/command_data.rb +74 -54
  11. data/lib/net/imap/config/attr_accessors.rb +75 -0
  12. data/lib/net/imap/config/attr_inheritance.rb +90 -0
  13. data/lib/net/imap/config/attr_type_coercion.rb +61 -0
  14. data/lib/net/imap/config.rb +470 -0
  15. data/lib/net/imap/data_encoding.rb +21 -9
  16. data/lib/net/imap/data_lite.rb +226 -0
  17. data/lib/net/imap/deprecated_client_options.rb +142 -0
  18. data/lib/net/imap/errors.rb +27 -1
  19. data/lib/net/imap/esearch_result.rb +180 -0
  20. data/lib/net/imap/fetch_data.rb +597 -0
  21. data/lib/net/imap/flags.rb +1 -1
  22. data/lib/net/imap/response_data.rb +250 -440
  23. data/lib/net/imap/response_parser/parser_utils.rb +245 -0
  24. data/lib/net/imap/response_parser.rb +1867 -1184
  25. data/lib/net/imap/sasl/anonymous_authenticator.rb +69 -0
  26. data/lib/net/imap/sasl/authentication_exchange.rb +139 -0
  27. data/lib/net/imap/sasl/authenticators.rb +122 -0
  28. data/lib/net/imap/sasl/client_adapter.rb +123 -0
  29. data/lib/net/imap/{authenticators/cram_md5.rb → sasl/cram_md5_authenticator.rb} +24 -14
  30. data/lib/net/imap/sasl/digest_md5_authenticator.rb +342 -0
  31. data/lib/net/imap/sasl/external_authenticator.rb +83 -0
  32. data/lib/net/imap/sasl/gs2_header.rb +80 -0
  33. data/lib/net/imap/{authenticators/login.rb → sasl/login_authenticator.rb} +28 -18
  34. data/lib/net/imap/sasl/oauthbearer_authenticator.rb +199 -0
  35. data/lib/net/imap/sasl/plain_authenticator.rb +101 -0
  36. data/lib/net/imap/sasl/protocol_adapters.rb +101 -0
  37. data/lib/net/imap/sasl/scram_algorithm.rb +58 -0
  38. data/lib/net/imap/sasl/scram_authenticator.rb +287 -0
  39. data/lib/net/imap/sasl/stringprep.rb +6 -66
  40. data/lib/net/imap/sasl/xoauth2_authenticator.rb +106 -0
  41. data/lib/net/imap/sasl.rb +148 -44
  42. data/lib/net/imap/sasl_adapter.rb +20 -0
  43. data/lib/net/imap/search_result.rb +146 -0
  44. data/lib/net/imap/sequence_set.rb +1565 -0
  45. data/lib/net/imap/stringprep/nameprep.rb +70 -0
  46. data/lib/net/imap/stringprep/saslprep.rb +69 -0
  47. data/lib/net/imap/stringprep/saslprep_tables.rb +96 -0
  48. data/lib/net/imap/stringprep/tables.rb +146 -0
  49. data/lib/net/imap/stringprep/trace.rb +85 -0
  50. data/lib/net/imap/stringprep.rb +159 -0
  51. data/lib/net/imap/uidplus_data.rb +244 -0
  52. data/lib/net/imap/vanished_data.rb +56 -0
  53. data/lib/net/imap.rb +2090 -823
  54. data/net-imap.gemspec +7 -8
  55. data/rakelib/benchmarks.rake +91 -0
  56. data/rakelib/rfcs.rake +2 -0
  57. data/rakelib/saslprep.rake +4 -4
  58. data/rakelib/string_prep_tables_generator.rb +84 -60
  59. data/sample/net-imap.rb +167 -0
  60. metadata +45 -49
  61. data/.github/dependabot.yml +0 -6
  62. data/.github/workflows/test.yml +0 -31
  63. data/.gitignore +0 -10
  64. data/benchmarks/stringprep.yml +0 -65
  65. data/benchmarks/table-regexps.yml +0 -39
  66. data/lib/net/imap/authenticators/digest_md5.rb +0 -115
  67. data/lib/net/imap/authenticators/plain.rb +0 -41
  68. data/lib/net/imap/authenticators/xoauth2.rb +0 -20
  69. data/lib/net/imap/sasl/saslprep.rb +0 -55
  70. data/lib/net/imap/sasl/saslprep_tables.rb +0 -98
  71. data/lib/net/imap/sasl/stringprep_tables.rb +0 -153
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP < Protocol
5
+
6
+ # Regexps and utility methods for implementing stringprep profiles. The
7
+ # \StringPrep algorithm is defined by
8
+ # {RFC-3454}[https://www.rfc-editor.org/rfc/rfc3454.html]. Each
9
+ # codepoint table defined in the RFC-3454 appendices is matched by a Regexp
10
+ # defined in this module.
11
+ module StringPrep
12
+ autoload :NamePrep, File.expand_path("stringprep/nameprep", __dir__)
13
+ autoload :SASLprep, File.expand_path("stringprep/saslprep", __dir__)
14
+ autoload :Tables, File.expand_path("stringprep/tables", __dir__)
15
+ autoload :Trace, File.expand_path("stringprep/trace", __dir__)
16
+
17
+ # ArgumentError raised when +string+ is invalid for the stringprep
18
+ # +profile+.
19
+ class StringPrepError < ArgumentError
20
+ attr_reader :string, :profile
21
+
22
+ def initialize(*args, string: nil, profile: nil)
23
+ @string = -string.to_str unless string.nil?
24
+ @profile = -profile.to_str unless profile.nil?
25
+ super(*args)
26
+ end
27
+ end
28
+
29
+ # StringPrepError raised when +string+ contains a codepoint prohibited by
30
+ # +table+.
31
+ class ProhibitedCodepoint < StringPrepError
32
+ attr_reader :table
33
+
34
+ def initialize(table, *args, **kwargs)
35
+ @table = table
36
+ details = (title = Tables::TITLES[table]) ?
37
+ "%s [%s]" % [title, table] : table
38
+ message = "String contains a prohibited codepoint: %s" % [details]
39
+ super(message, *args, **kwargs)
40
+ end
41
+ end
42
+
43
+ # StringPrepError raised when +string+ contains bidirectional characters
44
+ # which violate the StringPrep requirements.
45
+ class BidiStringError < StringPrepError
46
+ end
47
+
48
+ # Returns a Regexp matching the given +table+ name.
49
+ def self.[](table)
50
+ Tables::REGEXPS.fetch(table)
51
+ end
52
+
53
+ module_function
54
+
55
+ # >>>
56
+ # 1. Map -- For each character in the input, check if it has a mapping
57
+ # and, if so, replace it with its mapping. This is described in
58
+ # section 3.
59
+ #
60
+ # 2. Normalize -- Possibly normalize the result of step 1 using Unicode
61
+ # normalization. This is described in section 4.
62
+ #
63
+ # 3. Prohibit -- Check for any characters that are not allowed in the
64
+ # output. If any are found, return an error. This is described in
65
+ # section 5.
66
+ #
67
+ # 4. Check bidi -- Possibly check for right-to-left characters, and if
68
+ # any are found, make sure that the whole string satisfies the
69
+ # requirements for bidirectional strings. If the string does not
70
+ # satisfy the requirements for bidirectional strings, return an
71
+ # error. This is described in section 6.
72
+ #
73
+ # The above steps MUST be performed in the order given to comply with
74
+ # this specification.
75
+ #
76
+ def stringprep(string,
77
+ maps:,
78
+ normalization:,
79
+ prohibited:,
80
+ **opts)
81
+ string = string.encode("UTF-8") # also dups (and raises invalid encoding)
82
+ map_tables!(string, *maps) if maps
83
+ string.unicode_normalize!(normalization) if normalization
84
+ check_prohibited!(string, *prohibited, **opts) if prohibited
85
+ string
86
+ end
87
+
88
+ def map_tables!(string, *tables)
89
+ tables.each do |table|
90
+ regexp, replacements = Tables::MAPPINGS.fetch(table)
91
+ string.gsub!(regexp, replacements)
92
+ end
93
+ string
94
+ end
95
+
96
+ # Checks +string+ for any codepoint in +tables+. Raises a
97
+ # ProhibitedCodepoint describing the first matching table.
98
+ #
99
+ # Also checks bidirectional characters, when <tt>bidi: true</tt>, which may
100
+ # raise a BidiStringError.
101
+ #
102
+ # +profile+ is an optional string which will be added to any exception that
103
+ # is raised (it does not affect behavior).
104
+ def check_prohibited!(string,
105
+ *tables,
106
+ bidi: false,
107
+ unassigned: "A.1",
108
+ stored: false,
109
+ profile: nil)
110
+ tables = Tables::TITLES.keys.grep(/^C/) if tables.empty?
111
+ tables |= [unassigned] if stored
112
+ tables |= %w[C.8] if bidi
113
+ table = tables.find {|t|
114
+ case t
115
+ when String then Tables::REGEXPS.fetch(t).match?(string)
116
+ when Regexp then t.match?(string)
117
+ else raise ArgumentError, "only table names and regexps can be checked"
118
+ end
119
+ }
120
+ if table
121
+ raise ProhibitedCodepoint.new(
122
+ table, string: string, profile: profile
123
+ )
124
+ end
125
+ check_bidi!(string, profile: profile) if bidi
126
+ end
127
+
128
+ # Checks that +string+ obeys all of the "Bidirectional Characters"
129
+ # requirements in RFC-3454, §6:
130
+ #
131
+ # * The characters in \StringPrep\[\"C.8\"] MUST be prohibited
132
+ # * If a string contains any RandALCat character, the string MUST NOT
133
+ # contain any LCat character.
134
+ # * If a string contains any RandALCat character, a RandALCat
135
+ # character MUST be the first character of the string, and a
136
+ # RandALCat character MUST be the last character of the string.
137
+ #
138
+ # This is usually combined with #check_prohibited!, so table "C.8" is only
139
+ # checked when <tt>c_8: true</tt>.
140
+ #
141
+ # Raises either ProhibitedCodepoint or BidiStringError unless all
142
+ # requirements are met. +profile+ is an optional string which will be
143
+ # added to any exception that is raised (it does not affect behavior).
144
+ def check_bidi!(string, c_8: false, profile: nil)
145
+ check_prohibited!(string, "C.8", profile: profile) if c_8
146
+ if Tables::BIDI_FAILS_REQ2.match?(string)
147
+ raise BidiStringError.new(
148
+ Tables::BIDI_DESC_REQ2, string: string, profile: profile,
149
+ )
150
+ elsif Tables::BIDI_FAILS_REQ3.match?(string)
151
+ raise BidiStringError.new(
152
+ Tables::BIDI_DESC_REQ3, string: string, profile: profile,
153
+ )
154
+ end
155
+ end
156
+
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP < Protocol
5
+
6
+ # *NOTE:* <em>UIDPlusData is deprecated and will be removed in the +0.6.0+
7
+ # release.</em> To use AppendUIDData and CopyUIDData before +0.6.0+, set
8
+ # Config#parser_use_deprecated_uidplus_data to +false+.
9
+ #
10
+ # UIDPlusData represents the ResponseCode#data that accompanies the
11
+ # +APPENDUID+ and +COPYUID+ {response codes}[rdoc-ref:ResponseCode].
12
+ #
13
+ # A server that supports +UIDPLUS+ should send UIDPlusData in response to
14
+ # the append[rdoc-ref:Net::IMAP#append], copy[rdoc-ref:Net::IMAP#copy],
15
+ # move[rdoc-ref:Net::IMAP#move], {uid copy}[rdoc-ref:Net::IMAP#uid_copy],
16
+ # and {uid move}[rdoc-ref:Net::IMAP#uid_move] commands---unless the
17
+ # destination mailbox reports +UIDNOTSTICKY+.
18
+ #
19
+ # Note that append[rdoc-ref:Net::IMAP#append], copy[rdoc-ref:Net::IMAP#copy]
20
+ # and {uid_copy}[rdoc-ref:Net::IMAP#uid_copy] return UIDPlusData in their
21
+ # TaggedResponse. But move[rdoc-ref:Net::IMAP#copy] and
22
+ # {uid_move}[rdoc-ref:Net::IMAP#uid_move] _should_ send UIDPlusData in an
23
+ # UntaggedResponse response before sending their TaggedResponse. However
24
+ # some servers do send UIDPlusData in the TaggedResponse for +MOVE+
25
+ # commands---this complies with the older +UIDPLUS+ specification but is
26
+ # discouraged by the +MOVE+ extension and disallowed by +IMAP4rev2+.
27
+ #
28
+ # == Required capability
29
+ # Requires either +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315]]
30
+ # or +IMAP4rev2+ capability.
31
+ #
32
+ class UIDPlusData < Struct.new(:uidvalidity, :source_uids, :assigned_uids)
33
+ ##
34
+ # method: uidvalidity
35
+ # :call-seq: uidvalidity -> nonzero uint32
36
+ #
37
+ # The UIDVALIDITY of the destination mailbox.
38
+
39
+ ##
40
+ # method: source_uids
41
+ # :call-seq: source_uids -> nil or an array of nonzero uint32
42
+ #
43
+ # The UIDs of the copied or moved messages.
44
+ #
45
+ # Note:: Returns +nil+ for Net::IMAP#append.
46
+
47
+ ##
48
+ # method: assigned_uids
49
+ # :call-seq: assigned_uids -> an array of nonzero uint32
50
+ #
51
+ # The newly assigned UIDs of the copied, moved, or appended messages.
52
+ #
53
+ # Note:: This always returns an array, even when it contains only one UID.
54
+
55
+ ##
56
+ # :call-seq: uid_mapping -> nil or a hash
57
+ #
58
+ # Returns a hash mapping each source UID to the newly assigned destination
59
+ # UID.
60
+ #
61
+ # Note:: Returns +nil+ for Net::IMAP#append.
62
+ def uid_mapping
63
+ source_uids&.zip(assigned_uids)&.to_h
64
+ end
65
+ end
66
+
67
+ # >>>
68
+ # *NOTE:* <em>AppendUIDData will replace UIDPlusData for +APPENDUID+ in the
69
+ # +0.6.0+ release.</em> To use AppendUIDData before +0.6.0+, set
70
+ # Config#parser_use_deprecated_uidplus_data to +false+.
71
+ #
72
+ # AppendUIDData represents the ResponseCode#data that accompanies the
73
+ # +APPENDUID+ {response code}[rdoc-ref:ResponseCode].
74
+ #
75
+ # A server that supports +UIDPLUS+ (or +IMAP4rev2+) should send
76
+ # AppendUIDData inside every TaggedResponse returned by the
77
+ # append[rdoc-ref:Net::IMAP#append] command---unless the target mailbox
78
+ # reports +UIDNOTSTICKY+.
79
+ #
80
+ # == Required capability
81
+ # Requires either +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315]]
82
+ # or +IMAP4rev2+ capability.
83
+ class AppendUIDData < Data.define(:uidvalidity, :assigned_uids)
84
+ def initialize(uidvalidity:, assigned_uids:)
85
+ uidvalidity = Integer(uidvalidity)
86
+ assigned_uids = SequenceSet[assigned_uids]
87
+ NumValidator.ensure_nz_number(uidvalidity)
88
+ if assigned_uids.include_star?
89
+ raise DataFormatError, "uid-set cannot contain '*'"
90
+ end
91
+ super
92
+ end
93
+
94
+ ##
95
+ # attr_reader: uidvalidity
96
+ # :call-seq: uidvalidity -> nonzero uint32
97
+ #
98
+ # The UIDVALIDITY of the destination mailbox.
99
+
100
+ ##
101
+ # attr_reader: assigned_uids
102
+ #
103
+ # A SequenceSet with the newly assigned UIDs of the appended messages.
104
+
105
+ # Returns the number of messages that have been appended.
106
+ def size
107
+ assigned_uids.count_with_duplicates
108
+ end
109
+ end
110
+
111
+ # >>>
112
+ # *NOTE:* <em>CopyUIDData will replace UIDPlusData for +COPYUID+ in the
113
+ # +0.6.0+ release.</em> To use CopyUIDData before +0.6.0+, set
114
+ # Config#parser_use_deprecated_uidplus_data to +false+.
115
+ #
116
+ # CopyUIDData represents the ResponseCode#data that accompanies the
117
+ # +COPYUID+ {response code}[rdoc-ref:ResponseCode].
118
+ #
119
+ # A server that supports +UIDPLUS+ (or +IMAP4rev2+) should send CopyUIDData
120
+ # in response to
121
+ # copy[rdoc-ref:Net::IMAP#copy], {uid_copy}[rdoc-ref:Net::IMAP#uid_copy],
122
+ # move[rdoc-ref:Net::IMAP#copy], and {uid_move}[rdoc-ref:Net::IMAP#uid_move]
123
+ # commands---unless the destination mailbox reports +UIDNOTSTICKY+.
124
+ #
125
+ # Note that copy[rdoc-ref:Net::IMAP#copy] and
126
+ # {uid_copy}[rdoc-ref:Net::IMAP#uid_copy] return CopyUIDData in their
127
+ # TaggedResponse. But move[rdoc-ref:Net::IMAP#copy] and
128
+ # {uid_move}[rdoc-ref:Net::IMAP#uid_move] _should_ send CopyUIDData in an
129
+ # UntaggedResponse response before sending their TaggedResponse. However
130
+ # some servers do send CopyUIDData in the TaggedResponse for +MOVE+
131
+ # commands---this complies with the older +UIDPLUS+ specification but is
132
+ # discouraged by the +MOVE+ extension and disallowed by +IMAP4rev2+.
133
+ #
134
+ # == Required capability
135
+ # Requires either +UIDPLUS+ [RFC4315[https://www.rfc-editor.org/rfc/rfc4315]]
136
+ # or +IMAP4rev2+ capability.
137
+ class CopyUIDData < Data.define(:uidvalidity, :source_uids, :assigned_uids)
138
+ def initialize(uidvalidity:, source_uids:, assigned_uids:)
139
+ uidvalidity = Integer(uidvalidity)
140
+ source_uids = SequenceSet[source_uids]
141
+ assigned_uids = SequenceSet[assigned_uids]
142
+ NumValidator.ensure_nz_number(uidvalidity)
143
+ if source_uids.include_star? || assigned_uids.include_star?
144
+ raise DataFormatError, "uid-set cannot contain '*'"
145
+ elsif source_uids.count_with_duplicates != assigned_uids.count_with_duplicates
146
+ raise DataFormatError, "mismatched uid-set sizes for %s and %s" % [
147
+ source_uids, assigned_uids
148
+ ]
149
+ end
150
+ super
151
+ end
152
+
153
+ ##
154
+ # attr_reader: uidvalidity
155
+ #
156
+ # The +UIDVALIDITY+ of the destination mailbox (a nonzero unsigned 32 bit
157
+ # integer).
158
+
159
+ ##
160
+ # attr_reader: source_uids
161
+ #
162
+ # A SequenceSet with the original UIDs of the copied or moved messages.
163
+
164
+ ##
165
+ # attr_reader: assigned_uids
166
+ #
167
+ # A SequenceSet with the newly assigned UIDs of the copied or moved
168
+ # messages.
169
+
170
+ # Returns the number of messages that have been copied or moved.
171
+ # source_uids and the assigned_uids will both the same number of UIDs.
172
+ def size
173
+ assigned_uids.count_with_duplicates
174
+ end
175
+
176
+ # :call-seq:
177
+ # assigned_uid_for(source_uid) -> uid
178
+ # self[source_uid] -> uid
179
+ #
180
+ # Returns the UID in the destination mailbox for the message that was
181
+ # copied from +source_uid+ in the source mailbox.
182
+ #
183
+ # This is the reverse of #source_uid_for.
184
+ #
185
+ # Related: source_uid_for, each_uid_pair, uid_mapping
186
+ def assigned_uid_for(source_uid)
187
+ idx = source_uids.find_ordered_index(source_uid) and
188
+ assigned_uids.ordered_at(idx)
189
+ end
190
+ alias :[] :assigned_uid_for
191
+
192
+ # :call-seq:
193
+ # source_uid_for(assigned_uid) -> uid
194
+ #
195
+ # Returns the UID in the source mailbox for the message that was copied to
196
+ # +assigned_uid+ in the source mailbox.
197
+ #
198
+ # This is the reverse of #assigned_uid_for.
199
+ #
200
+ # Related: assigned_uid_for, each_uid_pair, uid_mapping
201
+ def source_uid_for(assigned_uid)
202
+ idx = assigned_uids.find_ordered_index(assigned_uid) and
203
+ source_uids.ordered_at(idx)
204
+ end
205
+
206
+ # Yields a pair of UIDs for each copied message. The first is the
207
+ # message's UID in the source mailbox and the second is the UID in the
208
+ # destination mailbox.
209
+ #
210
+ # Returns an enumerator when no block is given.
211
+ #
212
+ # Please note the warning on uid_mapping before calling methods like
213
+ # +to_h+ or +to_a+ on the returned enumerator.
214
+ #
215
+ # Related: uid_mapping, assigned_uid_for, source_uid_for
216
+ def each_uid_pair
217
+ return enum_for(__method__) unless block_given?
218
+ source_uids.each_ordered_number.lazy
219
+ .zip(assigned_uids.each_ordered_number.lazy) do
220
+ |source_uid, assigned_uid|
221
+ yield source_uid, assigned_uid
222
+ end
223
+ end
224
+ alias each_pair each_uid_pair
225
+ alias each each_uid_pair
226
+
227
+ # :call-seq: uid_mapping -> hash
228
+ #
229
+ # Returns a hash mapping each source UID to the newly assigned destination
230
+ # UID.
231
+ #
232
+ # <em>*Warning:*</em> The hash that is created may consume _much_ more
233
+ # memory than the data used to create it. When handling responses from an
234
+ # untrusted server, check #size before calling this method.
235
+ #
236
+ # Related: each_uid_pair, assigned_uid_for, source_uid_for
237
+ def uid_mapping
238
+ each_uid_pair.to_h
239
+ end
240
+
241
+ end
242
+
243
+ end
244
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Net
4
+ class IMAP < Protocol
5
+
6
+ # Net::IMAP::VanishedData represents the contents of a +VANISHED+ response,
7
+ # which is described by the
8
+ # {QRESYNC}[https://www.rfc-editor.org/rfc/rfc7162.html] extension.
9
+ # [{RFC7162 §3.2.10}[https://www.rfc-editor.org/rfc/rfc7162.html#section-3.2.10]].
10
+ #
11
+ # +VANISHED+ responses replace +EXPUNGE+ responses when either the
12
+ # {QRESYNC}[https://www.rfc-editor.org/rfc/rfc7162.html] or the
13
+ # {UIDONLY}[https://www.rfc-editor.org/rfc/rfc9586.html] extension has been
14
+ # enabled.
15
+ class VanishedData < Data.define(:uids, :earlier)
16
+
17
+ # Returns a new VanishedData object.
18
+ #
19
+ # * +uids+ will be converted by SequenceSet.[].
20
+ # * +earlier+ will be converted to +true+ or +false+
21
+ def initialize(uids:, earlier:)
22
+ uids = SequenceSet[uids]
23
+ earlier = !!earlier
24
+ super
25
+ end
26
+
27
+ ##
28
+ # :attr_reader: uids
29
+ #
30
+ # SequenceSet of UIDs that have been permanently removed from the mailbox.
31
+
32
+ ##
33
+ # :attr_reader: earlier
34
+ #
35
+ # +true+ when the response was caused by Net::IMAP#uid_fetch with
36
+ # <tt>vanished: true</tt> or Net::IMAP#select/Net::IMAP#examine with
37
+ # <tt>qresync: true</tt>.
38
+ #
39
+ # +false+ when the response is used to announce message removals within an
40
+ # already selected mailbox.
41
+
42
+ # rdoc doesn't handle attr aliases nicely. :(
43
+ alias earlier? earlier # :nodoc:
44
+ ##
45
+ # :attr_reader: earlier?
46
+ #
47
+ # Alias for #earlier.
48
+
49
+ # Returns an Array of all of the UIDs in #uids.
50
+ #
51
+ # See SequenceSet#numbers.
52
+ def to_a; uids.numbers end
53
+
54
+ end
55
+ end
56
+ end