ruby-ulid 0.2.0 → 0.4.0
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 +71 -47
- data/lib/ruby-ulid.rb +2 -1
- data/lib/ulid/crockford_base32.rb +4 -3
- data/lib/ulid/monotonic_generator.rb +22 -15
- data/lib/ulid/ractor_unshareable_constants.rb +12 -0
- data/lib/ulid/uuid.rb +9 -5
- data/lib/ulid/version.rb +2 -1
- data/lib/ulid.rb +117 -83
- data/sig/ulid.rbs +34 -23
- metadata +6 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b50cb3fee0b304a62ebd460e7d08b932d3c58c1f7810a1dc24b5166df10ceaf
|
4
|
+
data.tar.gz: b84a2ad766640fa7c87e4bbb5b0fdd637bf831407f235989cdd043ea5b1189aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e44eadac5ad4c83f1c8da74fbfa502a3b3b7293dda8eb44855bb477070687afd815d46237a9cc15266223f7cd0981b69670165767b366156d23728cdb76c878e
|
7
|
+
data.tar.gz: 349b2e8c2b16d5c9ae58270fdb5e87a3c364eef4b3ae2972680cb5de119ba737ed035db36bb7283fc96ff7c7a54663be5de8c0725e0de1975d8f66de34dd052c
|
data/README.md
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
# ruby-ulid
|
2
2
|
|
3
|
+
[](https://github.com/kachick/ruby-ulid/actions/workflows/test_behaviors.yml/?branch=main)
|
4
|
+
[](http://badge.fury.io/rb/ruby-ulid)
|
5
|
+
|
3
6
|
## Overview
|
4
7
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
+
[ulid/spec](https://github.com/ulid/spec) is useful.
|
9
|
+
Especially possess all `uniqueness`, `randomness`, `extractable timestamps` and `sortable` features.
|
10
|
+
This gem aims to provide the generator, monotonic generator, parser and handy manipulation features around ULID.
|
11
|
+
Also providing [RBS](https://github.com/ruby/rbs) signatures.
|
8
12
|
|
9
13
|
---
|
10
14
|
|
11
|
-
](https://github.com/kachick/ruby-ulid/actions/workflows/test_behaviors.yml/?branch=main)
|
14
|
-
[](http://badge.fury.io/rb/ruby-ulid)
|
15
|
-
[](https://github.dev/kachick/ruby-ulid)
|
15
|
+

|
16
16
|
|
17
17
|
## Universally Unique Lexicographically Sortable Identifier
|
18
18
|
|
@@ -47,21 +47,43 @@ $ gem install ruby-ulid
|
|
47
47
|
Should be installed!
|
48
48
|
```
|
49
49
|
|
50
|
-
Add this line
|
50
|
+
Add this line in your Gemfile.
|
51
51
|
|
52
52
|
```ruby
|
53
|
-
gem
|
53
|
+
gem('ruby-ulid', '~> 0.4.0')
|
54
54
|
```
|
55
55
|
|
56
|
-
###
|
57
|
-
|
58
|
-
The generated `ULID` is an object not just a string.
|
59
|
-
It means easily get the timestamps and binary formats.
|
56
|
+
### How to use
|
60
57
|
|
61
58
|
```ruby
|
62
59
|
require 'ulid'
|
63
60
|
|
61
|
+
defined? ULID
|
62
|
+
# => "constant"
|
63
|
+
```
|
64
|
+
|
65
|
+
### Basic Generator
|
66
|
+
|
67
|
+
The generated `ULID` is an object not just a string.
|
68
|
+
|
69
|
+
```ruby
|
64
70
|
ulid = ULID.generate #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
71
|
+
```
|
72
|
+
|
73
|
+
### Parser
|
74
|
+
|
75
|
+
You can get the objects from exists encoded ULIDs.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
ulid = ULID.parse('01ARZ3NDEKTSV4RRFFQ69G5FAV') #=> ULID(2016-07-30 23:54:10.259 UTC: 01ARZ3NDEKTSV4RRFFQ69G5FAV)
|
79
|
+
```
|
80
|
+
|
81
|
+
### ULID object
|
82
|
+
|
83
|
+
You can extract timestamps and binary formats.
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
ulid = ULID.parse('01F4A5Y1YAQCYAYCTC7GRMJ9AA') #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
65
87
|
ulid.to_time #=> 2021-04-27 17:27:22.826 UTC
|
66
88
|
ulid.milliseconds #=> 1619544442826
|
67
89
|
ulid.to_s #=> "01F4A5Y1YAQCYAYCTC7GRMJ9AA"
|
@@ -71,16 +93,9 @@ ulid.to_i #=> 1957909092946624190749577070267409738
|
|
71
93
|
ulid.octets #=> [1, 121, 20, 95, 7, 202, 187, 60, 175, 51, 76, 60, 49, 73, 37, 74]
|
72
94
|
```
|
73
95
|
|
74
|
-
You can get the objects from exists encoded ULIDs
|
75
|
-
|
76
|
-
```ruby
|
77
|
-
ulid = ULID.parse('01ARZ3NDEKTSV4RRFFQ69G5FAV') #=> ULID(2016-07-30 23:54:10.259 UTC: 01ARZ3NDEKTSV4RRFFQ69G5FAV)
|
78
|
-
ulid.to_time #=> 2016-07-30 23:54:10.259 UTC
|
79
|
-
```
|
80
|
-
|
81
96
|
### Sortable with the timestamp
|
82
97
|
|
83
|
-
ULIDs are sortable when they are generated in different timestamp with milliseconds precision
|
98
|
+
ULIDs are sortable when they are generated in different timestamp with milliseconds precision.
|
84
99
|
|
85
100
|
```ruby
|
86
101
|
ulids = 1000.times.map do
|
@@ -91,7 +106,7 @@ ulids.uniq(&:to_time).size #=> 1000
|
|
91
106
|
ulids.sort == ulids #=> true
|
92
107
|
```
|
93
108
|
|
94
|
-
`ULID.generate` can take fixed `Time` instance. The shorthand is `ULID.at
|
109
|
+
`ULID.generate` can take fixed `Time` instance. The shorthand is `ULID.at`.
|
95
110
|
|
96
111
|
```ruby
|
97
112
|
time = Time.at(946684800).utc #=> 2000-01-01 00:00:00 UTC
|
@@ -147,11 +162,11 @@ sample_ulids_by_the_time.take(5) #=>
|
|
147
162
|
ulids.sort == ulids #=> true
|
148
163
|
```
|
149
164
|
|
150
|
-
Same generator does not generate duplicated ULIDs even in multi threads environment. It is implemented with [Monitor](https://bugs.ruby-lang.org/issues/16255)
|
165
|
+
Same generator does not generate duplicated ULIDs even in multi threads environment. It is implemented with [Monitor](https://bugs.ruby-lang.org/issues/16255).
|
151
166
|
|
152
167
|
### Filtering IDs with `Time`
|
153
168
|
|
154
|
-
`ULID` can be element of the `Range`. If
|
169
|
+
`ULID` can be element of the `Range`. If they were generated with monotonic generator, ID based filtering is easy and reliable.
|
155
170
|
|
156
171
|
```ruby
|
157
172
|
include_end = ulid1..ulid2
|
@@ -187,7 +202,9 @@ time = Time.at(946684800, Rational('123456.789')).utc #=> 2000-01-01 00:00:00.12
|
|
187
202
|
ULID.floor(time) #=> 2000-01-01 00:00:00.123 UTC
|
188
203
|
```
|
189
204
|
|
190
|
-
###
|
205
|
+
### Some methods to help manipulations
|
206
|
+
|
207
|
+
#### Scanner for string (e.g. `JSON`)
|
191
208
|
|
192
209
|
For rough operations, `ULID.scan` might be useful.
|
193
210
|
|
@@ -230,7 +247,7 @@ ULID.scan(json).to_a
|
|
230
247
|
```
|
231
248
|
|
232
249
|
`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
|
250
|
+
The results and spec are not fixed. Should not be used except snippets/console operation.
|
234
251
|
|
235
252
|
```ruby
|
236
253
|
ULID.parse('01F4GNBXW1AM2KWW52PVT3ZY9X').patterns
|
@@ -241,7 +258,7 @@ ULID.parse('01F4GNBXW1AM2KWW52PVT3ZY9X').patterns
|
|
241
258
|
}
|
242
259
|
```
|
243
260
|
|
244
|
-
|
261
|
+
#### Get boundary ULIDs
|
245
262
|
|
246
263
|
`ULID.min` and `ULID.max` return termination values for ULID spec.
|
247
264
|
|
@@ -256,10 +273,12 @@ ULID.min(time) #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3V0000000000000000)
|
|
256
273
|
ULID.max(time) #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3VZZZZZZZZZZZZZZZZ)
|
257
274
|
```
|
258
275
|
|
276
|
+
#### As element in Enumerable
|
277
|
+
|
259
278
|
`ULID#next` and `ULID#succ` returns next(successor) ULID.
|
260
279
|
Especially `ULID#succ` makes it possible `Range[ULID]#each`.
|
261
280
|
|
262
|
-
NOTE:
|
281
|
+
NOTE: However basically `Range[ULID]#each` should not be used. Iincrementing 128 bits IDs are not reasonable operation in most cases.
|
263
282
|
|
264
283
|
```ruby
|
265
284
|
ULID.parse('01BX5ZZKBKZZZZZZZZZZZZZZZY').next.to_s #=> "01BX5ZZKBKZZZZZZZZZZZZZZZZ"
|
@@ -267,7 +286,7 @@ ULID.parse('01BX5ZZKBKZZZZZZZZZZZZZZZZ').next.to_s #=> "01BX5ZZKBM00000000000000
|
|
267
286
|
ULID.parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').next #=> nil
|
268
287
|
```
|
269
288
|
|
270
|
-
`ULID#pred` returns predecessor ULID
|
289
|
+
`ULID#pred` returns predecessor ULID.
|
271
290
|
|
272
291
|
```ruby
|
273
292
|
ULID.parse('01BX5ZZKBK0000000000000001').pred.to_s #=> "01BX5ZZKBK0000000000000000"
|
@@ -275,6 +294,8 @@ ULID.parse('01BX5ZZKBK0000000000000000').pred.to_s #=> "01BX5ZZKBJZZZZZZZZZZZZZZ
|
|
275
294
|
ULID.parse('00000000000000000000000000').pred #=> nil
|
276
295
|
```
|
277
296
|
|
297
|
+
#### Test helpers
|
298
|
+
|
278
299
|
`ULID.sample` returns random ULIDs.
|
279
300
|
|
280
301
|
Basically ignores generating time.
|
@@ -300,33 +321,23 @@ ulid1 = ULID.parse('01F4A5Y1YAQCYAYCTC7GRMJ9AA') #=> ULID(2021-04-27 17:27:22.82
|
|
300
321
|
ulid2 = ULID.parse('01F4PTVCSN9ZPFKYTY2DDJVRK4') #=> ULID(2021-05-02 15:23:48.917 UTC: 01F4PTVCSN9ZPFKYTY2DDJVRK4)
|
301
322
|
ulids = ULID.sample(1000, period: ulid1..ulid2)
|
302
323
|
ulids.uniq.size #=> 1000
|
303
|
-
ulids.take(
|
324
|
+
ulids.take(5)
|
304
325
|
#=>
|
305
326
|
#[ULID(2021-05-02 06:57:19.954 UTC: 01F4NXW02JNB8H0J0TK48JD39X),
|
306
327
|
# ULID(2021-05-02 07:06:07.458 UTC: 01F4NYC372GVP7NS0YAYQGT4VZ),
|
307
328
|
# ULID(2021-05-01 06:16:35.791 UTC: 01F4K94P6F6P68K0H64WRDSFKW),
|
308
329
|
# ULID(2021-04-27 22:17:37.844 UTC: 01F4APHGSMFJZQTGXKZBFFBPJP),
|
309
|
-
# ULID(2021-04-28 20:17:55.357 UTC: 01F4D231MXQJXAR8G2JZHEJNH3)
|
310
|
-
|
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)
|
330
|
+
# ULID(2021-04-28 20:17:55.357 UTC: 01F4D231MXQJXAR8G2JZHEJNH3)]
|
331
|
+
ULID.sample(5, period: ulid1.to_time..ulid2.to_time)
|
316
332
|
#=>
|
317
333
|
# [ULID(2021-04-29 06:44:41.513 UTC: 01F4E5YPD9XQ3MYXWK8ZJKY8SW),
|
318
334
|
# ULID(2021-05-01 00:35:06.629 UTC: 01F4JNKD85SVK1EAEYSJGF53A2),
|
319
335
|
# ULID(2021-05-02 12:45:28.408 UTC: 01F4PHSEYRG9BWBEWMRW1XE6WW),
|
320
336
|
# 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)]
|
337
|
+
# ULID(2021-04-29 21:38:58.109 UTC: 01F4FS45DX4049JEQK4W6TER6G)]
|
327
338
|
```
|
328
339
|
|
329
|
-
|
340
|
+
#### Variants of format
|
330
341
|
|
331
342
|
I'm afraid so, we should consider [Current ULID spec](https://github.com/ulid/spec/tree/d0c7170df4517939e70129b4d6462cc162f2d5bf#universally-unique-lexicographically-sortable-identifier) has `orthographical variants of the format` possibilities.
|
332
343
|
|
@@ -354,7 +365,7 @@ ULID.normalized?('-olarz3-noekisv4rrff-q6ig5fav--') #=> false
|
|
354
365
|
ULID.normalized?('01ARZ3N0EK1SV4RRFFQ61G5FAV') #=> true
|
355
366
|
```
|
356
367
|
|
357
|
-
|
368
|
+
#### UUIDv4 converter for migration use-cases
|
358
369
|
|
359
370
|
`ULID.from_uuidv4` and `ULID#to_uuidv4` is the converter.
|
360
371
|
The imported timestamp is meaningless. So ULID's benefit will lost.
|
@@ -438,6 +449,18 @@ See [Benchmark](https://github.com/kachick/ruby-ulid/wiki/Benchmark).
|
|
438
449
|
|
439
450
|
The results are not something to be proud of.
|
440
451
|
|
452
|
+
## How to use rbs
|
453
|
+
|
454
|
+
See structure at [examples/rbs_sandbox](https://github.com/kachick/ruby-ulid/tree/main/examples/rbs_sandbox)
|
455
|
+
|
456
|
+
I have checked the behavior with [ruby/rbs@2.6.0](https://github.com/ruby/rbs) + [soutaro/steep@1.0.1](https://github.com/soutaro/steep) + [soutaro/steep-vscode](https://github.com/soutaro/steep-vscode).
|
457
|
+
|
458
|
+
* 
|
459
|
+
* 
|
460
|
+
* 
|
461
|
+
* 
|
462
|
+
* 
|
463
|
+
|
441
464
|
## References
|
442
465
|
|
443
466
|
- [Repository](https://github.com/kachick/ruby-ulid)
|
@@ -446,4 +469,5 @@ The results are not something to be proud of.
|
|
446
469
|
|
447
470
|
## Note
|
448
471
|
|
449
|
-
-
|
472
|
+
- [UUIDv6, UUIDv7, UUIDv8](https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-01.html) is another choice for sortable and randomness ID.
|
473
|
+
However they are stayed in draft state. ref: [ruby-ulid#37](https://github.com/kachick/ruby-ulid/issues/37)
|
data/lib/ruby-ulid.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# coding: us-ascii
|
2
2
|
# frozen_string_literal: true
|
3
|
+
# shareable_constant_value: literal
|
3
4
|
|
4
5
|
# Copyright (C) 2021 Kenichi Kamiya
|
5
6
|
|
@@ -14,10 +15,10 @@ class ULID
|
|
14
15
|
# * https://github.com/kachick/ruby-ulid/issues/57
|
15
16
|
# * https://github.com/kachick/ruby-ulid/issues/78
|
16
17
|
module CrockfordBase32
|
17
|
-
class SetupError <
|
18
|
+
class SetupError < UnexpectedError; end
|
18
19
|
|
19
20
|
n32_chars = [*'0'..'9', *'A'..'V'].map(&:freeze).freeze
|
20
|
-
raise
|
21
|
+
raise(SetupError, 'obvious bug exists in the mapping algorithm') unless n32_chars.size == 32
|
21
22
|
|
22
23
|
n32_char_by_number = {}
|
23
24
|
n32_chars.each_with_index do |char, index|
|
@@ -48,7 +49,7 @@ class ULID
|
|
48
49
|
map[encoding_char] = char_32
|
49
50
|
end
|
50
51
|
end.freeze
|
51
|
-
raise
|
52
|
+
raise(SetupError, 'obvious bug exists in the mapping algorithm') unless N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys == crockford_base32_mappings.keys
|
52
53
|
|
53
54
|
CROCKFORD_BASE32_CHAR_PATTERN = /[#{N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys.join}]/.freeze
|
54
55
|
|
@@ -1,19 +1,23 @@
|
|
1
1
|
# coding: us-ascii
|
2
2
|
# frozen_string_literal: true
|
3
|
+
# shareable_constant_value: literal
|
3
4
|
|
4
5
|
# Copyright (C) 2021 Kenichi Kamiya
|
5
6
|
|
6
7
|
class ULID
|
7
8
|
class MonotonicGenerator
|
8
|
-
|
9
|
+
# @note When use https://github.com/ko1/ractor-tvar might realize Ractor based thread safe monotonic generator.
|
10
|
+
# However it is a C extention, I'm pending to use it for now.
|
11
|
+
include(MonitorMixin)
|
9
12
|
|
13
|
+
# @dynamic prev
|
10
14
|
# @return [ULID, nil]
|
11
|
-
attr_reader
|
15
|
+
attr_reader(:prev)
|
12
16
|
|
13
|
-
undef_method
|
17
|
+
undef_method(:instance_variable_set)
|
14
18
|
|
15
19
|
def initialize
|
16
|
-
super
|
20
|
+
super
|
17
21
|
@prev = nil
|
18
22
|
end
|
19
23
|
|
@@ -21,7 +25,8 @@ class ULID
|
|
21
25
|
def inspect
|
22
26
|
"ULID::MonotonicGenerator(prev: #{@prev.inspect})"
|
23
27
|
end
|
24
|
-
|
28
|
+
# @dynamic to_s
|
29
|
+
alias_method(:to_s, :inspect)
|
25
30
|
|
26
31
|
# @param [Time, Integer] moment
|
27
32
|
# @return [ULID]
|
@@ -30,23 +35,25 @@ class ULID
|
|
30
35
|
# Basically will not happen. Just means this feature prefers error rather than invalid value.
|
31
36
|
def generate(moment: ULID.current_milliseconds)
|
32
37
|
synchronize do
|
33
|
-
|
34
|
-
|
35
|
-
|
38
|
+
prev_ulid = @prev
|
39
|
+
unless prev_ulid
|
40
|
+
ret = ULID.generate(moment: moment)
|
41
|
+
@prev = ret
|
42
|
+
return ret
|
36
43
|
end
|
37
44
|
|
38
45
|
milliseconds = ULID.milliseconds_from_moment(moment)
|
39
46
|
|
40
47
|
ulid = (
|
41
|
-
if
|
48
|
+
if prev_ulid.milliseconds < milliseconds
|
42
49
|
ULID.generate(moment: milliseconds)
|
43
50
|
else
|
44
|
-
ULID.from_milliseconds_and_entropy(milliseconds:
|
51
|
+
ULID.from_milliseconds_and_entropy(milliseconds: prev_ulid.milliseconds, entropy: prev_ulid.entropy.succ)
|
45
52
|
end
|
46
53
|
)
|
47
54
|
|
48
|
-
unless ulid >
|
49
|
-
base_message = "monotonicity broken from unexpected reasons # generated: #{ulid.inspect}, prev: #{
|
55
|
+
unless ulid > prev_ulid
|
56
|
+
base_message = "monotonicity broken from unexpected reasons # generated: #{ulid.inspect}, prev: #{prev_ulid.inspect}"
|
50
57
|
additional_information = (
|
51
58
|
if Thread.list == [Thread.main]
|
52
59
|
'# NOTE: looks single thread only exist'
|
@@ -55,7 +62,7 @@ class ULID
|
|
55
62
|
end
|
56
63
|
)
|
57
64
|
|
58
|
-
raise
|
65
|
+
raise(UnexpectedError, base_message + additional_information)
|
59
66
|
end
|
60
67
|
|
61
68
|
@prev = ulid
|
@@ -63,12 +70,12 @@ class ULID
|
|
63
70
|
end
|
64
71
|
end
|
65
72
|
|
66
|
-
undef_method
|
73
|
+
undef_method(:freeze)
|
67
74
|
|
68
75
|
# @raise [TypeError] always raises exception and does not freeze self
|
69
76
|
# @return [void]
|
70
77
|
def freeze
|
71
|
-
raise
|
78
|
+
raise(TypeError, "cannot freeze #{self.class}")
|
72
79
|
end
|
73
80
|
end
|
74
81
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# coding: us-ascii
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
class ULID
|
5
|
+
min = parse('00000000000000000000000000').freeze
|
6
|
+
max = parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').freeze
|
7
|
+
|
8
|
+
ractor_can_make_shareable_time = RUBY_VERSION >= '3.1'
|
9
|
+
|
10
|
+
MIN = ractor_can_make_shareable_time ? Ractor.make_shareable(min) : min
|
11
|
+
MAX = ractor_can_make_shareable_time ? Ractor.make_shareable(max) : max
|
12
|
+
end
|
data/lib/ulid/uuid.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# coding: us-ascii
|
2
2
|
# frozen_string_literal: true
|
3
|
+
# shareable_constant_value: literal
|
3
4
|
|
4
5
|
# Copyright (C) 2021 Kenichi Kamiya
|
5
6
|
|
@@ -10,18 +11,18 @@
|
|
10
11
|
class ULID
|
11
12
|
# Imported from https://stackoverflow.com/a/38191104/1212807, thank you!
|
12
13
|
UUIDV4_PATTERN = /\A[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i.freeze
|
13
|
-
private_constant
|
14
|
+
private_constant(:UUIDV4_PATTERN)
|
14
15
|
|
15
16
|
# @param [String, #to_str] uuid
|
16
17
|
# @return [ULID]
|
17
18
|
# @raise [ParserError] if the given format is not correct for UUIDv4 specs
|
18
19
|
def self.from_uuidv4(uuid)
|
19
20
|
uuid = String.try_convert(uuid)
|
20
|
-
raise
|
21
|
+
raise(ArgumentError, 'ULID.from_uuidv4 takes only strings') unless uuid
|
21
22
|
|
22
23
|
prefix_trimmed = uuid.delete_prefix('urn:uuid:')
|
23
24
|
unless UUIDV4_PATTERN.match?(prefix_trimmed)
|
24
|
-
raise
|
25
|
+
raise(ParserError, "given `#{uuid}` does not match to `#{UUIDV4_PATTERN.inspect}`")
|
25
26
|
end
|
26
27
|
|
27
28
|
normalized = prefix_trimmed.gsub(/[^0-9A-Fa-f]/, '')
|
@@ -32,8 +33,11 @@ class ULID
|
|
32
33
|
def to_uuidv4
|
33
34
|
# This code referenced https://github.com/ruby/ruby/blob/121fa24a3451b45c41ac0a661b64e9fc8600e589/lib/securerandom.rb#L221-L241
|
34
35
|
array = octets.pack('C*').unpack('NnnnnN')
|
35
|
-
array[2]
|
36
|
-
|
36
|
+
ref2, ref3 = array[2], array[3]
|
37
|
+
raise unless Integer === ref2 && Integer === ref3
|
38
|
+
|
39
|
+
array[2] = (ref2 & 0x0fff) | 0x4000
|
40
|
+
array[3] = (ref3 & 0x3fff) | 0x8000
|
37
41
|
('%08x-%04x-%04x-%04x-%04x%08x' % array).freeze
|
38
42
|
end
|
39
43
|
end
|
data/lib/ulid/version.rb
CHANGED
data/lib/ulid.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# coding: us-ascii
|
2
2
|
# frozen_string_literal: true
|
3
|
+
# shareable_constant_value: experimental_everything
|
3
4
|
|
4
5
|
# Copyright (C) 2021 Kenichi Kamiya
|
5
6
|
|
6
|
-
require
|
7
|
+
require('securerandom')
|
7
8
|
|
8
9
|
# @see https://github.com/ulid/spec
|
9
10
|
# @!attribute [r] milliseconds
|
@@ -11,7 +12,7 @@ require 'securerandom'
|
|
11
12
|
# @!attribute [r] entropy
|
12
13
|
# @return [Integer]
|
13
14
|
class ULID
|
14
|
-
include
|
15
|
+
include(Comparable)
|
15
16
|
|
16
17
|
class Error < StandardError; end
|
17
18
|
class OverflowError < Error; end
|
@@ -25,10 +26,12 @@ class ULID
|
|
25
26
|
|
26
27
|
TIMESTAMP_ENCODED_LENGTH = 10
|
27
28
|
RANDOMNESS_ENCODED_LENGTH = 16
|
28
|
-
ENCODED_LENGTH =
|
29
|
+
ENCODED_LENGTH = 26
|
30
|
+
|
29
31
|
TIMESTAMP_OCTETS_LENGTH = 6
|
30
32
|
RANDOMNESS_OCTETS_LENGTH = 10
|
31
|
-
OCTETS_LENGTH =
|
33
|
+
OCTETS_LENGTH = 16
|
34
|
+
|
32
35
|
MAX_MILLISECONDS = 281474976710655
|
33
36
|
MAX_ENTROPY = 1208925819614629174706175
|
34
37
|
MAX_INTEGER = 340282366920938463463374607431768211455
|
@@ -40,14 +43,15 @@ class ULID
|
|
40
43
|
STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /\A#{PATTERN_WITH_CROCKFORD_BASE32_SUBSET.source}\z/i.freeze
|
41
44
|
|
42
45
|
# Optimized for `ULID.scan`, might be changed the definition with gathered `ULID.scan` spec changed.
|
43
|
-
|
44
|
-
SCANNING_PATTERN = /[0-7][#{CROCKFORD_BASE32_ENCODING_STRING}]{#{TIMESTAMP_ENCODED_LENGTH - 1}}[#{CROCKFORD_BASE32_ENCODING_STRING}]{#{RANDOMNESS_ENCODED_LENGTH}}/i.freeze
|
46
|
+
SCANNING_PATTERN = /\b[0-7][#{CROCKFORD_BASE32_ENCODING_STRING}]{#{TIMESTAMP_ENCODED_LENGTH - 1}}[#{CROCKFORD_BASE32_ENCODING_STRING}]{#{RANDOMNESS_ENCODED_LENGTH}}\b/i.freeze
|
45
47
|
|
46
|
-
#
|
48
|
+
# Similar as Time#inspect since Ruby 2.7, however it is NOT same.
|
49
|
+
# Time#inspect trancates needless digits. Keeping full milliseconds with "%3N" will fit for ULID.
|
47
50
|
# @see https://bugs.ruby-lang.org/issues/15958
|
51
|
+
# @see https://github.com/ruby/ruby/blob/744d17ff6c33b09334508e8110007ea2a82252f5/time.c#L4026-L4078
|
48
52
|
TIME_FORMAT_IN_INSPECT = '%Y-%m-%d %H:%M:%S.%3N %Z'
|
49
53
|
|
50
|
-
private_class_method
|
54
|
+
private_class_method(:new)
|
51
55
|
|
52
56
|
# @param [Integer, Time] moment
|
53
57
|
# @param [Integer] entropy
|
@@ -60,7 +64,7 @@ class ULID
|
|
60
64
|
# @param [Time] time
|
61
65
|
# @return [ULID]
|
62
66
|
def self.at(time)
|
63
|
-
raise
|
67
|
+
raise(ArgumentError, 'ULID.at takes only `Time` instance') unless Time === time
|
64
68
|
|
65
69
|
from_milliseconds_and_entropy(milliseconds: milliseconds_from_time(time), entropy: reasonable_entropy)
|
66
70
|
end
|
@@ -79,7 +83,7 @@ class ULID
|
|
79
83
|
|
80
84
|
RANDOM_INTEGER_GENERATOR = -> {
|
81
85
|
SecureRandom.random_number(MAX_INTEGER)
|
82
|
-
}
|
86
|
+
}.freeze
|
83
87
|
|
84
88
|
# @param [Range<Time>, Range<nil>, Range[ULID], nil] period
|
85
89
|
# @overload sample(number, period: nil)
|
@@ -92,14 +96,14 @@ class ULID
|
|
92
96
|
# * Do not ensure the uniqueness
|
93
97
|
# * Do not take random generator for the arguments
|
94
98
|
# * Raising error instead of truncating elements for the given number
|
95
|
-
def self.sample(
|
99
|
+
def self.sample(number=nil, period: nil)
|
96
100
|
int_generator = (
|
97
101
|
if period
|
98
102
|
ulid_range = range(period)
|
99
103
|
min, max, exclude_end = ulid_range.begin.to_i, ulid_range.end.to_i, ulid_range.exclude_end?
|
100
104
|
|
101
105
|
possibilities = (max - min) + (exclude_end ? 0 : 1)
|
102
|
-
raise
|
106
|
+
raise(ArgumentError, "given range `#{ulid_range.inspect}` does not have possibilities") unless possibilities.positive?
|
103
107
|
|
104
108
|
-> {
|
105
109
|
SecureRandom.random_number(possibilities) + min
|
@@ -109,24 +113,21 @@ class ULID
|
|
109
113
|
end
|
110
114
|
)
|
111
115
|
|
112
|
-
case
|
113
|
-
when
|
116
|
+
case number
|
117
|
+
when nil
|
114
118
|
from_integer(int_generator.call)
|
115
|
-
when
|
116
|
-
number = args.first
|
117
|
-
raise ArgumentError, 'accepts no argument or integer only' unless Integer === number
|
118
|
-
|
119
|
+
when Integer
|
119
120
|
if number > MAX_INTEGER || number.negative?
|
120
|
-
raise
|
121
|
+
raise(ArgumentError, "given number `#{number}` is larger than ULID limit `#{MAX_INTEGER}` or negative")
|
121
122
|
end
|
122
123
|
|
123
|
-
if period && (number > possibilities)
|
124
|
-
raise
|
124
|
+
if period && possibilities && (number > possibilities)
|
125
|
+
raise(ArgumentError, "given number `#{number}` is larger than given possibilities `#{possibilities}`")
|
125
126
|
end
|
126
127
|
|
127
128
|
Array.new(number) { from_integer(int_generator.call) }
|
128
129
|
else
|
129
|
-
raise
|
130
|
+
raise(ArgumentError, 'accepts no argument or integer only')
|
130
131
|
end
|
131
132
|
end
|
132
133
|
|
@@ -136,11 +137,13 @@ class ULID
|
|
136
137
|
# @yieldreturn [self]
|
137
138
|
def self.scan(string)
|
138
139
|
string = String.try_convert(string)
|
139
|
-
raise
|
140
|
-
return to_enum(
|
140
|
+
raise(ArgumentError, 'ULID.scan takes only strings') unless string
|
141
|
+
return to_enum(:scan, string) unless block_given?
|
141
142
|
|
142
143
|
string.scan(SCANNING_PATTERN) do |matched|
|
143
|
-
|
144
|
+
if String === matched
|
145
|
+
yield(parse(matched))
|
146
|
+
end
|
144
147
|
end
|
145
148
|
self
|
146
149
|
end
|
@@ -150,14 +153,16 @@ class ULID
|
|
150
153
|
# @raise [OverflowError] if the given integer is larger than the ULID limit
|
151
154
|
# @raise [ArgumentError] if the given integer is negative number
|
152
155
|
def self.from_integer(integer)
|
153
|
-
raise
|
154
|
-
raise
|
155
|
-
raise
|
156
|
+
raise(ArgumentError, 'ULID.from_integer takes only `Integer`') unless Integer === integer
|
157
|
+
raise(OverflowError, "integer overflow: given #{integer}, max: #{MAX_INTEGER}") unless integer <= MAX_INTEGER
|
158
|
+
raise(ArgumentError, "integer should not be negative: given: #{integer}") if integer.negative?
|
156
159
|
|
157
160
|
n32encoded = integer.to_s(32).rjust(ENCODED_LENGTH, '0')
|
158
161
|
n32encoded_timestamp = n32encoded.slice(0, TIMESTAMP_ENCODED_LENGTH)
|
159
162
|
n32encoded_randomness = n32encoded.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH)
|
160
163
|
|
164
|
+
raise(UnexpectedError) unless n32encoded_timestamp && n32encoded_randomness
|
165
|
+
|
161
166
|
milliseconds = n32encoded_timestamp.to_i(32)
|
162
167
|
entropy = n32encoded_randomness.to_i(32)
|
163
168
|
|
@@ -168,37 +173,43 @@ class ULID
|
|
168
173
|
# @return [Range<ULID>]
|
169
174
|
# @raise [ArgumentError] if the given period is not a `Range[Time]`, `Range[nil]` or `Range[ULID]`
|
170
175
|
def self.range(period)
|
171
|
-
raise
|
176
|
+
raise(ArgumentError, 'ULID.range takes only `Range[Time]`, `Range[nil]` or `Range[ULID]`') unless Range === period
|
172
177
|
|
173
178
|
begin_element, end_element, exclude_end = period.begin, period.end, period.exclude_end?
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
179
|
+
new_begin, new_end = false, false
|
180
|
+
|
181
|
+
begin_ulid = (
|
182
|
+
case begin_element
|
183
|
+
when Time
|
184
|
+
new_begin = true
|
185
|
+
min(begin_element)
|
186
|
+
when nil
|
187
|
+
MIN
|
188
|
+
when self
|
189
|
+
begin_element
|
190
|
+
else
|
191
|
+
raise(ArgumentError, "ULID.range takes only `Range[Time]`, `Range[nil]` or `Range[ULID]`, given: #{period.inspect}")
|
192
|
+
end
|
193
|
+
)
|
186
194
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
195
|
+
end_ulid = (
|
196
|
+
case end_element
|
197
|
+
when Time
|
198
|
+
new_end = true
|
199
|
+
exclude_end ? min(end_element) : max(end_element)
|
200
|
+
when nil
|
201
|
+
exclude_end = false
|
202
|
+
# The end should be max and include end, because nil end means to cover endless ULIDs until the limit
|
203
|
+
MAX
|
204
|
+
when self
|
205
|
+
end_element
|
206
|
+
else
|
207
|
+
raise(ArgumentError, "ULID.range takes only `Range[Time]`, `Range[nil]` or `Range[ULID]`, given: #{period.inspect}")
|
208
|
+
end
|
209
|
+
)
|
199
210
|
|
200
|
-
begin_ulid.freeze
|
201
|
-
end_ulid.freeze
|
211
|
+
begin_ulid.freeze if new_begin
|
212
|
+
end_ulid.freeze if new_end
|
202
213
|
|
203
214
|
Range.new(begin_ulid, end_ulid, exclude_end)
|
204
215
|
end
|
@@ -206,7 +217,7 @@ class ULID
|
|
206
217
|
# @param [Time] time
|
207
218
|
# @return [Time]
|
208
219
|
def self.floor(time)
|
209
|
-
raise
|
220
|
+
raise(ArgumentError, 'ULID.floor takes only `Time` instance') unless Time === time
|
210
221
|
|
211
222
|
time.floor(3)
|
212
223
|
end
|
@@ -234,7 +245,7 @@ class ULID
|
|
234
245
|
when Time
|
235
246
|
milliseconds_from_time(moment)
|
236
247
|
else
|
237
|
-
raise
|
248
|
+
raise(ArgumentError, '`moment` should be a `Time` or `Integer as milliseconds`')
|
238
249
|
end
|
239
250
|
end
|
240
251
|
|
@@ -248,10 +259,10 @@ class ULID
|
|
248
259
|
# @raise [ParserError] if the given format is not correct for ULID specs
|
249
260
|
def self.parse(string)
|
250
261
|
string = String.try_convert(string)
|
251
|
-
raise
|
262
|
+
raise(ArgumentError, 'ULID.parse takes only strings') unless string
|
252
263
|
|
253
264
|
unless STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET.match?(string)
|
254
|
-
raise
|
265
|
+
raise(ParserError, "given `#{string}` does not match to `#{STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET.inspect}`")
|
255
266
|
end
|
256
267
|
|
257
268
|
from_integer(CrockfordBase32.decode(string))
|
@@ -262,7 +273,7 @@ class ULID
|
|
262
273
|
# @raise [ParserError] if the given format is not correct for ULID specs, even if ignored `orthographical variants of the format`
|
263
274
|
def self.normalize(string)
|
264
275
|
string = String.try_convert(string)
|
265
|
-
raise
|
276
|
+
raise(ArgumentError, 'ULID.normalize takes only strings') unless string
|
266
277
|
|
267
278
|
normalized_in_crockford = CrockfordBase32.normalize(string)
|
268
279
|
# Ensure the ULID correctness, because CrockfordBase32 does not always mean to satisfy ULID format
|
@@ -298,7 +309,7 @@ class ULID
|
|
298
309
|
else
|
299
310
|
object_class_name = safe_get_class_name(object)
|
300
311
|
converted_class_name = safe_get_class_name(converted)
|
301
|
-
raise
|
312
|
+
raise(TypeError, "can't convert #{object_class_name} to ULID (#{object_class_name}#to_ulid gives #{converted_class_name})")
|
302
313
|
end
|
303
314
|
end
|
304
315
|
end
|
@@ -314,8 +325,12 @@ class ULID
|
|
314
325
|
begin
|
315
326
|
object.class
|
316
327
|
rescue NoMethodError
|
328
|
+
# steep can't correctly handle singleton class assign. See https://github.com/soutaro/steep/pull/586 for further detail
|
329
|
+
# So this annotation is hack for the type infer.
|
330
|
+
# @type var object: BasicObject
|
331
|
+
# @type var singleton_class: untyped
|
317
332
|
singleton_class = class << object; self; end
|
318
|
-
singleton_class.ancestors.detect { |ancestor| !ancestor.equal?(singleton_class) }
|
333
|
+
(Class === singleton_class) ? singleton_class.ancestors.detect { |ancestor| !ancestor.equal?(singleton_class) } : fallback
|
319
334
|
end
|
320
335
|
)
|
321
336
|
|
@@ -335,10 +350,10 @@ class ULID
|
|
335
350
|
# @raise [OverflowError] if the given value is larger than the ULID limit
|
336
351
|
# @raise [ArgumentError] if the given milliseconds and/or entropy is negative number
|
337
352
|
def self.from_milliseconds_and_entropy(milliseconds:, entropy:)
|
338
|
-
raise
|
339
|
-
raise
|
340
|
-
raise
|
341
|
-
raise
|
353
|
+
raise(ArgumentError, 'milliseconds and entropy should be an `Integer`') unless Integer === milliseconds && Integer === entropy
|
354
|
+
raise(OverflowError, "timestamp overflow: given #{milliseconds}, max: #{MAX_MILLISECONDS}") unless milliseconds <= MAX_MILLISECONDS
|
355
|
+
raise(OverflowError, "entropy overflow: given #{entropy}, max: #{MAX_ENTROPY}") unless entropy <= MAX_ENTROPY
|
356
|
+
raise(ArgumentError, 'milliseconds and entropy should not be negative') if milliseconds.negative? || entropy.negative?
|
342
357
|
|
343
358
|
n32encoded_timestamp = milliseconds.to_s(32).rjust(TIMESTAMP_ENCODED_LENGTH, '0')
|
344
359
|
n32encoded_randomness = entropy.to_s(32).rjust(RANDOMNESS_ENCODED_LENGTH, '0')
|
@@ -347,7 +362,8 @@ class ULID
|
|
347
362
|
new(milliseconds: milliseconds, entropy: entropy, integer: integer)
|
348
363
|
end
|
349
364
|
|
350
|
-
|
365
|
+
# @dynamic milliseconds, entropy
|
366
|
+
attr_reader(:milliseconds, :entropy)
|
351
367
|
|
352
368
|
# @api private
|
353
369
|
# @param [Integer] milliseconds
|
@@ -370,7 +386,11 @@ class ULID
|
|
370
386
|
def to_i
|
371
387
|
@integer
|
372
388
|
end
|
373
|
-
|
389
|
+
|
390
|
+
# @return [Integer]
|
391
|
+
def hash
|
392
|
+
[ULID, @integer].hash
|
393
|
+
end
|
374
394
|
|
375
395
|
# @return [Integer, nil]
|
376
396
|
def <=>(other)
|
@@ -386,15 +406,29 @@ class ULID
|
|
386
406
|
def eql?(other)
|
387
407
|
equal?(other) || (ULID === other && @integer == other.to_i)
|
388
408
|
end
|
389
|
-
|
409
|
+
# @dynamic ==
|
410
|
+
alias_method(:==, :eql?)
|
390
411
|
|
412
|
+
# Return `true` for same value of ULID, variant formats of strings, same Time in ULID precision(msec).
|
413
|
+
# Do not consider integer, octets and partial strings, then returns `false`.
|
414
|
+
#
|
391
415
|
# @return [Boolean]
|
416
|
+
# @see .normalize
|
417
|
+
# @see .floor
|
392
418
|
def ===(other)
|
393
419
|
case other
|
394
420
|
when ULID
|
395
421
|
@integer == other.to_i
|
396
422
|
when String
|
397
|
-
|
423
|
+
begin
|
424
|
+
normalized = ULID.normalize(other)
|
425
|
+
rescue Exception
|
426
|
+
false
|
427
|
+
else
|
428
|
+
to_s == normalized
|
429
|
+
end
|
430
|
+
when Time
|
431
|
+
to_time == ULID.floor(other)
|
398
432
|
else
|
399
433
|
false
|
400
434
|
end
|
@@ -416,22 +450,22 @@ class ULID
|
|
416
450
|
|
417
451
|
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer)]
|
418
452
|
def timestamp_octets
|
419
|
-
octets.slice(0, TIMESTAMP_OCTETS_LENGTH)
|
453
|
+
octets.slice(0, TIMESTAMP_OCTETS_LENGTH) || raise(UnexpectedError)
|
420
454
|
end
|
421
455
|
|
422
456
|
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer)]
|
423
457
|
def randomness_octets
|
424
|
-
octets.slice(TIMESTAMP_OCTETS_LENGTH, RANDOMNESS_OCTETS_LENGTH)
|
458
|
+
octets.slice(TIMESTAMP_OCTETS_LENGTH, RANDOMNESS_OCTETS_LENGTH) || raise(UnexpectedError)
|
425
459
|
end
|
426
460
|
|
427
461
|
# @return [String]
|
428
462
|
def timestamp
|
429
|
-
@timestamp ||= to_s.slice(0, TIMESTAMP_ENCODED_LENGTH).freeze
|
463
|
+
@timestamp ||= (to_s.slice(0, TIMESTAMP_ENCODED_LENGTH).freeze || raise(UnexpectedError))
|
430
464
|
end
|
431
465
|
|
432
466
|
# @return [String]
|
433
467
|
def randomness
|
434
|
-
@randomness ||= to_s.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH).freeze
|
468
|
+
@randomness ||= (to_s.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH).freeze || raise(UnexpectedError))
|
435
469
|
end
|
436
470
|
|
437
471
|
# @note Providing for rough operations. The keys and values is not fixed.
|
@@ -457,7 +491,8 @@ class ULID
|
|
457
491
|
ULID.from_integer(succ_int)
|
458
492
|
end
|
459
493
|
end
|
460
|
-
|
494
|
+
# @dynamic next
|
495
|
+
alias_method(:next, :succ)
|
461
496
|
|
462
497
|
# @return [ULID, nil] when called on ULID as `00000000000000000000000000`, returns `nil` instead of ULID
|
463
498
|
def pred
|
@@ -509,7 +544,7 @@ class ULID
|
|
509
544
|
self
|
510
545
|
end
|
511
546
|
|
512
|
-
undef_method
|
547
|
+
undef_method(:instance_variable_set)
|
513
548
|
|
514
549
|
private
|
515
550
|
|
@@ -521,13 +556,12 @@ class ULID
|
|
521
556
|
end
|
522
557
|
end
|
523
558
|
|
524
|
-
require_relative
|
525
|
-
require_relative
|
526
|
-
require_relative
|
559
|
+
require_relative('ulid/version')
|
560
|
+
require_relative('ulid/crockford_base32')
|
561
|
+
require_relative('ulid/monotonic_generator')
|
562
|
+
require_relative('ulid/ractor_unshareable_constants')
|
527
563
|
|
528
564
|
class ULID
|
529
|
-
|
530
|
-
MAX
|
531
|
-
|
532
|
-
private_constant :TIME_FORMAT_IN_INSPECT, :MIN, :MAX, :RANDOM_INTEGER_GENERATOR
|
565
|
+
# Do not write as `ULID.private_constant` for avoiding YARD warnings `[warn]: in YARD::Handlers::Ruby::PrivateConstantHandler: Undocumentable private constants:`
|
566
|
+
private_constant(:TIME_FORMAT_IN_INSPECT, :MIN, :MAX, :RANDOM_INTEGER_GENERATOR, :CROCKFORD_BASE32_ENCODING_STRING)
|
533
567
|
end
|
data/sig/ulid.rbs
CHANGED
@@ -36,7 +36,7 @@ class ULID < Object
|
|
36
36
|
end
|
37
37
|
|
38
38
|
module CrockfordBase32
|
39
|
-
class SetupError <
|
39
|
+
class SetupError < UnexpectedError
|
40
40
|
end
|
41
41
|
|
42
42
|
N32_CHAR_BY_CROCKFORD_BASE32_CHAR: Hash[String, String]
|
@@ -97,9 +97,9 @@ class ULID < Object
|
|
97
97
|
def to_ulid: () -> ULID
|
98
98
|
end
|
99
99
|
|
100
|
-
type
|
101
|
-
type timestamp_octets = [Integer, Integer, Integer, Integer, Integer, Integer]
|
102
|
-
type randomness_octets = [Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer]
|
100
|
+
type full_octets = [Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer] | Array[Integer]
|
101
|
+
type timestamp_octets = [Integer, Integer, Integer, Integer, Integer, Integer] | Array[Integer]
|
102
|
+
type randomness_octets = [Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer] | Array[Integer]
|
103
103
|
type period = Range[Time] | Range[nil] | Range[ULID]
|
104
104
|
|
105
105
|
@string: String?
|
@@ -292,33 +292,23 @@ class ULID < Object
|
|
292
292
|
# ulid2 = ULID.parse('01F4PTVCSN9ZPFKYTY2DDJVRK4') #=> ULID(2021-05-02 15:23:48.917 UTC: 01F4PTVCSN9ZPFKYTY2DDJVRK4)
|
293
293
|
# ulids = ULID.sample(1000, period: ulid1..ulid2)
|
294
294
|
# ulids.uniq.size #=> 1000
|
295
|
-
# ulids.take(
|
295
|
+
# ulids.take(5)
|
296
296
|
# #=>
|
297
297
|
# #[ULID(2021-05-02 06:57:19.954 UTC: 01F4NXW02JNB8H0J0TK48JD39X),
|
298
298
|
# # ULID(2021-05-02 07:06:07.458 UTC: 01F4NYC372GVP7NS0YAYQGT4VZ),
|
299
299
|
# # ULID(2021-05-01 06:16:35.791 UTC: 01F4K94P6F6P68K0H64WRDSFKW),
|
300
300
|
# # ULID(2021-04-27 22:17:37.844 UTC: 01F4APHGSMFJZQTGXKZBFFBPJP),
|
301
|
-
# # ULID(2021-04-28 20:17:55.357 UTC: 01F4D231MXQJXAR8G2JZHEJNH3)
|
302
|
-
# # ULID(2021-04-30 07:18:54.307 UTC: 01F4GTA2332AS2VPHC4FMKC7R5),
|
303
|
-
# # ULID(2021-05-02 12:26:03.480 UTC: 01F4PGNXARG554Y3HYVBDW4T9S),
|
304
|
-
# # ULID(2021-04-29 09:52:15.107 UTC: 01F4EGP483ZX2747FQPWQNPPMW),
|
305
|
-
# # ULID(2021-04-29 03:18:24.152 UTC: 01F4DT4Z4RA0QV8WFQGRAG63EH),
|
306
|
-
# # ULID(2021-05-02 13:27:16.394 UTC: 01F4PM605ABF5SDVMEHBH8JJ9R)]
|
301
|
+
# # ULID(2021-04-28 20:17:55.357 UTC: 01F4D231MXQJXAR8G2JZHEJNH3)]
|
307
302
|
# ULID.sample(10, period: ulid1.to_time..ulid2.to_time)
|
308
303
|
# #=>
|
309
304
|
# # [ULID(2021-04-29 06:44:41.513 UTC: 01F4E5YPD9XQ3MYXWK8ZJKY8SW),
|
310
305
|
# # ULID(2021-05-01 00:35:06.629 UTC: 01F4JNKD85SVK1EAEYSJGF53A2),
|
311
306
|
# # ULID(2021-05-02 12:45:28.408 UTC: 01F4PHSEYRG9BWBEWMRW1XE6WW),
|
312
307
|
# # ULID(2021-05-01 03:06:09.130 UTC: 01F4JY7ZBABCBMX16XH2Q4JW4W),
|
313
|
-
# # ULID(2021-04-29 21:38:58.109 UTC: 01F4FS45DX4049JEQK4W6TER6G)
|
314
|
-
# # ULID(2021-04-29 17:14:14.116 UTC: 01F4F9ZDQ449BE8BBZFEHYQWG2),
|
315
|
-
# # ULID(2021-04-30 16:18:08.205 UTC: 01F4HS5DPD1HWDVJNJ6YKJXKSK),
|
316
|
-
# # ULID(2021-04-30 10:31:33.602 UTC: 01F4H5ATF2A1CSQF0XV5NKZ288),
|
317
|
-
# # ULID(2021-04-28 16:49:06.484 UTC: 01F4CP4PDM214Q6H3KJP7DYJRR),
|
318
|
-
# # ULID(2021-04-28 15:05:06.808 UTC: 01F4CG68ZRST94T056KRZ5K9S4)]
|
308
|
+
# # ULID(2021-04-29 21:38:58.109 UTC: 01F4FS45DX4049JEQK4W6TER6G)]
|
319
309
|
# ```
|
320
310
|
def self.sample: (?period: period) -> ULID
|
321
|
-
| (Integer number, ?period: period) -> Array[ULID]
|
311
|
+
| (Integer number, ?period: period?) -> Array[ULID]
|
322
312
|
def self.valid?: (untyped) -> bool
|
323
313
|
|
324
314
|
# Returns normalized string
|
@@ -409,7 +399,9 @@ class ULID < Object
|
|
409
399
|
# ulid.to_i #=> 1957909092946624190749577070267409738
|
410
400
|
# ```
|
411
401
|
def to_i: -> Integer
|
412
|
-
|
402
|
+
|
403
|
+
# Returns integer for making as a hash key use-case
|
404
|
+
def hash: -> Integer
|
413
405
|
|
414
406
|
# Basically same as String based sort.
|
415
407
|
#
|
@@ -434,9 +426,28 @@ class ULID < Object
|
|
434
426
|
# ULID.parse('4NNB20D9C1ME2NGMTX51ERZJX0') == ULID.parse('4nnb20d9c1me2ngmtx51erzjx0')
|
435
427
|
# #=> true
|
436
428
|
# ```
|
437
|
-
def eql?: (
|
429
|
+
def eql?: (ULID other) -> bool
|
430
|
+
| (untyped other) -> false
|
438
431
|
alias == eql?
|
439
|
-
|
432
|
+
|
433
|
+
# Return `true` for same value of ULID, variant formats of strings, same Time in ULID precision(msec).
|
434
|
+
# Do not consider integer, octets and partial strings, then returns `false`.
|
435
|
+
#
|
436
|
+
# ```ruby
|
437
|
+
# ulid = ULID.parse('01G6Z7Q4RSH97E6QHAC7VK19G2')
|
438
|
+
# ulid === ULID.parse(ulid.to_s)
|
439
|
+
# #=> true
|
440
|
+
# ulid === ulid.to_s.downcase
|
441
|
+
# #=> true
|
442
|
+
# ulid === ulid.to_time
|
443
|
+
# #=> true
|
444
|
+
# ulid === ulid.to_i
|
445
|
+
# #=> false
|
446
|
+
# ulid === ulid.next
|
447
|
+
# #=> false
|
448
|
+
# ```
|
449
|
+
def ===: (ULID | String | Time other) -> bool
|
450
|
+
| (untyped other) -> false
|
440
451
|
|
441
452
|
# ```ruby
|
442
453
|
# ulid = ULID.generate
|
@@ -462,7 +473,7 @@ class ULID < Object
|
|
462
473
|
# Intentionally avoiding to use `Record type` ref: https://github.com/ruby/rbs/blob/4fb4c33b2325d1a73d79ff7aaeb49f21cec1e0e5/docs/syntax.md#record-type
|
463
474
|
# Because the returning values are not fixed.
|
464
475
|
def patterns: -> Hash[Symbol, Regexp | String]
|
465
|
-
def octets: ->
|
476
|
+
def octets: -> full_octets
|
466
477
|
def timestamp_octets: -> timestamp_octets
|
467
478
|
def randomness_octets: -> randomness_octets
|
468
479
|
|
@@ -525,7 +536,7 @@ class ULID < Object
|
|
525
536
|
private
|
526
537
|
|
527
538
|
# A private API. Should not be used in your code.
|
528
|
-
def self.new: (milliseconds: Integer, entropy: Integer, integer: Integer) ->
|
539
|
+
def self.new: (milliseconds: Integer, entropy: Integer, integer: Integer) -> ULID
|
529
540
|
|
530
541
|
# A private API. Should not be used in your code.
|
531
542
|
def self.reasonable_entropy: -> Integer
|
metadata
CHANGED
@@ -1,19 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-ulid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenichi Kamiya
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-07-02 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
14
|
-
|
15
|
-
This gem aims to provide the generator, monotonic generator, parser and handy manipulation features around the ULID.
|
16
|
-
Also providing `ruby/rbs` signature files.
|
13
|
+
description: " generator, monotonic generator, parser and manipulations for ULID
|
14
|
+
(RBS included)\n"
|
17
15
|
email:
|
18
16
|
- kachick1+ruby@gmail.com
|
19
17
|
executables: []
|
@@ -26,6 +24,7 @@ files:
|
|
26
24
|
- lib/ulid.rb
|
27
25
|
- lib/ulid/crockford_base32.rb
|
28
26
|
- lib/ulid/monotonic_generator.rb
|
27
|
+
- lib/ulid/ractor_unshareable_constants.rb
|
29
28
|
- lib/ulid/uuid.rb
|
30
29
|
- lib/ulid/version.rb
|
31
30
|
- sig/ulid.rbs
|
@@ -46,7 +45,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
46
45
|
requirements:
|
47
46
|
- - ">="
|
48
47
|
- !ruby/object:Gem::Version
|
49
|
-
version: 2.7.
|
48
|
+
version: 2.7.2
|
50
49
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
50
|
requirements:
|
52
51
|
- - ">="
|