ruby-ulid 0.0.18 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,70 @@
1
+ # coding: us-ascii
2
+ # frozen_string_literal: true
3
+ # Copyright (C) 2021 Kenichi Kamiya
4
+
5
+ class ULID
6
+ # Currently supporting only for `subset` for actual use-case`
7
+ # Original decoding spec allows other characters.
8
+ # But I think ULID should allow `subset` of Crockford's Base32.
9
+ # See below
10
+ # * https://github.com/ulid/spec/pull/57
11
+ # * https://github.com/kachick/ruby-ulid/issues/57
12
+ # * https://github.com/kachick/ruby-ulid/issues/78
13
+ module CrockfordBase32
14
+ class SetupError < ScriptError; end
15
+
16
+ n32_chars = [*'0'..'9', *'A'..'V'].map(&:freeze).freeze
17
+ raise SetupError, 'obvious bug exists in the mapping algorithm' unless n32_chars.size == 32
18
+
19
+ n32_char_by_number = {}
20
+ n32_chars.each_with_index do |char, index|
21
+ n32_char_by_number[index] = char
22
+ end
23
+ n32_char_by_number.freeze
24
+
25
+ crockford_base32_mappings = {
26
+ 'J' => 18,
27
+ 'K' => 19,
28
+ 'M' => 20,
29
+ 'N' => 21,
30
+ 'P' => 22,
31
+ 'Q' => 23,
32
+ 'R' => 24,
33
+ 'S' => 25,
34
+ 'T' => 26,
35
+ 'V' => 27,
36
+ 'W' => 28,
37
+ 'X' => 29,
38
+ 'Y' => 30,
39
+ 'Z' => 31
40
+ }.freeze
41
+
42
+ N32_CHAR_BY_CROCKFORD_BASE32_CHAR = CROCKFORD_BASE32_ENCODING_STRING.chars.map(&:freeze).each_with_object({}) do |encoding_char, map|
43
+ if n = crockford_base32_mappings[encoding_char]
44
+ char_32 = n32_char_by_number.fetch(n)
45
+ map[encoding_char] = char_32
46
+ end
47
+ end.freeze
48
+ raise SetupError, 'obvious bug exists in the mapping algorithm' unless N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys == crockford_base32_mappings.keys
49
+ CROCKFORD_BASE32_CHAR_PATTERN = /[#{N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys.join}]/.freeze
50
+
51
+ CROCKFORD_BASE32_CHAR_BY_N32_CHAR = N32_CHAR_BY_CROCKFORD_BASE32_CHAR.invert.freeze
52
+ N32_CHAR_PATTERN = /[#{CROCKFORD_BASE32_CHAR_BY_N32_CHAR.keys.join}]/.freeze
53
+
54
+ # @api private
55
+ # @param [String] string
56
+ # @return [Integer]
57
+ def self.decode(string)
58
+ n32encoded = string.upcase.gsub(CROCKFORD_BASE32_CHAR_PATTERN, N32_CHAR_BY_CROCKFORD_BASE32_CHAR)
59
+ n32encoded.to_i(32)
60
+ end
61
+
62
+ # @api private
63
+ # @param [Integer] integer
64
+ # @return [String]
65
+ def self.encode(integer)
66
+ n32encoded = integer.to_s(32)
67
+ n32encoded.upcase.gsub(N32_CHAR_PATTERN, CROCKFORD_BASE32_CHAR_BY_N32_CHAR).rjust(ENCODED_LENGTH, '0')
68
+ end
69
+ end
70
+ end
@@ -4,39 +4,60 @@
4
4
 
5
5
  class ULID
6
6
  class MonotonicGenerator
7
- # @api private
8
- attr_accessor :latest_milliseconds, :latest_entropy
7
+ # @return [ULID, nil]
8
+ attr_reader :prev
9
+
10
+ undef_method :instance_variable_set
9
11
 
10
12
  def initialize
11
- reset
13
+ @mutex = Thread::Mutex.new
14
+ @prev = nil
15
+ end
16
+
17
+ # @return [String]
18
+ def inspect
19
+ "ULID::MonotonicGenerator(prev: #{@prev.inspect})"
12
20
  end
21
+ alias_method :to_s, :inspect
13
22
 
14
23
  # @param [Time, Integer] moment
15
24
  # @return [ULID]
16
25
  # @raise [OverflowError] if the entropy part is larger than the ULID limit in same milliseconds
17
- # @raise [ArgumentError] if the given moment(milliseconds) is negative number
26
+ # @raise [UnexpectedError] if the generated ULID is an invalid value in monotonicity spec.
27
+ # Basically will not happen. Just means this feature prefers error rather than invalid value.
18
28
  def generate(moment: ULID.current_milliseconds)
19
- milliseconds = ULID.milliseconds_from_moment(moment)
20
- raise ArgumentError, "milliseconds should not be negative: given: #{milliseconds}" if milliseconds.negative?
21
-
22
- if @latest_milliseconds < milliseconds
23
- @latest_milliseconds = milliseconds
24
- @latest_entropy = ULID.reasonable_entropy
25
- else
26
- @latest_entropy += 1
27
- end
29
+ @mutex.synchronize do
30
+ unless @prev
31
+ @prev = ULID.generate(moment: moment)
32
+ return @prev
33
+ end
28
34
 
29
- ULID.from_monotonic_generator(self)
30
- end
35
+ milliseconds = ULID.milliseconds_from_moment(moment)
31
36
 
32
- # @api private
33
- # @return [void]
34
- def reset
35
- @latest_milliseconds = 0
36
- @latest_entropy = ULID.reasonable_entropy
37
- nil
37
+ ulid = if @prev.milliseconds < milliseconds
38
+ ULID.generate(moment: milliseconds)
39
+ else
40
+ ULID.from_milliseconds_and_entropy(milliseconds: @prev.milliseconds, entropy: @prev.entropy.succ)
41
+ end
42
+
43
+ unless ulid > @prev
44
+ base_message = "monotonicity broken from unexpected reasons # generated: #{ulid.inspect}, prev: #{@prev.inspect}"
45
+ additional_information = if Thread.list == [Thread.main]
46
+ '# NOTE: looks single thread only exist'
47
+ else
48
+ '# NOTE: ran on multi threads, so this might from concurrency issue'
49
+ end
50
+
51
+ raise UnexpectedError, base_message + additional_information
52
+ end
53
+
54
+ @prev = ulid
55
+ ulid
56
+ end
38
57
  end
39
58
 
59
+ undef_method :freeze
60
+
40
61
  # @raise [TypeError] always raises exception and does not freeze self
41
62
  # @return [void]
42
63
  def freeze
data/lib/ulid/uuid.rb CHANGED
@@ -15,15 +15,16 @@ class ULID
15
15
  # @return [ULID]
16
16
  # @raise [ParserError] if the given format is not correct for UUIDv4 specs
17
17
  def self.from_uuidv4(uuid)
18
- begin
19
- uuid = uuid.to_str
20
- prefix_trimmed = uuid.sub(/\Aurn:uuid:/, '')
21
- raise "given string is not matched to pattern #{UUIDV4_PATTERN.inspect}" unless UUIDV4_PATTERN.match?(prefix_trimmed)
22
- normalized = prefix_trimmed.gsub(/[^0-9A-Fa-f]/, '')
23
- from_integer(normalized.to_i(16))
24
- rescue => err
25
- raise ParserError, "parsing failure as #{err.inspect} for given #{uuid}"
18
+ uuid = String.try_convert(uuid)
19
+ raise ArgumentError, 'ULID.from_uuidv4 takes only strings' unless uuid
20
+
21
+ prefix_trimmed = uuid.delete_prefix('urn:uuid:')
22
+ unless UUIDV4_PATTERN.match?(prefix_trimmed)
23
+ raise ParserError, "given `#{uuid}` does not match to `#{UUIDV4_PATTERN.inspect}`"
26
24
  end
25
+
26
+ normalized = prefix_trimmed.gsub(/[^0-9A-Fa-f]/, '')
27
+ from_integer(normalized.to_i(16))
27
28
  end
28
29
 
29
30
  # @return [String]
data/lib/ulid/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  class ULID
5
- VERSION = '0.0.18'
5
+ VERSION = '0.1.3'
6
6
  end
data/sig/ulid.rbs CHANGED
@@ -1,6 +1,6 @@
1
- # Classes
2
- class ULID
1
+ class ULID < Object
3
2
  VERSION: String
3
+ CROCKFORD_BASE32_ENCODING_STRING: String
4
4
  TIMESTAMP_ENCODED_LENGTH: 10
5
5
  RANDOMNESS_ENCODED_LENGTH: 16
6
6
  ENCODED_LENGTH: 26
@@ -11,16 +11,13 @@ class ULID
11
11
  MAX_ENTROPY: 1208925819614629174706175
12
12
  MAX_INTEGER: 340282366920938463463374607431768211455
13
13
  TIME_FORMAT_IN_INSPECT: '%Y-%m-%d %H:%M:%S.%3N %Z'
14
- PATTERN: Regexp
15
- STRICT_PATTERN: Regexp
14
+ RANDOM_INTEGER_GENERATOR: ^() -> Integer
15
+ PATTERN_WITH_CROCKFORD_BASE32_SUBSET: Regexp
16
+ STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET: Regexp
17
+ SCANNING_PATTERN: Regexp
16
18
  UUIDV4_PATTERN: Regexp
17
- N32_CHAR_BY_CROCKFORD_BASE32_CHAR: Hash[String, String]
18
- CROCKFORD_BASE32_CHAR_PATTERN: Regexp
19
- CROCKFORD_BASE32_CHAR_BY_N32_CHAR: Hash[String, String]
20
- N32_CHAR_PATTERN: Regexp
21
19
  MIN: ULID
22
20
  MAX: ULID
23
- UNDEFINED: BasicObject
24
21
  include Comparable
25
22
 
26
23
  # The `moment` is a `Time` or `Intger of the milliseconds`
@@ -35,84 +32,462 @@ class ULID
35
32
  class ParserError < Error
36
33
  end
37
34
 
38
- class SetupError < ScriptError
35
+ class UnexpectedError < Error
36
+ end
37
+
38
+ module CrockfordBase32
39
+ class SetupError < ScriptError
40
+ end
41
+
42
+ N32_CHAR_BY_CROCKFORD_BASE32_CHAR: Hash[String, String]
43
+ CROCKFORD_BASE32_CHAR_PATTERN: Regexp
44
+ CROCKFORD_BASE32_CHAR_BY_N32_CHAR: Hash[String, String]
45
+ N32_CHAR_PATTERN: Regexp
46
+
47
+ # A pribate API. Should not be used in your code.
48
+ def self.encode: (Integer integer) -> String
49
+
50
+ # A pribate API. Should not be used in your code.
51
+ def self.decode: (String string) -> Integer
39
52
  end
40
53
 
41
54
  class MonotonicGenerator
42
- attr_accessor latest_milliseconds: Integer
43
- attr_accessor latest_entropy: Integer
55
+ @mutex: Thread::Mutex
56
+
57
+ # The returned value is `not` Thready-safety
58
+ attr_reader prev: ULID | nil
59
+
60
+ # A pribate API. Should not be used in your code.
44
61
  def initialize: -> void
62
+
63
+ # See [How to keep `Sortable` even if in same timestamp](https://github.com/kachick/ruby-ulid#how-to-keep-sortable-even-if-in-same-timestamp)
45
64
  def generate: (?moment: moment) -> ULID
46
- def reset: -> void
65
+
66
+ # The returned value is `not` Thready-safety
67
+ def inspect: -> String
68
+ alias to_s inspect
47
69
  def freeze: -> void
48
70
  end
49
71
 
72
+ interface _ToULID
73
+ def to_ulid: () -> ULID
74
+ end
75
+
50
76
  type octets = [Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer]
51
77
  type timestamp_octets = [Integer, Integer, Integer, Integer, Integer, Integer]
52
78
  type randomness_octets = [Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer]
79
+ type period = Range[Time] | Range[nil] | Range[ULID]
53
80
 
54
- @milliseconds: Integer
55
- @entropy: Integer
56
81
  @string: String?
57
- @integer: Integer?
58
- @octets: octets?
59
- @timestamp_octets: timestamp_octets?
60
- @randomness_octets: randomness_octets?
82
+ @integer: Integer
61
83
  @timestamp: String?
62
84
  @randomness: String?
63
85
  @inspect: String?
64
86
  @time: Time?
65
- @next: ULID?
66
87
 
67
- def self.generate: (?moment: moment, ?entropy: Integer) -> ULID
68
- def self.at: (Time time) -> ULID
88
+ # Retuns a ULID
89
+ #
90
+ # They are sortable when generated in different timestamp with milliseconds precision
91
+ #
92
+ # ```ruby
93
+ # ulids = 1000.times.map do
94
+ # sleep(0.001)
95
+ # ULID.generate
96
+ # end
97
+ # ulids.uniq(&:to_time).size #=> 1000
98
+ # ulids.sort == ulids #=> true
99
+ # ```
100
+ #
101
+ # `ULID.generate` can take fixed `Time` instance.
102
+ # See also the short hand [ULID.at](https://kachick.github.io/ruby-ulid/ULID.html#at-class_method)
103
+ #
104
+ # ```ruby
105
+ # time = Time.at(946684800).utc #=> 2000-01-01 00:00:00 UTC
106
+ # ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB00N018DCPJA4H9379P)
107
+ # ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB006WQT3JTMN0T14EBP)
108
+ # ```
109
+ #
110
+ # The basic generator prefers `randomness`, it does not guarantee `sortable` for same milliseconds ULIDs.
111
+ #
112
+ # ```ruby
113
+ # ulids = 10000.times.map do
114
+ # ULID.generate
115
+ # end
116
+ # ulids.uniq(&:to_time).size #=> 35 (the size is not fixed, might be changed in environment)
117
+ # ulids.sort == ulids #=> false
118
+ # ```
119
+ #
120
+ # If you want to keep sortable even if in same timestamp, See also [ULID::MonotonicGenerator](https://github.com/kachick/ruby-ulid#how-to-keep-sortable-even-if-in-same-timestamp)
121
+ #
122
+ def self.generate: (?moment: moment, ?entropy: Integer) -> self
123
+
124
+ # Shorthand of `ULID.generate(moment: Time)`
125
+ # See also [ULID.generate](https://kachick.github.io/ruby-ulid/ULID.html#generate-class_method)
126
+ #
127
+ # ```ruby
128
+ # time = Time.at(946684800).utc #=> 2000-01-01 00:00:00 UTC
129
+ # ULID.at(time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB002W5BGWWKN76N22H6)
130
+ #
131
+ # ulids = 1000.times.map do |n|
132
+ # ULID.at(time + n)
133
+ # end
134
+ # ulids.sort == ulids #=> true
135
+ # ```
136
+ def self.at: (Time time) -> self
137
+
138
+ # A pribate API. Should not be used in your code.
69
139
  def self.current_milliseconds: -> Integer
70
- def self.milliseconds_from_time: (Time time) -> Integer
140
+
141
+ # A pribate API. Should not be used in your code.
71
142
  def self.milliseconds_from_moment: (moment moment) -> Integer
72
- def self.range: (Range[Time] | Range[nil] time_range) -> Range[ULID]
143
+
144
+ # `ULID` can be element of the `Range`. If you generated the IDs in monotonic generator, ID based filtering is easy and reliable
145
+ #
146
+ # ```ruby
147
+ # include_end = ulid1..ulid2
148
+ # exclude_end = ulid1...ulid2
149
+ #
150
+ # ulids.grep(one_of_the_above)
151
+ # ulids.grep_v(one_of_the_above)
152
+ # ```
153
+ #
154
+ # When want to filter ULIDs with `Time`, we should consider to handle the precision.
155
+ # So this gem provides `ULID.range` to generate reasonable `Range[ULID]` from `Range[Time]`
156
+ #
157
+ # ```ruby
158
+ # # Both of below, The begin of `Range[ULID]` will be the minimum in the floored milliseconds of the time1
159
+ # include_end = ULID.range(time1..time2) #=> The end of `Range[ULID]` will be the maximum in the floored milliseconds of the time2
160
+ # exclude_end = ULID.range(time1...time2) #=> The end of `Range[ULID]` will be the minimum in the floored milliseconds of the time2
161
+ #
162
+ # # Below patterns are acceptable
163
+ # pinpointing = ULID.range(time1..time1) #=> This will match only for all IDs in `time1`
164
+ # until_the_end = ULID.range(..time1) #=> This will match only for all IDs upto `time1` (The `nil` starting `Range` can be used since Ruby 2.7)
165
+ # until_the_end = ULID.range(ULID.min.to_time..time1) #=> This is same as above for Ruby 2.6
166
+ # until_the_ulid_limit = ULID.range(time1..) # This will match only for all IDs from `time1` to max value of the ULID limit
167
+ #
168
+ # # So you can use the generated range objects as below
169
+ # ulids.grep(one_of_the_above)
170
+ # ulids.grep_v(one_of_the_above)
171
+ # #=> I hope the results should be actually you want!
172
+ # ```
173
+ #
174
+ def self.range: (period period) -> Range[ULID]
175
+
176
+ # Returns new `Time` with truncating excess precisions in ULID spec.
177
+ #
178
+ # ```ruby
179
+ # time = Time.at(946684800, Rational('123456.789')).utc
180
+ # #=> 2000-01-01 00:00:00.123456789 UTC
181
+ # ULID.floor(time) #=> 2000-01-01 00:00:00.123 UTC
182
+ # ```
73
183
  def self.floor: (Time time) -> Time
74
- def self.reasonable_entropy: -> Integer
75
- def self.parse: (String string) -> ULID
184
+
185
+ # Get ULID instance from encoded String.
186
+ #
187
+ # ```ruby
188
+ # ulid = ULID.parse('01ARZ3NDEKTSV4RRFFQ69G5FAV')
189
+ # #=> ULID(2016-07-30 23:54:10.259 UTC: 01ARZ3NDEKTSV4RRFFQ69G5FAV)
190
+ # ```
191
+ def self.parse: (_ToStr string) -> self
192
+
193
+ # ```ruby
194
+ # # Currently experimental feature, so needed to load the extension.
195
+ # require 'ulid/uuid'
196
+ #
197
+ # # Basically reversible
198
+ # ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
199
+ # #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
200
+ # ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
201
+ # ```
202
+ #
203
+ # See also [Why this is experimental?](https://github.com/kachick/ruby-ulid/issues/76)
76
204
  def self.from_uuidv4: (String uuid) -> ULID
77
- def self.from_integer: (Integer integer) -> ULID
78
- def self.min: (?moment: moment) -> ULID
79
- def self.max: (?moment: moment) -> ULID
80
- def self.sample: -> ULID
81
- | (Integer number) -> Array[ULID]
82
- def self.valid?: (untyped string) -> bool
83
- def self.scan: (String string) -> Enumerator[ULID, singleton(ULID)]
84
- | (String string) { (ULID ulid) -> void } -> singleton(ULID)
85
- def self.octets_from_integer: (Integer integer) -> octets
86
- def self.inverse_of_digits: (Array[Integer] reversed_digits) -> Integer
87
- def self.from_monotonic_generator: (MonotonicGenerator generator) -> ULID
205
+ def self.from_integer: (Integer integer) -> self
206
+
207
+ # Returns termination values for ULID spec.
208
+ #
209
+ # ```ruby
210
+ # ULID.min
211
+ # #=> ULID(1970-01-01 00:00:00.000 UTC: 00000000000000000000000000)
212
+ # ```
213
+ #
214
+ # It can take `Time` instance as an optional argument.
215
+ # Then returns ULID that has minimum value of randomness part in the timestamp.
216
+ #
217
+ # ```ruby
218
+ # time = Time.at(946684800, Rational('123456.789')).utc
219
+ # #=> 2000-01-01 00:00:00.123456789 UTC
220
+ # ULID.min(time)
221
+ # #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3V0000000000000000)
222
+ # ```
223
+ #
224
+ # See also [ULID.max](https://kachick.github.io/ruby-ulid/ULID.html#max-class_method)
225
+ def self.min: (?moment moment) -> ULID
226
+
227
+ # Returns termination values for ULID spec.
228
+ #
229
+ # ```ruby
230
+ # ULID.max
231
+ # #=> ULID(10889-08-02 05:31:50.655 UTC: 7ZZZZZZZZZZZZZZZZZZZZZZZZZ)
232
+ # ```
233
+ #
234
+ # It can take `Time` instance as an optional argument.
235
+ # Then returns ULID that has maximum value of randomness part in the timestamp.
236
+ #
237
+ # ```ruby
238
+ # time = Time.at(946684800, Rational('123456.789')).utc
239
+ # #=> 2000-01-01 00:00:00.123456789 UTC
240
+ # ULID.max(time)
241
+ # #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3VZZZZZZZZZZZZZZZZ)
242
+ # ```
243
+ #
244
+ # See also [ULID.min](https://kachick.github.io/ruby-ulid/ULID.html#min-class_method)
245
+ def self.max: (?moment moment) -> ULID
246
+
247
+ # Returns random ULIDs.
248
+ #
249
+ # Basically ignores generating time.
250
+ #
251
+ # ```ruby
252
+ # ULID.sample #=> ULID(2545-07-26 06:51:20.085 UTC: 0GGKQ45GMNMZR6N8A8GFG0ZXST)
253
+ # ULID.sample #=> ULID(5098-07-26 21:31:06.946 UTC: 2SSBNGGYA272J7BMDCG4Z6EEM5)
254
+ # ULID.sample(0) #=> []
255
+ # ULID.sample(1) #=> [ULID(2241-04-16 03:31:18.440 UTC: 07S52YWZ98AZ8T565MD9VRYMQH)]
256
+ # ULID.sample(5)
257
+ # #=>
258
+ # #[ULID(5701-04-29 12:41:19.647 UTC: 3B2YH2DV0ZYDDATGTYSKMM1CMT),
259
+ # # ULID(2816-08-01 01:21:46.612 UTC: 0R9GT6RZKMK3RG02Q2HAFVKEY2),
260
+ # # ULID(10408-10-05 17:06:27.848 UTC: 7J6CPTEEC86Y24EQ4F1Y93YYN0),
261
+ # # ULID(2741-09-02 16:24:18.803 UTC: 0P4Q4V34KKAJW46QW47WQB5463),
262
+ # # ULID(2665-03-16 14:50:22.724 UTC: 0KYFW9DWM4CEGFNTAC6YFAVVJ6)]
263
+ # ```
264
+ #
265
+ # You can specify a range object for the timestamp restriction, See also [ULID.range](https://kachick.github.io/ruby-ulid/ULID.html#range-class_method).
266
+ #
267
+ # ```ruby
268
+ # ulid1 = ULID.parse('01F4A5Y1YAQCYAYCTC7GRMJ9AA') #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
269
+ # ulid2 = ULID.parse('01F4PTVCSN9ZPFKYTY2DDJVRK4') #=> ULID(2021-05-02 15:23:48.917 UTC: 01F4PTVCSN9ZPFKYTY2DDJVRK4)
270
+ # ulids = ULID.sample(1000, period: ulid1..ulid2)
271
+ # ulids.uniq.size #=> 1000
272
+ # ulids.take(10)
273
+ # #=>
274
+ # #[ULID(2021-05-02 06:57:19.954 UTC: 01F4NXW02JNB8H0J0TK48JD39X),
275
+ # # ULID(2021-05-02 07:06:07.458 UTC: 01F4NYC372GVP7NS0YAYQGT4VZ),
276
+ # # ULID(2021-05-01 06:16:35.791 UTC: 01F4K94P6F6P68K0H64WRDSFKW),
277
+ # # ULID(2021-04-27 22:17:37.844 UTC: 01F4APHGSMFJZQTGXKZBFFBPJP),
278
+ # # ULID(2021-04-28 20:17:55.357 UTC: 01F4D231MXQJXAR8G2JZHEJNH3),
279
+ # # ULID(2021-04-30 07:18:54.307 UTC: 01F4GTA2332AS2VPHC4FMKC7R5),
280
+ # # ULID(2021-05-02 12:26:03.480 UTC: 01F4PGNXARG554Y3HYVBDW4T9S),
281
+ # # ULID(2021-04-29 09:52:15.107 UTC: 01F4EGP483ZX2747FQPWQNPPMW),
282
+ # # ULID(2021-04-29 03:18:24.152 UTC: 01F4DT4Z4RA0QV8WFQGRAG63EH),
283
+ # # ULID(2021-05-02 13:27:16.394 UTC: 01F4PM605ABF5SDVMEHBH8JJ9R)]
284
+ # ULID.sample(10, period: ulid1.to_time..ulid2.to_time)
285
+ # #=>
286
+ # # [ULID(2021-04-29 06:44:41.513 UTC: 01F4E5YPD9XQ3MYXWK8ZJKY8SW),
287
+ # # ULID(2021-05-01 00:35:06.629 UTC: 01F4JNKD85SVK1EAEYSJGF53A2),
288
+ # # ULID(2021-05-02 12:45:28.408 UTC: 01F4PHSEYRG9BWBEWMRW1XE6WW),
289
+ # # ULID(2021-05-01 03:06:09.130 UTC: 01F4JY7ZBABCBMX16XH2Q4JW4W),
290
+ # # ULID(2021-04-29 21:38:58.109 UTC: 01F4FS45DX4049JEQK4W6TER6G),
291
+ # # ULID(2021-04-29 17:14:14.116 UTC: 01F4F9ZDQ449BE8BBZFEHYQWG2),
292
+ # # ULID(2021-04-30 16:18:08.205 UTC: 01F4HS5DPD1HWDVJNJ6YKJXKSK),
293
+ # # ULID(2021-04-30 10:31:33.602 UTC: 01F4H5ATF2A1CSQF0XV5NKZ288),
294
+ # # ULID(2021-04-28 16:49:06.484 UTC: 01F4CP4PDM214Q6H3KJP7DYJRR),
295
+ # # ULID(2021-04-28 15:05:06.808 UTC: 01F4CG68ZRST94T056KRZ5K9S4)]
296
+ # ```
297
+ def self.sample: (?period: period) -> self
298
+ | (Integer number, ?period: period) -> Array[self]
299
+ def self.valid?: (untyped) -> bool
300
+
301
+ # Returns parsed ULIDs from given String for rough operations.
302
+ #
303
+ # ```ruby
304
+ # json =<<'EOD'
305
+ # {
306
+ # "id": "01F4GNAV5ZR6FJQ5SFQC7WDSY3",
307
+ # "author": {
308
+ # "id": "01F4GNBXW1AM2KWW52PVT3ZY9X",
309
+ # "name": "kachick"
310
+ # },
311
+ # "title": "My awesome blog post",
312
+ # "comments": [
313
+ # {
314
+ # "id": "01F4GNCNC3CH0BCRZBPPDEKBKS",
315
+ # "commenter": {
316
+ # "id": "01F4GNBXW1AM2KWW52PVT3ZY9X",
317
+ # "name": "kachick"
318
+ # }
319
+ # },
320
+ # {
321
+ # "id": "01F4GNCXAMXQ1SGBH5XCR6ZH0M",
322
+ # "commenter": {
323
+ # "id": "01F4GND4RYYSKNAADHQ9BNXAWJ",
324
+ # "name": "pankona"
325
+ # }
326
+ # }
327
+ # ]
328
+ # }
329
+ # EOD
330
+ #
331
+ # ULID.scan(json).to_a
332
+ # #=>
333
+ # # [ULID(2021-04-30 05:51:57.119 UTC: 01F4GNAV5ZR6FJQ5SFQC7WDSY3),
334
+ # # ULID(2021-04-30 05:52:32.641 UTC: 01F4GNBXW1AM2KWW52PVT3ZY9X),
335
+ # # ULID(2021-04-30 05:52:56.707 UTC: 01F4GNCNC3CH0BCRZBPPDEKBKS),
336
+ # # ULID(2021-04-30 05:52:32.641 UTC: 01F4GNBXW1AM2KWW52PVT3ZY9X),
337
+ # # ULID(2021-04-30 05:53:04.852 UTC: 01F4GNCXAMXQ1SGBH5XCR6ZH0M),
338
+ # # ULID(2021-04-30 05:53:12.478 UTC: 01F4GND4RYYSKNAADHQ9BNXAWJ)]
339
+ # ```
340
+ def self.scan: (_ToStr string) -> Enumerator[self, singleton(ULID)]
341
+ | (_ToStr string) { (self ulid) -> void } -> singleton(ULID)
342
+ def self.from_milliseconds_and_entropy: (milliseconds: Integer, entropy: Integer) -> self
343
+ def self.try_convert: (_ToULID) -> ULID
344
+ | (untyped) -> nil
345
+
346
+ # ```ruby
347
+ # ulid = ULID.generate
348
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
349
+ # ulid.milliseconds #=> 1619544442826
350
+ # ```
88
351
  attr_reader milliseconds: Integer
89
352
  attr_reader entropy: Integer
90
- def initialize: (milliseconds: Integer, entropy: Integer, ?integer: Integer) -> void
353
+
354
+ # ```ruby
355
+ # ulid = ULID.generate
356
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
357
+ # ulid.to_s #=> "01F4A5Y1YAQCYAYCTC7GRMJ9AA"
358
+ # ```
91
359
  def to_s: -> String
360
+
361
+ # ```ruby
362
+ # ulid = ULID.generate
363
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
364
+ # ulid.to_i #=> 1957909092946624190749577070267409738
365
+ # ```
92
366
  def to_i: -> Integer
93
367
  alias hash to_i
368
+
369
+ # Basically same as String based sort.
370
+ #
371
+ # ```ruby
372
+ # ulids = ULID.sample(10000); nil
373
+ # ulids.map(&:to_s).sort == ulids.sort.map(&:to_s)
374
+ # #=> true
375
+ # ```
376
+ #
377
+ # To be precise, this sorting unaffected with `case sensitive or not` and might handle [ulid/spec#57](https://github.com/ulid/spec/pull/57) in future. So preferable than `lexicographically sortable` in actual case.
378
+ #
94
379
  def <=>: (ULID other) -> Integer
95
380
  | (untyped other) -> nil
381
+
382
+ # ```ruby
383
+ # ulid = ULID.generate
384
+ # ulid.inspect #=> "ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)"
385
+ # ```
96
386
  def inspect: -> String
387
+
388
+ # ```ruby
389
+ # ULID.parse('4NNB20D9C1ME2NGMTX51ERZJX0') == ULID.parse('4nnb20d9c1me2ngmtx51erzjx0')
390
+ # #=> true
391
+ # ```
97
392
  def eql?: (untyped other) -> bool
98
393
  alias == eql?
99
394
  def ===: (untyped other) -> bool
395
+
396
+ # ```ruby
397
+ # ulid = ULID.generate
398
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
399
+ # ulid.to_time #=> 2021-04-27 17:27:22.826 UTC
400
+ # ```
100
401
  def to_time: -> Time
402
+
403
+ # ```ruby
404
+ # ulid = ULID.generate
405
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
406
+ # ulid.timestamp #=> "01F4A5Y1YA"
407
+ # ```
101
408
  def timestamp: -> String
409
+
410
+ # ```ruby
411
+ # ulid = ULID.generate
412
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
413
+ # ulid.randomness #=> "QCYAYCTC7GRMJ9AA"
414
+ # ```
102
415
  def randomness: -> String
416
+
417
+ # Intentionally avoiding to use `Record type` ref: https://github.com/ruby/rbs/blob/4fb4c33b2325d1a73d79ff7aaeb49f21cec1e0e5/docs/syntax.md#record-type
418
+ # Because the returning values are not fixed.
103
419
  def patterns: -> Hash[Symbol, Regexp | String]
104
- def pattern: -> Regexp
105
- def strict_pattern: -> Regexp
106
420
  def octets: -> octets
107
421
  def timestamp_octets: -> timestamp_octets
108
422
  def randomness_octets: -> randomness_octets
423
+
424
+ # ```ruby
425
+ # # Currently experimental feature, so needed to load the extension.
426
+ # require 'ulid/uuid'
427
+ #
428
+ # # Basically reversible
429
+ # ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
430
+ # #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
431
+ # ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
432
+ # ```
433
+ #
434
+ # See also [Why this is experimental?](https://github.com/kachick/ruby-ulid/issues/76)
109
435
  def to_uuidv4: -> String
110
- def next: -> ULID?
111
- alias succ next
436
+
437
+ # Returns next(successor) ULID.
438
+ # Especially `ULID#succ` makes it possible `Range[ULID]#each`.
439
+ #
440
+ # NOTE: But basically `Range[ULID]#each` should not be used, incrementing 128 bits IDs are not reasonable operation in most case
441
+ #
442
+ # ```ruby
443
+ # ULID.parse('01BX5ZZKBKZZZZZZZZZZZZZZZY').next.to_s #=> "01BX5ZZKBKZZZZZZZZZZZZZZZZ"
444
+ # ULID.parse('01BX5ZZKBKZZZZZZZZZZZZZZZZ').next.to_s #=> "01BX5ZZKBM0000000000000000"
445
+ # ULID.parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').next #=> nil
446
+ # ```
447
+ #
448
+ # See also [ULID#pred](https://kachick.github.io/ruby-ulid/ULID.html#pred-instance_method)
449
+ def succ: -> ULID?
450
+ alias next succ
451
+
452
+ # Returns predecessor ULID.
453
+ #
454
+ # ```ruby
455
+ # ULID.parse('01BX5ZZKBK0000000000000001').pred.to_s #=> "01BX5ZZKBK0000000000000000"
456
+ # ULID.parse('01BX5ZZKBK0000000000000000').pred.to_s #=> "01BX5ZZKBJZZZZZZZZZZZZZZZZ"
457
+ # ULID.parse('00000000000000000000000000').pred #=> nil
458
+ # ```
459
+ #
460
+ # See also [ULID#succ](https://kachick.github.io/ruby-ulid/ULID.html#succ-instance_method)
112
461
  def pred: -> ULID?
113
462
  def freeze: -> self
114
463
 
464
+ # Returns `self`
465
+ def to_ulid: -> self
466
+
467
+ # Returns `self`. Not a new instance.
468
+ def dup: -> self
469
+
470
+ # Same API as [Object#clone](https://github.com/ruby/rbs/blob/4fb4c33b2325d1a73d79ff7aaeb49f21cec1e0e5/core/object.rbs#L79)
471
+ # But actually returns `self`. Not a new instance.
472
+ def clone: (?freeze: bool) -> self
473
+
115
474
  private
116
- def self.argument_error_for_range_building: (untyped argument) -> ArgumentError
475
+
476
+ # A pribate API. Should not be used in your code.
477
+ def self.new: (milliseconds: Integer, entropy: Integer, integer: Integer) -> self
478
+
479
+ # A pribate API. Should not be used in your code.
480
+ def self.reasonable_entropy: -> Integer
481
+
482
+ # A pribate API. Should not be used in your code.
483
+ def self.milliseconds_from_time: (Time time) -> Integer
484
+
485
+ # A pribate API. Should not be used in your code.
486
+ def self.safe_get_class_name: (untyped object) -> String
487
+
488
+ # A pribate API. Should not be used in your code.
489
+ def initialize: (milliseconds: Integer, entropy: Integer, integer: Integer) -> void
490
+
491
+ # A pribate API. Should not be used in your code.
117
492
  def cache_all_instance_variables: -> void
118
493
  end