ruby-ulid 0.0.17 → 0.1.2
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.
- checksums.yaml +4 -4
- data/README.md +85 -13
- data/lib/ulid.rb +231 -228
- data/lib/ulid/crockford_base32.rb +70 -0
- data/lib/ulid/monotonic_generator.rb +42 -21
- data/lib/ulid/uuid.rb +38 -0
- data/lib/ulid/version.rb +1 -1
- data/sig/ulid.rbs +419 -53
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04b50bb9543b78d03a0e1676b988060b3e34348c0785979fde52c46e414be1d8
|
4
|
+
data.tar.gz: bf070ba0522e8ce743c67bcfa225bf5b49009ae4ecd6532eef19484d51eb14ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ad79f68c4903f775750417085676a98e810cae71b635b0b0e3e5547dd8b652a6bd89b01cb452510054f400a9bfef7ae26e308456d46a7571be10cb7ef1f334f
|
7
|
+
data.tar.gz: 88769b682521bb83b3775784e808b4e08636313d2055c7afe1f6b3a966e211237c597e0c354e0a6a6a26e227246c96845671f24cd7ebb8bf9d93014d043147f0
|
data/README.md
CHANGED
@@ -10,7 +10,7 @@ Also providing [ruby/rbs](https://github.com/ruby/rbs) signature files.
|
|
10
10
|
|
11
11
|

|
12
12
|
|
13
|
-

|
14
14
|
[](http://badge.fury.io/rb/ruby-ulid)
|
15
15
|
|
16
16
|
## Universally Unique Lexicographically Sortable Identifier
|
@@ -49,7 +49,7 @@ Should be installed!
|
|
49
49
|
Add this line to your application/library's `Gemfile` is needed in basic use-case
|
50
50
|
|
51
51
|
```ruby
|
52
|
-
gem 'ruby-ulid', '0.0.
|
52
|
+
gem 'ruby-ulid', '>= 0.1.2', '< 0.2.0'
|
53
53
|
```
|
54
54
|
|
55
55
|
### Generator and Parser
|
@@ -62,7 +62,11 @@ require 'ulid'
|
|
62
62
|
|
63
63
|
ulid = ULID.generate #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
64
64
|
ulid.to_time #=> 2021-04-27 17:27:22.826 UTC
|
65
|
+
ulid.milliseconds #=> 1619544442826
|
65
66
|
ulid.to_s #=> "01F4A5Y1YAQCYAYCTC7GRMJ9AA"
|
67
|
+
ulid.timestamp #=> "01F4A5Y1YA"
|
68
|
+
ulid.randomness #=> "QCYAYCTC7GRMJ9AA"
|
69
|
+
ulid.to_i #=> 1957909092946624190749577070267409738
|
66
70
|
ulid.octets #=> [1, 121, 20, 95, 7, 202, 187, 60, 175, 51, 76, 60, 49, 73, 37, 74]
|
67
71
|
```
|
68
72
|
|
@@ -86,15 +90,16 @@ ulids.uniq(&:to_time).size #=> 1000
|
|
86
90
|
ulids.sort == ulids #=> true
|
87
91
|
```
|
88
92
|
|
89
|
-
`ULID.generate` can take fixed `Time` instance
|
93
|
+
`ULID.generate` can take fixed `Time` instance. The shorthand is `ULID.at`
|
90
94
|
|
91
95
|
```ruby
|
92
96
|
time = Time.at(946684800).utc #=> 2000-01-01 00:00:00 UTC
|
93
97
|
ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB00N018DCPJA4H9379P)
|
94
98
|
ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB006WQT3JTMN0T14EBP)
|
99
|
+
ULID.at(time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB002W5BGWWKN76N22H6)
|
95
100
|
|
96
101
|
ulids = 1000.times.map do |n|
|
97
|
-
ULID.
|
102
|
+
ULID.at(time + n)
|
98
103
|
end
|
99
104
|
ulids.sort == ulids #=> true
|
100
105
|
```
|
@@ -141,6 +146,8 @@ sample_ulids_by_the_time.take(5) #=>
|
|
141
146
|
ulids.sort == ulids #=> true
|
142
147
|
```
|
143
148
|
|
149
|
+
Same generator does not generate duplicated ULIDs even in multi threads environment. It is implemented with [Thread::Mutex](https://github.com/ruby/ruby/blob/5f8bca32571fa9c651f6903d36f66082363f8879/thread_sync.c#L1572-L1582)
|
150
|
+
|
144
151
|
### Filtering IDs with `Time`
|
145
152
|
|
146
153
|
`ULID` can be element of the `Range`. If you generated the IDs in monotonic generator, ID based filtering is easy and reliable
|
@@ -163,7 +170,7 @@ exclude_end = ULID.range(time1...time2) #=> The end of `Range[ULID]` will be the
|
|
163
170
|
|
164
171
|
# Below patterns are acceptable
|
165
172
|
pinpointing = ULID.range(time1..time1) #=> This will match only for all IDs in `time1`
|
166
|
-
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)
|
173
|
+
# 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)
|
167
174
|
until_the_end = ULID.range(ULID.min.to_time..time1) #=> This is same as above for Ruby 2.6
|
168
175
|
until_the_ulid_limit = ULID.range(time1..) # This will match only for all IDs from `time1` to max value of the ULID limit
|
169
176
|
|
@@ -173,6 +180,13 @@ ulids.grep_v(one_of_the_above)
|
|
173
180
|
#=> I hope the results should be actually you want!
|
174
181
|
```
|
175
182
|
|
183
|
+
If you want to manually handle the Time objects, `ULID.floor` returns new `Time` with truncating excess precisions in ULID spec.
|
184
|
+
|
185
|
+
```ruby
|
186
|
+
time = Time.at(946684800, Rational('123456.789')).utc #=> 2000-01-01 00:00:00.123456789 UTC
|
187
|
+
ULID.floor(time) #=> 2000-01-01 00:00:00.123 UTC
|
188
|
+
```
|
189
|
+
|
176
190
|
### Scanner for string (e.g. `JSON`)
|
177
191
|
|
178
192
|
For rough operations, `ULID.scan` might be useful.
|
@@ -215,17 +229,31 @@ ULID.scan(json).to_a
|
|
215
229
|
# ULID(2021-04-30 05:53:12.478 UTC: 01F4GND4RYYSKNAADHQ9BNXAWJ)]
|
216
230
|
```
|
217
231
|
|
232
|
+
`ULID#patterns` is a util for text based operations.
|
233
|
+
The results and spec are not fixed. Should not be used except snippets/console operation
|
234
|
+
|
235
|
+
```ruby
|
236
|
+
ULID.parse('01F4GNBXW1AM2KWW52PVT3ZY9X').patterns
|
237
|
+
#=> returns like a fallowing Hash
|
238
|
+
{
|
239
|
+
named_captures: /(?<timestamp>01F4GNBXW1)(?<randomness>AM2KWW52PVT3ZY9X)/i,
|
240
|
+
strict_named_captures: /\A(?<timestamp>01F4GNBXW1)(?<randomness>AM2KWW52PVT3ZY9X)\z/i
|
241
|
+
}
|
242
|
+
```
|
243
|
+
|
218
244
|
### Some methods to help manipulations
|
219
245
|
|
220
246
|
`ULID.min` and `ULID.max` return termination values for ULID spec.
|
221
247
|
|
248
|
+
It can take `Time` instance as an optional argument. Then returns min/max ID that has limit of randomness part in the time.
|
249
|
+
|
222
250
|
```ruby
|
223
251
|
ULID.min #=> ULID(1970-01-01 00:00:00.000 UTC: 00000000000000000000000000)
|
224
252
|
ULID.max #=> ULID(10889-08-02 05:31:50.655 UTC: 7ZZZZZZZZZZZZZZZZZZZZZZZZZ)
|
225
253
|
|
226
254
|
time = Time.at(946684800, Rational('123456.789')).utc #=> 2000-01-01 00:00:00.123456789 UTC
|
227
|
-
ULID.min(
|
228
|
-
ULID.max(
|
255
|
+
ULID.min(time) #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3V0000000000000000)
|
256
|
+
ULID.max(time) #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3VZZZZZZZZZZZZZZZZ)
|
229
257
|
```
|
230
258
|
|
231
259
|
`ULID#next` and `ULID#succ` returns next(successor) ULID.
|
@@ -247,7 +275,9 @@ ULID.parse('01BX5ZZKBK0000000000000000').pred.to_s #=> "01BX5ZZKBJZZZZZZZZZZZZZZ
|
|
247
275
|
ULID.parse('00000000000000000000000000').pred #=> nil
|
248
276
|
```
|
249
277
|
|
250
|
-
`ULID.sample` returns random ULIDs
|
278
|
+
`ULID.sample` returns random ULIDs.
|
279
|
+
|
280
|
+
Basically ignores generating time.
|
251
281
|
|
252
282
|
```ruby
|
253
283
|
ULID.sample #=> ULID(2545-07-26 06:51:20.085 UTC: 0GGKQ45GMNMZR6N8A8GFG0ZXST)
|
@@ -263,13 +293,49 @@ ULID.sample(5)
|
|
263
293
|
# ULID(2665-03-16 14:50:22.724 UTC: 0KYFW9DWM4CEGFNTAC6YFAVVJ6)]
|
264
294
|
```
|
265
295
|
|
296
|
+
You can specify a range object for the timestamp restriction, see also `ULID.range`.
|
297
|
+
|
298
|
+
```ruby
|
299
|
+
ulid1 = ULID.parse('01F4A5Y1YAQCYAYCTC7GRMJ9AA') #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
300
|
+
ulid2 = ULID.parse('01F4PTVCSN9ZPFKYTY2DDJVRK4') #=> ULID(2021-05-02 15:23:48.917 UTC: 01F4PTVCSN9ZPFKYTY2DDJVRK4)
|
301
|
+
ulids = ULID.sample(1000, period: ulid1..ulid2)
|
302
|
+
ulids.uniq.size #=> 1000
|
303
|
+
ulids.take(10)
|
304
|
+
#=>
|
305
|
+
#[ULID(2021-05-02 06:57:19.954 UTC: 01F4NXW02JNB8H0J0TK48JD39X),
|
306
|
+
# ULID(2021-05-02 07:06:07.458 UTC: 01F4NYC372GVP7NS0YAYQGT4VZ),
|
307
|
+
# ULID(2021-05-01 06:16:35.791 UTC: 01F4K94P6F6P68K0H64WRDSFKW),
|
308
|
+
# ULID(2021-04-27 22:17:37.844 UTC: 01F4APHGSMFJZQTGXKZBFFBPJP),
|
309
|
+
# ULID(2021-04-28 20:17:55.357 UTC: 01F4D231MXQJXAR8G2JZHEJNH3),
|
310
|
+
# ULID(2021-04-30 07:18:54.307 UTC: 01F4GTA2332AS2VPHC4FMKC7R5),
|
311
|
+
# ULID(2021-05-02 12:26:03.480 UTC: 01F4PGNXARG554Y3HYVBDW4T9S),
|
312
|
+
# ULID(2021-04-29 09:52:15.107 UTC: 01F4EGP483ZX2747FQPWQNPPMW),
|
313
|
+
# ULID(2021-04-29 03:18:24.152 UTC: 01F4DT4Z4RA0QV8WFQGRAG63EH),
|
314
|
+
# ULID(2021-05-02 13:27:16.394 UTC: 01F4PM605ABF5SDVMEHBH8JJ9R)]
|
315
|
+
ULID.sample(10, period: ulid1.to_time..ulid2.to_time)
|
316
|
+
#=>
|
317
|
+
# [ULID(2021-04-29 06:44:41.513 UTC: 01F4E5YPD9XQ3MYXWK8ZJKY8SW),
|
318
|
+
# ULID(2021-05-01 00:35:06.629 UTC: 01F4JNKD85SVK1EAEYSJGF53A2),
|
319
|
+
# ULID(2021-05-02 12:45:28.408 UTC: 01F4PHSEYRG9BWBEWMRW1XE6WW),
|
320
|
+
# ULID(2021-05-01 03:06:09.130 UTC: 01F4JY7ZBABCBMX16XH2Q4JW4W),
|
321
|
+
# ULID(2021-04-29 21:38:58.109 UTC: 01F4FS45DX4049JEQK4W6TER6G),
|
322
|
+
# ULID(2021-04-29 17:14:14.116 UTC: 01F4F9ZDQ449BE8BBZFEHYQWG2),
|
323
|
+
# ULID(2021-04-30 16:18:08.205 UTC: 01F4HS5DPD1HWDVJNJ6YKJXKSK),
|
324
|
+
# ULID(2021-04-30 10:31:33.602 UTC: 01F4H5ATF2A1CSQF0XV5NKZ288),
|
325
|
+
# ULID(2021-04-28 16:49:06.484 UTC: 01F4CP4PDM214Q6H3KJP7DYJRR),
|
326
|
+
# ULID(2021-04-28 15:05:06.808 UTC: 01F4CG68ZRST94T056KRZ5K9S4)]
|
327
|
+
```
|
328
|
+
|
266
329
|
### UUIDv4 converter for migration use-cases
|
267
330
|
|
268
331
|
`ULID.from_uuidv4` and `ULID#to_uuidv4` is the converter.
|
269
|
-
The imported timestamp is meaningless. So ULID's benefit will lost
|
332
|
+
The imported timestamp is meaningless. So ULID's benefit will lost.
|
270
333
|
|
271
334
|
```ruby
|
272
|
-
#
|
335
|
+
# Currently experimental feature, so needed to load the extension.
|
336
|
+
require 'ulid/uuid'
|
337
|
+
|
338
|
+
# Basically reversible
|
273
339
|
ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39') #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
|
274
340
|
ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
|
275
341
|
|
@@ -322,13 +388,13 @@ Major methods can be replaced as below.
|
|
322
388
|
-ULID.generate
|
323
389
|
+ULID.generate.to_s
|
324
390
|
-ULID.at(time)
|
325
|
-
+ULID.
|
391
|
+
+ULID.at(time).to_s
|
326
392
|
-ULID.time(string)
|
327
393
|
+ULID.parse(string).to_time
|
328
394
|
-ULID.min_ulid_at(time)
|
329
|
-
+ULID.min(
|
395
|
+
+ULID.min(time).to_s
|
330
396
|
-ULID.max_ulid_at(time)
|
331
|
-
+ULID.max(
|
397
|
+
+ULID.max(time).to_s
|
332
398
|
```
|
333
399
|
|
334
400
|
NOTE: It is still having precision issue similar as `ulid gem` in the both generator and parser. I sent PRs.
|
@@ -337,6 +403,12 @@ NOTE: It is still having precision issue similar as `ulid gem` in the both gener
|
|
337
403
|
1. [Fix to handle timestamp precision in parser](https://github.com/abachman/ulid-ruby/pull/5)
|
338
404
|
1. [Fix to handle timestamp precision in generator](https://github.com/abachman/ulid-ruby/pull/4)
|
339
405
|
|
406
|
+
### Compare performance with them
|
407
|
+
|
408
|
+
See [Benchmark](https://github.com/kachick/ruby-ulid/wiki/Benchmark).
|
409
|
+
|
410
|
+
The results are not something to be proud of.
|
411
|
+
|
340
412
|
## References
|
341
413
|
|
342
414
|
- [Repository](https://github.com/kachick/ruby-ulid)
|
data/lib/ulid.rb
CHANGED
@@ -15,76 +15,114 @@ class ULID
|
|
15
15
|
class Error < StandardError; end
|
16
16
|
class OverflowError < Error; end
|
17
17
|
class ParserError < Error; end
|
18
|
-
class
|
18
|
+
class UnexpectedError < Error; end
|
19
19
|
|
20
|
-
|
21
|
-
#
|
22
|
-
#
|
23
|
-
|
20
|
+
# Excluded I, L, O, U, -.
|
21
|
+
# This is the encoding patterns.
|
22
|
+
# The decoding issue is written in ULID::CrockfordBase32
|
23
|
+
CROCKFORD_BASE32_ENCODING_STRING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
TIMESTAMP_ENCODED_LENGTH = 10
|
26
|
+
RANDOMNESS_ENCODED_LENGTH = 16
|
27
|
+
ENCODED_LENGTH = TIMESTAMP_ENCODED_LENGTH + RANDOMNESS_ENCODED_LENGTH
|
28
28
|
TIMESTAMP_OCTETS_LENGTH = 6
|
29
29
|
RANDOMNESS_OCTETS_LENGTH = 10
|
30
30
|
OCTETS_LENGTH = TIMESTAMP_OCTETS_LENGTH + RANDOMNESS_OCTETS_LENGTH
|
31
31
|
MAX_MILLISECONDS = 281474976710655
|
32
32
|
MAX_ENTROPY = 1208925819614629174706175
|
33
33
|
MAX_INTEGER = 340282366920938463463374607431768211455
|
34
|
-
PATTERN = /(?<timestamp>[0-7][#{encoding_string}]{#{TIMESTAMP_PART_LENGTH - 1}})(?<randomness>[#{encoding_string}]{#{RANDOMNESS_PART_LENGTH}})/i.freeze
|
35
|
-
STRICT_PATTERN = /\A#{PATTERN.source}\z/i.freeze
|
36
34
|
|
37
|
-
#
|
38
|
-
|
35
|
+
# @see https://github.com/ulid/spec/pull/57
|
36
|
+
# Currently not used as a constant, but kept as a reference for now.
|
37
|
+
PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /(?<timestamp>[0-7][#{CROCKFORD_BASE32_ENCODING_STRING}]{#{TIMESTAMP_ENCODED_LENGTH - 1}})(?<randomness>[#{CROCKFORD_BASE32_ENCODING_STRING}]{#{RANDOMNESS_ENCODED_LENGTH}})/i.freeze
|
38
|
+
|
39
|
+
STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /\A#{PATTERN_WITH_CROCKFORD_BASE32_SUBSET.source}\z/i.freeze
|
40
|
+
|
41
|
+
# Optimized for `ULID.scan`, might be changed the definition with gathered `ULID.scan` spec changed.
|
42
|
+
# This can't contain `\b` for considering UTF-8 (e.g. Japanese), so intentional `false negative` definition.
|
43
|
+
SCANNING_PATTERN = /[0-7][#{CROCKFORD_BASE32_ENCODING_STRING}]{#{TIMESTAMP_ENCODED_LENGTH - 1}}[#{CROCKFORD_BASE32_ENCODING_STRING}]{#{RANDOMNESS_ENCODED_LENGTH}}/i.freeze
|
39
44
|
|
40
45
|
# Same as Time#inspect since Ruby 2.7, just to keep backward compatibility
|
41
46
|
# @see https://bugs.ruby-lang.org/issues/15958
|
42
47
|
TIME_FORMAT_IN_INSPECT = '%Y-%m-%d %H:%M:%S.%3N %Z'
|
43
48
|
|
44
|
-
|
45
|
-
Kernel.instance_method(:freeze).bind(UNDEFINED).call
|
49
|
+
private_class_method :new
|
46
50
|
|
47
51
|
# @param [Integer, Time] moment
|
48
52
|
# @param [Integer] entropy
|
49
53
|
# @return [ULID]
|
50
54
|
def self.generate(moment: current_milliseconds, entropy: reasonable_entropy)
|
51
|
-
|
55
|
+
from_milliseconds_and_entropy(milliseconds: milliseconds_from_moment(moment), entropy: entropy)
|
52
56
|
end
|
53
57
|
|
54
|
-
#
|
58
|
+
# Short hand of `ULID.generate(moment: time)`
|
59
|
+
# @param [Time] time
|
60
|
+
# @return [ULID]
|
61
|
+
def self.at(time)
|
62
|
+
raise ArgumentError, 'ULID.at takes only `Time` instance' unless Time === time
|
63
|
+
from_milliseconds_and_entropy(milliseconds: milliseconds_from_time(time), entropy: reasonable_entropy)
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param [Time, Integer] moment
|
55
67
|
# @return [ULID]
|
56
|
-
def self.min(moment
|
68
|
+
def self.min(moment=0)
|
57
69
|
0.equal?(moment) ? MIN : generate(moment: moment, entropy: 0)
|
58
70
|
end
|
59
71
|
|
60
|
-
# @param [
|
72
|
+
# @param [Time, Integer] moment
|
61
73
|
# @return [ULID]
|
62
|
-
def self.max(moment
|
74
|
+
def self.max(moment=MAX_MILLISECONDS)
|
63
75
|
MAX_MILLISECONDS.equal?(moment) ? MAX : generate(moment: moment, entropy: MAX_ENTROPY)
|
64
76
|
end
|
65
77
|
|
66
|
-
|
67
|
-
|
68
|
-
|
78
|
+
RANDOM_INTEGER_GENERATOR = -> {
|
79
|
+
SecureRandom.random_number(MAX_INTEGER)
|
80
|
+
}
|
81
|
+
|
82
|
+
# @param [Range<Time>, Range<nil>, Range[ULID], nil] period
|
83
|
+
# @overload sample(number, period: nil)
|
84
|
+
# @param [Integer] number
|
85
|
+
# @return [Array<ULID>]
|
86
|
+
# @raise [ArgumentError] if the given number is lager than `ULID spec limits` or `Possibilities of given period`, or given negative number
|
87
|
+
# @overload sample(period: nil)
|
88
|
+
# @return [ULID]
|
69
89
|
# @note Major difference of `Array#sample` interface is below
|
70
90
|
# * Do not ensure the uniqueness
|
71
91
|
# * Do not take random generator for the arguments
|
72
92
|
# * Raising error instead of truncating elements for the given number
|
73
|
-
def self.sample(
|
74
|
-
if
|
75
|
-
|
93
|
+
def self.sample(*args, period: nil)
|
94
|
+
int_generator = if period
|
95
|
+
ulid_range = range(period)
|
96
|
+
min, max, exclude_end = ulid_range.begin.to_i, ulid_range.end.to_i, ulid_range.exclude_end?
|
97
|
+
|
98
|
+
possibilities = (max - min) + (exclude_end ? 0 : 1)
|
99
|
+
raise ArgumentError, "given range `#{ulid_range.inspect}` does not have possibilities" unless possibilities.positive?
|
100
|
+
|
101
|
+
-> {
|
102
|
+
SecureRandom.random_number(possibilities) + min
|
103
|
+
}
|
76
104
|
else
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
105
|
+
RANDOM_INTEGER_GENERATOR
|
106
|
+
end
|
107
|
+
|
108
|
+
case args.size
|
109
|
+
when 0
|
110
|
+
from_integer(int_generator.call)
|
111
|
+
when 1
|
112
|
+
number = args.first
|
113
|
+
raise ArgumentError, 'accepts no argument or integer only' unless Integer === number
|
114
|
+
|
115
|
+
if number > MAX_INTEGER || number.negative?
|
116
|
+
raise ArgumentError, "given number `#{number}` is larger than ULID limit `#{MAX_INTEGER}` or negative"
|
82
117
|
end
|
83
118
|
|
84
|
-
if
|
85
|
-
raise ArgumentError, "given number is larger than
|
119
|
+
if period && (number > possibilities)
|
120
|
+
raise ArgumentError, "given number `#{number}` is larger than given possibilities `#{possibilities}`"
|
86
121
|
end
|
87
|
-
|
122
|
+
|
123
|
+
Array.new(number) { from_integer(int_generator.call) }
|
124
|
+
else
|
125
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..1)"
|
88
126
|
end
|
89
127
|
end
|
90
128
|
|
@@ -93,77 +131,68 @@ class ULID
|
|
93
131
|
# @yieldparam [ULID] ulid
|
94
132
|
# @yieldreturn [self]
|
95
133
|
def self.scan(string)
|
96
|
-
string = string
|
134
|
+
string = String.try_convert(string)
|
135
|
+
raise ArgumentError, 'ULID.scan takes only strings' unless string
|
97
136
|
return to_enum(__callee__, string) unless block_given?
|
98
|
-
string.scan(
|
99
|
-
yield parse(
|
137
|
+
string.scan(SCANNING_PATTERN) do |matched|
|
138
|
+
yield parse(matched)
|
100
139
|
end
|
101
140
|
self
|
102
141
|
end
|
103
142
|
|
104
|
-
# @param [
|
105
|
-
# @return [ULID]
|
106
|
-
# @raise [ParserError] if the given format is not correct for UUIDv4 specs
|
107
|
-
def self.from_uuidv4(uuid)
|
108
|
-
begin
|
109
|
-
uuid = uuid.to_str
|
110
|
-
prefix_trimmed = uuid.sub(/\Aurn:uuid:/, '')
|
111
|
-
raise "given string is not matched to pattern #{UUIDV4_PATTERN.inspect}" unless UUIDV4_PATTERN.match?(prefix_trimmed)
|
112
|
-
normalized = prefix_trimmed.gsub(/[^0-9A-Fa-f]/, '')
|
113
|
-
from_integer(normalized.to_i(16))
|
114
|
-
rescue => err
|
115
|
-
raise ParserError, "parsing failure as #{err.inspect} for given #{uuid}"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# @param [Integer, #to_int] integer
|
143
|
+
# @param [Integer] integer
|
120
144
|
# @return [ULID]
|
121
145
|
# @raise [OverflowError] if the given integer is larger than the ULID limit
|
122
146
|
# @raise [ArgumentError] if the given integer is negative number
|
123
|
-
# @todo Need optimized for performance
|
124
147
|
def self.from_integer(integer)
|
125
|
-
|
148
|
+
raise ArgumentError, 'ULID.from_integer takes only `Integer`' unless Integer === integer
|
126
149
|
raise OverflowError, "integer overflow: given #{integer}, max: #{MAX_INTEGER}" unless integer <= MAX_INTEGER
|
127
150
|
raise ArgumentError, "integer should not be negative: given: #{integer}" if integer.negative?
|
128
151
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
milliseconds = inverse_of_digits(time_octets)
|
133
|
-
entropy = inverse_of_digits(randomness_octets)
|
152
|
+
n32encoded = integer.to_s(32).rjust(ENCODED_LENGTH, '0')
|
153
|
+
n32encoded_timestamp = n32encoded.slice(0, TIMESTAMP_ENCODED_LENGTH)
|
154
|
+
n32encoded_randomness = n32encoded.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH)
|
134
155
|
|
135
|
-
|
156
|
+
milliseconds = n32encoded_timestamp.to_i(32)
|
157
|
+
entropy = n32encoded_randomness.to_i(32)
|
158
|
+
|
159
|
+
new milliseconds: milliseconds, entropy: entropy, integer: integer
|
136
160
|
end
|
137
161
|
|
138
|
-
# @param [Range<Time>, Range<nil
|
162
|
+
# @param [Range<Time>, Range<nil>, Range[ULID]] period
|
139
163
|
# @return [Range<ULID>]
|
140
|
-
# @raise [ArgumentError] if the given
|
141
|
-
def self.range(
|
142
|
-
raise
|
143
|
-
|
164
|
+
# @raise [ArgumentError] if the given period is not a `Range[Time]`, `Range[nil]` or `Range[ULID]`
|
165
|
+
def self.range(period)
|
166
|
+
raise ArgumentError, 'ULID.range takes only `Range[Time]`, `Range[nil]` or `Range[ULID]`' unless Range === period
|
167
|
+
begin_element, end_element, exclude_end = period.begin, period.end, period.exclude_end?
|
168
|
+
return period if self === begin_element && self === end_element
|
144
169
|
|
145
|
-
case
|
170
|
+
case begin_element
|
146
171
|
when Time
|
147
|
-
begin_ulid = min(
|
172
|
+
begin_ulid = min(begin_element)
|
148
173
|
when nil
|
149
174
|
begin_ulid = MIN
|
175
|
+
when self
|
176
|
+
begin_ulid = begin_element
|
150
177
|
else
|
151
|
-
raise
|
178
|
+
raise ArgumentError, "ULID.range takes only `Range[Time]`, `Range[nil]` or `Range[ULID]`, given: #{period.inspect}"
|
152
179
|
end
|
153
180
|
|
154
|
-
case
|
181
|
+
case end_element
|
155
182
|
when Time
|
156
183
|
if exclude_end
|
157
|
-
end_ulid = min(
|
184
|
+
end_ulid = min(end_element)
|
158
185
|
else
|
159
|
-
end_ulid = max(
|
186
|
+
end_ulid = max(end_element)
|
160
187
|
end
|
161
188
|
when nil
|
162
189
|
# The end should be max and include end, because nil end means to cover endless ULIDs until the limit
|
163
190
|
end_ulid = MAX
|
164
191
|
exclude_end = false
|
192
|
+
when self
|
193
|
+
end_ulid = end_element
|
165
194
|
else
|
166
|
-
raise
|
195
|
+
raise ArgumentError, "ULID.range takes only `Range[Time]`, `Range[nil]` or `Range[ULID]`, given: #{period.inspect}"
|
167
196
|
end
|
168
197
|
|
169
198
|
begin_ulid.freeze
|
@@ -175,6 +204,8 @@ class ULID
|
|
175
204
|
# @param [Time] time
|
176
205
|
# @return [Time]
|
177
206
|
def self.floor(time)
|
207
|
+
raise ArgumentError, 'ULID.floor takes only `Time` instance' unless Time === time
|
208
|
+
|
178
209
|
if RUBY_VERSION >= '2.7'
|
179
210
|
time.floor(3)
|
180
211
|
else
|
@@ -191,7 +222,7 @@ class ULID
|
|
191
222
|
# @api private
|
192
223
|
# @param [Time] time
|
193
224
|
# @return [Integer]
|
194
|
-
def self.milliseconds_from_time(time)
|
225
|
+
private_class_method def self.milliseconds_from_time(time)
|
195
226
|
(time.to_r * 1000).to_i
|
196
227
|
end
|
197
228
|
|
@@ -199,154 +230,121 @@ class ULID
|
|
199
230
|
# @param [Time, Integer] moment
|
200
231
|
# @return [Integer]
|
201
232
|
def self.milliseconds_from_moment(moment)
|
202
|
-
|
233
|
+
case moment
|
234
|
+
when Integer
|
235
|
+
moment
|
236
|
+
when Time
|
237
|
+
milliseconds_from_time(moment)
|
238
|
+
else
|
239
|
+
raise ArgumentError, '`moment` should be a `Time` or `Integer as milliseconds`'
|
240
|
+
end
|
203
241
|
end
|
204
242
|
|
205
|
-
# @api private
|
206
243
|
# @return [Integer]
|
207
|
-
def self.reasonable_entropy
|
244
|
+
private_class_method def self.reasonable_entropy
|
208
245
|
SecureRandom.random_number(MAX_ENTROPY)
|
209
246
|
end
|
210
247
|
|
211
|
-
n32_chars = [*'0'..'9', *'A'..'V'].map(&:freeze).freeze
|
212
|
-
raise SetupError, 'obvious bug exists in the mapping algorithm' unless n32_chars.size == 32
|
213
|
-
|
214
|
-
n32_char_by_number = {}
|
215
|
-
n32_chars.each_with_index do |char, index|
|
216
|
-
n32_char_by_number[index] = char
|
217
|
-
end
|
218
|
-
n32_char_by_number.freeze
|
219
|
-
|
220
|
-
# Currently supporting only for `subset for actual use-case`
|
221
|
-
# See below
|
222
|
-
# * https://github.com/ulid/spec/pull/57
|
223
|
-
# * https://github.com/kachick/ruby-ulid/issues/57
|
224
|
-
# * https://github.com/kachick/ruby-ulid/issues/78
|
225
|
-
crockford_base32_mappings = {
|
226
|
-
'J' => 18,
|
227
|
-
'K' => 19,
|
228
|
-
'M' => 20,
|
229
|
-
'N' => 21,
|
230
|
-
'P' => 22,
|
231
|
-
'Q' => 23,
|
232
|
-
'R' => 24,
|
233
|
-
'S' => 25,
|
234
|
-
'T' => 26,
|
235
|
-
'V' => 27,
|
236
|
-
'W' => 28,
|
237
|
-
'X' => 29,
|
238
|
-
'Y' => 30,
|
239
|
-
'Z' => 31
|
240
|
-
}.freeze
|
241
|
-
|
242
|
-
N32_CHAR_BY_CROCKFORD_BASE32_CHAR = ENCODING_CHARS.each_with_object({}) do |encoding_char, map|
|
243
|
-
if n = crockford_base32_mappings[encoding_char]
|
244
|
-
char_32 = n32_char_by_number.fetch(n)
|
245
|
-
map[encoding_char] = char_32
|
246
|
-
end
|
247
|
-
end.freeze
|
248
|
-
raise SetupError, 'obvious bug exists in the mapping algorithm' unless N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys == crockford_base32_mappings.keys
|
249
|
-
CROCKFORD_BASE32_CHAR_PATTERN = /[#{N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys.join}]/.freeze
|
250
|
-
|
251
|
-
CROCKFORD_BASE32_CHAR_BY_N32_CHAR = N32_CHAR_BY_CROCKFORD_BASE32_CHAR.invert.freeze
|
252
|
-
N32_CHAR_PATTERN = /[#{CROCKFORD_BASE32_CHAR_BY_N32_CHAR.keys.join}]/.freeze
|
253
|
-
|
254
248
|
# @param [String, #to_str] string
|
255
249
|
# @return [ULID]
|
256
250
|
# @raise [ParserError] if the given format is not correct for ULID specs
|
257
251
|
def self.parse(string)
|
258
|
-
|
259
|
-
|
260
|
-
raise "given argument does not match to `#{STRICT_PATTERN.inspect}`" unless STRICT_PATTERN.match?(string)
|
261
|
-
n32encoded = convert_crockford_base32_to_n32(string.upcase)
|
262
|
-
timestamp = n32encoded.slice(0, TIMESTAMP_PART_LENGTH)
|
263
|
-
randomness = n32encoded.slice(TIMESTAMP_PART_LENGTH, RANDOMNESS_PART_LENGTH)
|
264
|
-
milliseconds = timestamp.to_i(32)
|
265
|
-
entropy = randomness.to_i(32)
|
266
|
-
rescue => err
|
267
|
-
raise ParserError, "parsing failure as #{err.inspect} for given #{string.inspect}"
|
268
|
-
end
|
252
|
+
string = String.try_convert(string)
|
253
|
+
raise ArgumentError, 'ULID.parse takes only strings' unless string
|
269
254
|
|
270
|
-
|
271
|
-
|
255
|
+
unless STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET.match?(string)
|
256
|
+
raise ParserError, "given `#{string}` does not match to `#{STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET.inspect}`"
|
257
|
+
end
|
272
258
|
|
273
|
-
|
274
|
-
private_class_method def self.convert_crockford_base32_to_n32(string)
|
275
|
-
string.gsub(CROCKFORD_BASE32_CHAR_PATTERN, N32_CHAR_BY_CROCKFORD_BASE32_CHAR)
|
259
|
+
from_integer(CrockfordBase32.decode(string))
|
276
260
|
end
|
277
261
|
|
278
262
|
# @return [Boolean]
|
279
|
-
def self.valid?(
|
280
|
-
|
281
|
-
|
282
|
-
false
|
283
|
-
else
|
284
|
-
true
|
263
|
+
def self.valid?(object)
|
264
|
+
string = String.try_convert(object)
|
265
|
+
string ? STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET.match?(string) : false
|
285
266
|
end
|
286
267
|
|
287
|
-
# @
|
288
|
-
# @
|
289
|
-
# @
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
268
|
+
# @param [ULID, #to_ulid] object
|
269
|
+
# @return [ULID, nil]
|
270
|
+
# @raise [TypeError] if `object.to_ulid` did not return ULID instance
|
271
|
+
def self.try_convert(object)
|
272
|
+
begin
|
273
|
+
converted = object.to_ulid
|
274
|
+
rescue NoMethodError
|
275
|
+
nil
|
276
|
+
else
|
277
|
+
if ULID === converted
|
278
|
+
converted
|
279
|
+
else
|
280
|
+
object_class_name = safe_get_class_name(object)
|
281
|
+
converted_class_name = safe_get_class_name(converted)
|
282
|
+
raise TypeError, "can't convert #{object_class_name} to ULID (#{object_class_name}#to_ulid gives #{converted_class_name})"
|
283
|
+
end
|
295
284
|
end
|
296
|
-
digits.reverse!
|
297
285
|
end
|
298
286
|
|
299
|
-
# @
|
300
|
-
# @
|
301
|
-
|
302
|
-
|
303
|
-
def self.inverse_of_digits(reversed_digits)
|
304
|
-
base = 256
|
305
|
-
num = 0
|
306
|
-
reversed_digits.each do |digit|
|
307
|
-
num = (num * base) + digit
|
308
|
-
end
|
309
|
-
num
|
310
|
-
end
|
287
|
+
# @param [BasicObject] object
|
288
|
+
# @return [String]
|
289
|
+
private_class_method def self.safe_get_class_name(object)
|
290
|
+
fallback = 'UnknownObject'
|
311
291
|
|
312
|
-
|
313
|
-
|
314
|
-
|
292
|
+
begin
|
293
|
+
name = String.try_convert(object.class.name)
|
294
|
+
rescue Exception
|
295
|
+
fallback
|
296
|
+
else
|
297
|
+
name || fallback
|
298
|
+
end
|
315
299
|
end
|
316
300
|
|
317
|
-
attr_reader :milliseconds, :entropy
|
318
|
-
|
319
301
|
# @api private
|
320
302
|
# @param [Integer] milliseconds
|
321
303
|
# @param [Integer] entropy
|
322
|
-
# @return [
|
304
|
+
# @return [ULID]
|
323
305
|
# @raise [OverflowError] if the given value is larger than the ULID limit
|
324
306
|
# @raise [ArgumentError] if the given milliseconds and/or entropy is negative number
|
325
|
-
def
|
326
|
-
milliseconds
|
327
|
-
entropy = entropy.to_int
|
307
|
+
def self.from_milliseconds_and_entropy(milliseconds:, entropy:)
|
308
|
+
raise ArgumentError, 'milliseconds and entropy should be an `Integer`' unless Integer === milliseconds && Integer === entropy
|
328
309
|
raise OverflowError, "timestamp overflow: given #{milliseconds}, max: #{MAX_MILLISECONDS}" unless milliseconds <= MAX_MILLISECONDS
|
329
310
|
raise OverflowError, "entropy overflow: given #{entropy}, max: #{MAX_ENTROPY}" unless entropy <= MAX_ENTROPY
|
330
311
|
raise ArgumentError, 'milliseconds and entropy should not be negative' if milliseconds.negative? || entropy.negative?
|
331
312
|
|
313
|
+
n32encoded_timestamp = milliseconds.to_s(32).rjust(TIMESTAMP_ENCODED_LENGTH, '0')
|
314
|
+
n32encoded_randomness = entropy.to_s(32).rjust(RANDOMNESS_ENCODED_LENGTH, '0')
|
315
|
+
integer = (n32encoded_timestamp + n32encoded_randomness).to_i(32)
|
316
|
+
|
317
|
+
new milliseconds: milliseconds, entropy: entropy, integer: integer
|
318
|
+
end
|
319
|
+
|
320
|
+
attr_reader :milliseconds, :entropy
|
321
|
+
|
322
|
+
# @api private
|
323
|
+
# @param [Integer] milliseconds
|
324
|
+
# @param [Integer] entropy
|
325
|
+
# @param [Integer] integer
|
326
|
+
# @return [void]
|
327
|
+
def initialize(milliseconds:, entropy:, integer:)
|
328
|
+
# All arguments check should be done with each constructors, not here
|
329
|
+
@integer = integer
|
332
330
|
@milliseconds = milliseconds
|
333
331
|
@entropy = entropy
|
334
332
|
end
|
335
333
|
|
336
334
|
# @return [String]
|
337
335
|
def to_s
|
338
|
-
@string ||=
|
336
|
+
@string ||= CrockfordBase32.encode(@integer).freeze
|
339
337
|
end
|
340
338
|
|
341
339
|
# @return [Integer]
|
342
340
|
def to_i
|
343
|
-
@integer
|
341
|
+
@integer
|
344
342
|
end
|
345
343
|
alias_method :hash, :to_i
|
346
344
|
|
347
345
|
# @return [Integer, nil]
|
348
346
|
def <=>(other)
|
349
|
-
|
347
|
+
(ULID === other) ? (@integer <=> other.to_i) : nil
|
350
348
|
end
|
351
349
|
|
352
350
|
# @return [String]
|
@@ -356,7 +354,7 @@ class ULID
|
|
356
354
|
|
357
355
|
# @return [Boolean]
|
358
356
|
def eql?(other)
|
359
|
-
|
357
|
+
equal?(other) || (ULID === other && @integer == other.to_i)
|
360
358
|
end
|
361
359
|
alias_method :==, :eql?
|
362
360
|
|
@@ -364,13 +362,9 @@ class ULID
|
|
364
362
|
def ===(other)
|
365
363
|
case other
|
366
364
|
when ULID
|
367
|
-
|
365
|
+
@integer == other.to_i
|
368
366
|
when String
|
369
|
-
|
370
|
-
self == self.class.parse(other)
|
371
|
-
rescue Exception
|
372
|
-
false
|
373
|
-
end
|
367
|
+
to_s == other.upcase
|
374
368
|
else
|
375
369
|
false
|
376
370
|
end
|
@@ -389,64 +383,69 @@ class ULID
|
|
389
383
|
|
390
384
|
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer)]
|
391
385
|
def octets
|
392
|
-
|
386
|
+
digits = @integer.digits(256)
|
387
|
+
(OCTETS_LENGTH - digits.size).times do
|
388
|
+
digits.push 0
|
389
|
+
end
|
390
|
+
digits.reverse!
|
393
391
|
end
|
394
392
|
|
395
393
|
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer)]
|
396
394
|
def timestamp_octets
|
397
|
-
|
395
|
+
octets.slice(0, TIMESTAMP_OCTETS_LENGTH)
|
398
396
|
end
|
399
397
|
|
400
398
|
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer)]
|
401
399
|
def randomness_octets
|
402
|
-
|
400
|
+
octets.slice(TIMESTAMP_OCTETS_LENGTH, RANDOMNESS_OCTETS_LENGTH)
|
403
401
|
end
|
404
402
|
|
405
403
|
# @return [String]
|
406
404
|
def timestamp
|
407
|
-
@timestamp ||=
|
405
|
+
@timestamp ||= to_s.slice(0, TIMESTAMP_ENCODED_LENGTH).freeze
|
408
406
|
end
|
409
407
|
|
410
408
|
# @return [String]
|
411
409
|
def randomness
|
412
|
-
@randomness ||=
|
410
|
+
@randomness ||= to_s.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH).freeze
|
413
411
|
end
|
414
412
|
|
415
|
-
# @
|
416
|
-
# @return [Regexp]
|
417
|
-
def
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
def strict_pattern
|
424
|
-
@strict_pattern ||= /\A#{pattern.source}\z/i.freeze
|
413
|
+
# @note Providing for rough operations. The keys and values is not fixed.
|
414
|
+
# @return [Hash{Symbol => Regexp, String}]
|
415
|
+
def patterns
|
416
|
+
named_captures = /(?<timestamp>#{timestamp})(?<randomness>#{randomness})/i.freeze
|
417
|
+
{
|
418
|
+
named_captures: named_captures,
|
419
|
+
strict_named_captures: /\A#{named_captures.source}\z/i.freeze
|
420
|
+
}
|
425
421
|
end
|
426
422
|
|
427
423
|
# @return [ULID, nil] when called on ULID as `7ZZZZZZZZZZZZZZZZZZZZZZZZZ`, returns `nil` instead of ULID
|
428
|
-
def
|
429
|
-
|
430
|
-
|
431
|
-
|
424
|
+
def succ
|
425
|
+
succ_int = @integer.succ
|
426
|
+
if succ_int >= MAX_INTEGER
|
427
|
+
if succ_int == MAX_INTEGER
|
428
|
+
MAX
|
429
|
+
else
|
430
|
+
nil
|
431
|
+
end
|
432
|
+
else
|
433
|
+
ULID.from_integer(succ_int)
|
434
|
+
end
|
432
435
|
end
|
433
|
-
alias_method :
|
436
|
+
alias_method :next, :succ
|
434
437
|
|
435
438
|
# @return [ULID, nil] when called on ULID as `00000000000000000000000000`, returns `nil` instead of ULID
|
436
439
|
def pred
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
array = octets.pack('C*').unpack('NnnnnN')
|
447
|
-
array[2] = (array[2] & 0x0fff) | 0x4000
|
448
|
-
array[3] = (array[3] & 0x3fff) | 0x8000
|
449
|
-
('%08x-%04x-%04x-%04x-%04x%08x' % array).freeze
|
440
|
+
pred_int = @integer.pred
|
441
|
+
if pred_int <= 0
|
442
|
+
if pred_int == 0
|
443
|
+
MIN
|
444
|
+
else
|
445
|
+
nil
|
446
|
+
end
|
447
|
+
else
|
448
|
+
ULID.from_integer(pred_int)
|
450
449
|
end
|
451
450
|
end
|
452
451
|
|
@@ -457,36 +456,40 @@ class ULID
|
|
457
456
|
super
|
458
457
|
end
|
459
458
|
|
460
|
-
|
459
|
+
# @return [self]
|
460
|
+
def to_ulid
|
461
|
+
self
|
462
|
+
end
|
461
463
|
|
462
|
-
# @
|
463
|
-
def
|
464
|
-
|
464
|
+
# @return [self]
|
465
|
+
def dup
|
466
|
+
self
|
465
467
|
end
|
466
468
|
|
467
|
-
# @return [
|
468
|
-
def
|
469
|
-
|
469
|
+
# @return [self]
|
470
|
+
def clone(freeze: true)
|
471
|
+
self
|
470
472
|
end
|
471
473
|
|
474
|
+
undef_method :instance_variable_set
|
475
|
+
|
476
|
+
private
|
477
|
+
|
472
478
|
# @return [void]
|
473
479
|
def cache_all_instance_variables
|
474
480
|
inspect
|
475
|
-
|
476
|
-
|
477
|
-
succ
|
478
|
-
pred
|
479
|
-
strict_pattern
|
480
|
-
to_uuidv4
|
481
|
+
timestamp
|
482
|
+
randomness
|
481
483
|
end
|
482
484
|
end
|
483
485
|
|
484
486
|
require_relative 'ulid/version'
|
487
|
+
require_relative 'ulid/crockford_base32'
|
485
488
|
require_relative 'ulid/monotonic_generator'
|
486
489
|
|
487
490
|
class ULID
|
488
491
|
MIN = parse('00000000000000000000000000').freeze
|
489
492
|
MAX = parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').freeze
|
490
493
|
|
491
|
-
private_constant :
|
494
|
+
private_constant :TIME_FORMAT_IN_INSPECT, :MIN, :MAX, :RANDOM_INTEGER_GENERATOR
|
492
495
|
end
|