ruby-ulid 0.4.0 → 0.6.1
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 +70 -99
- data/lib/ulid/crockford_base32.rb +55 -45
- data/lib/ulid/errors.rb +10 -0
- data/lib/ulid/monotonic_generator.rb +6 -2
- data/lib/ulid/version.rb +1 -1
- data/lib/ulid.rb +108 -48
- data/sig/ulid.rbs +94 -23
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5c7cf89034d0f74026278e2a4c113d57f8fc28b861b8bcf1d2210dbb76688e52
|
|
4
|
+
data.tar.gz: 340bfbea8fb1ff3ec0e49339e2a5a388a81552f8cd73a65ad9ff049448fc0c3f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 74d26cb5dc49a8d79a5c933817c1c8913954d675d996a91057f29859fd1fdf0a32c99d8121ef954e1d7d400110fa1737be17a9084bfc03655082de96c9614392
|
|
7
|
+
data.tar.gz: 96f19838c4c90ee313722857f88693e88f247e5380293291b362eadc834f6adb1613c01034a0cfe430f24576b4fea55f252b79f689b00c3e05b739118db8906e
|
data/README.md
CHANGED
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
[ulid/spec](https://github.com/ulid/spec) is useful.
|
|
9
9
|
Especially possess all `uniqueness`, `randomness`, `extractable timestamps` and `sortable` features.
|
|
10
|
-
This gem aims to provide the generator,
|
|
11
|
-
Also providing [RBS](https://github.com/ruby/rbs) signatures.
|
|
10
|
+
This gem aims to provide the generator, optional monotonicity, parser and other manipulation features around ULID with included [RBS](https://github.com/ruby/rbs).
|
|
12
11
|
|
|
13
12
|
---
|
|
14
13
|
|
|
@@ -50,7 +49,7 @@ Should be installed!
|
|
|
50
49
|
Add this line in your Gemfile.
|
|
51
50
|
|
|
52
51
|
```ruby
|
|
53
|
-
gem('ruby-ulid', '~> 0.
|
|
52
|
+
gem('ruby-ulid', '~> 0.6.1')
|
|
54
53
|
```
|
|
55
54
|
|
|
56
55
|
### How to use
|
|
@@ -58,34 +57,32 @@ gem('ruby-ulid', '~> 0.4.0')
|
|
|
58
57
|
```ruby
|
|
59
58
|
require 'ulid'
|
|
60
59
|
|
|
61
|
-
|
|
62
|
-
# => "
|
|
60
|
+
ULID::VERSION
|
|
61
|
+
# => "0.6.1"
|
|
63
62
|
```
|
|
64
63
|
|
|
65
|
-
|
|
64
|
+
NOTE: This README includes info about development version. If you would see released version's one. [Look at the ref](https://github.com/kachick/ruby-ulid/tree/v0.6.1).
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
### Generator and Parser
|
|
67
|
+
|
|
68
|
+
`ULID.generate` returns `ULID` instance. It is not just a string.
|
|
68
69
|
|
|
69
70
|
```ruby
|
|
70
71
|
ulid = ULID.generate #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
|
71
72
|
```
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
You can get the objects from exists encoded ULIDs.
|
|
74
|
+
`ULID.parse` returns `ULID` instance from exists encoded ULIDs.
|
|
76
75
|
|
|
77
76
|
```ruby
|
|
78
|
-
ulid = ULID.parse('
|
|
77
|
+
ulid = ULID.parse('01F4A5Y1YAQCYAYCTC7GRMJ9AA') #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
|
79
78
|
```
|
|
80
79
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
You can extract timestamps and binary formats.
|
|
80
|
+
It is helpful to inspect.
|
|
84
81
|
|
|
85
82
|
```ruby
|
|
86
|
-
ulid = ULID.parse('01F4A5Y1YAQCYAYCTC7GRMJ9AA') #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
|
87
83
|
ulid.to_time #=> 2021-04-27 17:27:22.826 UTC
|
|
88
84
|
ulid.milliseconds #=> 1619544442826
|
|
85
|
+
ulid.encode #=> "01F4A5Y1YAQCYAYCTC7GRMJ9AA"
|
|
89
86
|
ulid.to_s #=> "01F4A5Y1YAQCYAYCTC7GRMJ9AA"
|
|
90
87
|
ulid.timestamp #=> "01F4A5Y1YA"
|
|
91
88
|
ulid.randomness #=> "QCYAYCTC7GRMJ9AA"
|
|
@@ -93,6 +90,42 @@ ulid.to_i #=> 1957909092946624190749577070267409738
|
|
|
93
90
|
ulid.octets #=> [1, 121, 20, 95, 7, 202, 187, 60, 175, 51, 76, 60, 49, 73, 37, 74]
|
|
94
91
|
```
|
|
95
92
|
|
|
93
|
+
`ULID.generate` can take fixed `Time` instance. `ULID.at` is the shorthand.
|
|
94
|
+
|
|
95
|
+
```ruby
|
|
96
|
+
time = Time.at(946684800).utc #=> 2000-01-01 00:00:00 UTC
|
|
97
|
+
ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB00N018DCPJA4H9379P)
|
|
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)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Also `ULID.encode` and `ULID.decode_time` can be used to get primitive values for most usecases.
|
|
103
|
+
|
|
104
|
+
`ULID.encode` returns [normalized](#variants-of-format) String without ULID object creation.
|
|
105
|
+
It can take same arguments as `ULID.generate`.
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
ULID.encode #=> "01G86M42Q6SJ9XQM2ZRM6JRDSF"
|
|
109
|
+
ULID.encode(moment: Time.at(946684800).utc) #=> "00VHNCZB00SYG7RCEXZC9DA4E1"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
`ULID.decode_time` returns Time. It can take `in` keyarg as same as `Time.at`.
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
ULID.decode_time('00VHNCZB00SYG7RCEXZC9DA4E1') #=> 2000-01-01 00:00:00 UTC
|
|
116
|
+
ULID.decode_time('00VHNCZB00SYG7RCEXZC9DA4E1', in: '+09:00') #=> 2000-01-01 09:00:00 +0900
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
This project does not prioritize the speed. However it actually works faster than others! :zap:
|
|
120
|
+
|
|
121
|
+
Snapshot on 0.6.1 is below
|
|
122
|
+
|
|
123
|
+
* Generator is 1.5x faster than - [ulid gem](https://github.com/rafaelsales/ulid)
|
|
124
|
+
* Generator is 1.9x faster than - [ulid-ruby gem](https://github.com/abachman/ulid-ruby)
|
|
125
|
+
* Parser is 2.7x faster than - [ulid-ruby gem](https://github.com/abachman/ulid-ruby)
|
|
126
|
+
|
|
127
|
+
You can see further detail at [Benchmark](https://github.com/kachick/ruby-ulid/wiki/Benchmark).
|
|
128
|
+
|
|
96
129
|
### Sortable with the timestamp
|
|
97
130
|
|
|
98
131
|
ULIDs are sortable when they are generated in different timestamp with milliseconds precision.
|
|
@@ -106,21 +139,7 @@ ulids.uniq(&:to_time).size #=> 1000
|
|
|
106
139
|
ulids.sort == ulids #=> true
|
|
107
140
|
```
|
|
108
141
|
|
|
109
|
-
`
|
|
110
|
-
|
|
111
|
-
```ruby
|
|
112
|
-
time = Time.at(946684800).utc #=> 2000-01-01 00:00:00 UTC
|
|
113
|
-
ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB00N018DCPJA4H9379P)
|
|
114
|
-
ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB006WQT3JTMN0T14EBP)
|
|
115
|
-
ULID.at(time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB002W5BGWWKN76N22H6)
|
|
116
|
-
|
|
117
|
-
ulids = 1000.times.map do |n|
|
|
118
|
-
ULID.at(time + n)
|
|
119
|
-
end
|
|
120
|
-
ulids.sort == ulids #=> true
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
The basic generator prefers `randomness`, it does not guarantee `sortable` for same milliseconds ULIDs.
|
|
142
|
+
Basic generator prefers `randomness`, it does not guarantee `sortable` for same milliseconds ULIDs.
|
|
124
143
|
|
|
125
144
|
```ruby
|
|
126
145
|
ulids = 10000.times.map do
|
|
@@ -162,7 +181,7 @@ sample_ulids_by_the_time.take(5) #=>
|
|
|
162
181
|
ulids.sort == ulids #=> true
|
|
163
182
|
```
|
|
164
183
|
|
|
165
|
-
Same
|
|
184
|
+
Same instance of `ULID::MonotonicGenerator` does not generate duplicated ULIDs even in multi threads environment. It is implemented with [Monitor](https://bugs.ruby-lang.org/issues/16255).
|
|
166
185
|
|
|
167
186
|
### Filtering IDs with `Time`
|
|
168
187
|
|
|
@@ -202,7 +221,7 @@ time = Time.at(946684800, Rational('123456.789')).utc #=> 2000-01-01 00:00:00.12
|
|
|
202
221
|
ULID.floor(time) #=> 2000-01-01 00:00:00.123 UTC
|
|
203
222
|
```
|
|
204
223
|
|
|
205
|
-
###
|
|
224
|
+
### Tools
|
|
206
225
|
|
|
207
226
|
#### Scanner for string (e.g. `JSON`)
|
|
208
227
|
|
|
@@ -278,7 +297,7 @@ ULID.max(time) #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3VZZZZZZZZZZZZZZZZ)
|
|
|
278
297
|
`ULID#next` and `ULID#succ` returns next(successor) ULID.
|
|
279
298
|
Especially `ULID#succ` makes it possible `Range[ULID]#each`.
|
|
280
299
|
|
|
281
|
-
NOTE: However basically `Range[ULID]#each` should not be used.
|
|
300
|
+
NOTE: However basically `Range[ULID]#each` should not be used. Incrementing 128 bits IDs are not reasonable operation in most cases.
|
|
282
301
|
|
|
283
302
|
```ruby
|
|
284
303
|
ULID.parse('01BX5ZZKBKZZZZZZZZZZZZZZZY').next.to_s #=> "01BX5ZZKBKZZZZZZZZZZZZZZZZ"
|
|
@@ -341,31 +360,32 @@ ULID.sample(5, period: ulid1.to_time..ulid2.to_time)
|
|
|
341
360
|
|
|
342
361
|
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.
|
|
343
362
|
|
|
363
|
+
>Case insensitive
|
|
364
|
+
|
|
365
|
+
I can understand it might be considered in actual use-case. So `ULID.parse` accepts upcase and downcase.
|
|
366
|
+
However it is a controversial point, discussing in [ulid/spec#3](https://github.com/ulid/spec/issues/3).
|
|
367
|
+
|
|
344
368
|
>Uses Crockford's base32 for better efficiency and readability (5 bits per character)
|
|
345
369
|
|
|
346
370
|
The original `Crockford's base32` maps `I`, `L` to `1`, `O` to `0`.
|
|
347
371
|
And accepts freestyle inserting `Hyphens (-)`.
|
|
348
372
|
To consider this patterns or not is different in each implementations.
|
|
349
373
|
|
|
350
|
-
|
|
351
|
-
I have suggested it would be clarified in [ulid/spec#57](https://github.com/ulid/spec/pull/57).
|
|
374
|
+
I have suggested to clarify `subset of Crockford's base32` in [ulid/spec#57](https://github.com/ulid/spec/pull/57).
|
|
352
375
|
|
|
353
|
-
|
|
376
|
+
This gem provides some methods to handle the nasty possibilities.
|
|
354
377
|
|
|
355
|
-
|
|
356
|
-
But it is a controversial point, discussing in [ulid/spec#3](https://github.com/ulid/spec/issues/3).
|
|
357
|
-
|
|
358
|
-
Be that as it may, this gem provides API for handling the nasty possibilities.
|
|
359
|
-
|
|
360
|
-
`ULID.normalize` and `ULID.normalized?`
|
|
378
|
+
`ULID.normalize`, `ULID.normalized?`, `ULID.valid_as_variant_format?` and `ULID.parse_variant_format`
|
|
361
379
|
|
|
362
380
|
```ruby
|
|
363
|
-
ULID.normalize('-
|
|
364
|
-
ULID.normalized?('-
|
|
365
|
-
ULID.normalized?('
|
|
381
|
+
ULID.normalize('01g70y0y7g-z1xwdarexergsddd') #=> "01G70Y0Y7GZ1XWDAREXERGSDDD"
|
|
382
|
+
ULID.normalized?('01g70y0y7g-z1xwdarexergsddd') #=> false
|
|
383
|
+
ULID.normalized?('01G70Y0Y7GZ1XWDAREXERGSDDD') #=> true
|
|
384
|
+
ULID.valid_as_variant_format?('01g70y0y7g-z1xwdarexergsddd') #=> true
|
|
385
|
+
ULID.parse_variant_format('01G70Y0Y7G-ZLXWDIREXERGSDoD') #=> ULID(2022-07-03 02:25:22.672 UTC: 01G70Y0Y7GZ1XWD1REXERGSD0D)
|
|
366
386
|
```
|
|
367
387
|
|
|
368
|
-
#### UUIDv4 converter
|
|
388
|
+
#### UUIDv4 converter (experimental)
|
|
369
389
|
|
|
370
390
|
`ULID.from_uuidv4` and `ULID#to_uuidv4` is the converter.
|
|
371
391
|
The imported timestamp is meaningless. So ULID's benefit will lost.
|
|
@@ -398,68 +418,19 @@ ULID.min == reversed_min #=> false
|
|
|
398
418
|
ULID.max == reversed_max #=> false
|
|
399
419
|
```
|
|
400
420
|
|
|
401
|
-
##
|
|
402
|
-
|
|
403
|
-
As far as I know, major prior arts are below
|
|
404
|
-
|
|
405
|
-
### [ulid gem](https://rubygems.org/gems/ulid) - [rafaelsales/ulid](https://github.com/rafaelsales/ulid)
|
|
406
|
-
|
|
407
|
-
It is just providing basic `String` generator only.
|
|
408
|
-
So you can replace the code as below
|
|
409
|
-
|
|
410
|
-
```diff
|
|
411
|
-
-ULID.generate
|
|
412
|
-
+ULID.generate.to_s
|
|
413
|
-
```
|
|
414
|
-
|
|
415
|
-
NOTE: In version before `1.3.0`, timestamps might not be correct value.
|
|
416
|
-
|
|
417
|
-
1. [Sort order does not respect millisecond ordering](https://github.com/rafaelsales/ulid/issues/22)
|
|
418
|
-
1. [Fixed in this PR](https://github.com/rafaelsales/ulid/pull/23)
|
|
419
|
-
1. [Released in 1.3.0](https://github.com/rafaelsales/ulid/compare/1.2.0...v1.3.0)
|
|
420
|
-
|
|
421
|
-
### [ulid-ruby gem](https://rubygems.org/gems/ulid-ruby) - [abachman/ulid-ruby](https://github.com/abachman/ulid-ruby)
|
|
422
|
-
|
|
423
|
-
It is providing basic generator(except monotonic generator) and parser.
|
|
424
|
-
Major methods can be replaced as below.
|
|
425
|
-
|
|
426
|
-
```diff
|
|
427
|
-
-ULID.generate
|
|
428
|
-
+ULID.generate.to_s
|
|
429
|
-
-ULID.at(time)
|
|
430
|
-
+ULID.at(time).to_s
|
|
431
|
-
-ULID.time(string)
|
|
432
|
-
+ULID.parse(string).to_time
|
|
433
|
-
-ULID.min_ulid_at(time)
|
|
434
|
-
+ULID.min(time).to_s
|
|
435
|
-
-ULID.max_ulid_at(time)
|
|
436
|
-
+ULID.max(time).to_s
|
|
437
|
-
```
|
|
438
|
-
|
|
439
|
-
NOTE: In version before `1.0.2`, timestamps might not be correct value.
|
|
440
|
-
|
|
441
|
-
1. [Parsed time object has more than milliseconds](https://github.com/abachman/ulid-ruby/issues/3)
|
|
442
|
-
1. [Fix to handle timestamp precision in parser](https://github.com/abachman/ulid-ruby/pull/5)
|
|
443
|
-
1. [Fix to handle timestamp precision in generator](https://github.com/abachman/ulid-ruby/pull/4)
|
|
444
|
-
1. [Released in 1.0.2](https://github.com/abachman/ulid-ruby/compare/v1.0.0...v1.0.2)
|
|
445
|
-
|
|
446
|
-
### Compare performance with them
|
|
447
|
-
|
|
448
|
-
See [Benchmark](https://github.com/kachick/ruby-ulid/wiki/Benchmark).
|
|
421
|
+
## Migration from other gems
|
|
449
422
|
|
|
450
|
-
|
|
423
|
+
See [wiki page for gem migration](https://github.com/kachick/ruby-ulid/wiki/Gem-migration).
|
|
451
424
|
|
|
452
|
-
##
|
|
425
|
+
## RBS
|
|
453
426
|
|
|
454
|
-
|
|
427
|
+
Try at [examples/rbs_sandbox](https://github.com/kachick/ruby-ulid/tree/main/examples/rbs_sandbox).
|
|
455
428
|
|
|
456
429
|
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
430
|
|
|
458
431
|
* 
|
|
459
432
|
* 
|
|
460
433
|
* 
|
|
461
|
-
* 
|
|
462
|
-
* 
|
|
463
434
|
|
|
464
435
|
## References
|
|
465
436
|
|
|
@@ -15,63 +15,66 @@ class ULID
|
|
|
15
15
|
# * https://github.com/kachick/ruby-ulid/issues/57
|
|
16
16
|
# * https://github.com/kachick/ruby-ulid/issues/78
|
|
17
17
|
module CrockfordBase32
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'S' => 25,
|
|
38
|
-
'T' => 26,
|
|
39
|
-
'V' => 27,
|
|
40
|
-
'W' => 28,
|
|
41
|
-
'X' => 29,
|
|
42
|
-
'Y' => 30,
|
|
43
|
-
'Z' => 31
|
|
18
|
+
same_definitions = {
|
|
19
|
+
'0' => '0',
|
|
20
|
+
'1' => '1',
|
|
21
|
+
'2' => '2',
|
|
22
|
+
'3' => '3',
|
|
23
|
+
'4' => '4',
|
|
24
|
+
'5' => '5',
|
|
25
|
+
'6' => '6',
|
|
26
|
+
'7' => '7',
|
|
27
|
+
'8' => '8',
|
|
28
|
+
'9' => '9',
|
|
29
|
+
'A' => 'A',
|
|
30
|
+
'B' => 'B',
|
|
31
|
+
'C' => 'C',
|
|
32
|
+
'D' => 'D',
|
|
33
|
+
'E' => 'E',
|
|
34
|
+
'F' => 'F',
|
|
35
|
+
'G' => 'G',
|
|
36
|
+
'H' => 'H'
|
|
44
37
|
}.freeze
|
|
45
38
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
39
|
+
# Excluded I, L, O, U, - from Base32
|
|
40
|
+
base32_to_crockford = {
|
|
41
|
+
'I' => 'J',
|
|
42
|
+
'J' => 'K',
|
|
43
|
+
'K' => 'M',
|
|
44
|
+
'L' => 'N',
|
|
45
|
+
'M' => 'P',
|
|
46
|
+
'N' => 'Q',
|
|
47
|
+
'O' => 'R',
|
|
48
|
+
'P' => 'S',
|
|
49
|
+
'Q' => 'T',
|
|
50
|
+
'R' => 'V',
|
|
51
|
+
'S' => 'W',
|
|
52
|
+
'T' => 'X',
|
|
53
|
+
'U' => 'Y',
|
|
54
|
+
'V' => 'Z'
|
|
55
|
+
}.freeze
|
|
56
|
+
BASE32_TR_PATTERN = base32_to_crockford.keys.join.freeze
|
|
57
|
+
CROCKFORD_TR_PATTERN = base32_to_crockford.values.join.freeze
|
|
58
|
+
ENCODING_STRING = "#{same_definitions.values.join}#{CROCKFORD_TR_PATTERN}".freeze
|
|
58
59
|
|
|
59
|
-
|
|
60
|
+
variant_to_normarized = {
|
|
60
61
|
'L' => '1',
|
|
61
62
|
'l' => '1',
|
|
62
63
|
'I' => '1',
|
|
63
64
|
'i' => '1',
|
|
64
65
|
'O' => '0',
|
|
65
|
-
'o' => '0'
|
|
66
|
-
'-' => ''
|
|
66
|
+
'o' => '0'
|
|
67
67
|
}.freeze
|
|
68
|
-
|
|
68
|
+
VARIANT_TR_PATTERN = variant_to_normarized.keys.join.freeze
|
|
69
|
+
NORMALIZED_TR_PATTERN = variant_to_normarized.values.join.freeze
|
|
70
|
+
|
|
71
|
+
# @note Avoid to depend regex as possible. `tr(string, string)` is almost 2x Faster than `gsub(regex, hash)` in Ruby 3.1
|
|
69
72
|
|
|
70
73
|
# @api private
|
|
71
74
|
# @param [String] string
|
|
72
75
|
# @return [Integer]
|
|
73
76
|
def self.decode(string)
|
|
74
|
-
n32encoded = string.upcase.
|
|
77
|
+
n32encoded = string.upcase.tr(CROCKFORD_TR_PATTERN, BASE32_TR_PATTERN)
|
|
75
78
|
n32encoded.to_i(32)
|
|
76
79
|
end
|
|
77
80
|
|
|
@@ -80,14 +83,21 @@ class ULID
|
|
|
80
83
|
# @return [String]
|
|
81
84
|
def self.encode(integer)
|
|
82
85
|
n32encoded = integer.to_s(32)
|
|
83
|
-
n32encoded
|
|
86
|
+
from_n32(n32encoded).rjust(ENCODED_LENGTH, '0')
|
|
84
87
|
end
|
|
85
88
|
|
|
86
89
|
# @api private
|
|
87
90
|
# @param [String] string
|
|
88
91
|
# @return [String]
|
|
89
92
|
def self.normalize(string)
|
|
90
|
-
string.
|
|
93
|
+
string.delete('-').tr(VARIANT_TR_PATTERN, NORMALIZED_TR_PATTERN)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @api private
|
|
97
|
+
# @param [String] n32encoded
|
|
98
|
+
# @return [String]
|
|
99
|
+
def self.from_n32(n32encoded)
|
|
100
|
+
n32encoded.upcase.tr(BASE32_TR_PATTERN, CROCKFORD_TR_PATTERN)
|
|
91
101
|
end
|
|
92
102
|
end
|
|
93
103
|
end
|
data/lib/ulid/errors.rb
ADDED
|
@@ -10,7 +10,6 @@ class ULID
|
|
|
10
10
|
# However it is a C extention, I'm pending to use it for now.
|
|
11
11
|
include(MonitorMixin)
|
|
12
12
|
|
|
13
|
-
# @dynamic prev
|
|
14
13
|
# @return [ULID, nil]
|
|
15
14
|
attr_reader(:prev)
|
|
16
15
|
|
|
@@ -25,7 +24,6 @@ class ULID
|
|
|
25
24
|
def inspect
|
|
26
25
|
"ULID::MonotonicGenerator(prev: #{@prev.inspect})"
|
|
27
26
|
end
|
|
28
|
-
# @dynamic to_s
|
|
29
27
|
alias_method(:to_s, :inspect)
|
|
30
28
|
|
|
31
29
|
# @param [Time, Integer] moment
|
|
@@ -70,6 +68,12 @@ class ULID
|
|
|
70
68
|
end
|
|
71
69
|
end
|
|
72
70
|
|
|
71
|
+
# @param [Time, Integer] moment
|
|
72
|
+
# @return [String]
|
|
73
|
+
def encode(moment: ULID.current_milliseconds)
|
|
74
|
+
generate(moment: moment).encode
|
|
75
|
+
end
|
|
76
|
+
|
|
73
77
|
undef_method(:freeze)
|
|
74
78
|
|
|
75
79
|
# @raise [TypeError] always raises exception and does not freeze self
|
data/lib/ulid/version.rb
CHANGED
data/lib/ulid.rb
CHANGED
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
require('securerandom')
|
|
8
8
|
|
|
9
|
+
require_relative('ulid/version')
|
|
10
|
+
require_relative('ulid/errors')
|
|
11
|
+
require_relative('ulid/crockford_base32')
|
|
12
|
+
require_relative('ulid/monotonic_generator')
|
|
13
|
+
|
|
9
14
|
# @see https://github.com/ulid/spec
|
|
10
15
|
# @!attribute [r] milliseconds
|
|
11
16
|
# @return [Integer]
|
|
@@ -14,16 +19,6 @@ require('securerandom')
|
|
|
14
19
|
class ULID
|
|
15
20
|
include(Comparable)
|
|
16
21
|
|
|
17
|
-
class Error < StandardError; end
|
|
18
|
-
class OverflowError < Error; end
|
|
19
|
-
class ParserError < Error; end
|
|
20
|
-
class UnexpectedError < Error; end
|
|
21
|
-
|
|
22
|
-
# Excluded I, L, O, U, -.
|
|
23
|
-
# This is the encoding patterns.
|
|
24
|
-
# The decoding issue is written in ULID::CrockfordBase32
|
|
25
|
-
CROCKFORD_BASE32_ENCODING_STRING = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'
|
|
26
|
-
|
|
27
22
|
TIMESTAMP_ENCODED_LENGTH = 10
|
|
28
23
|
RANDOMNESS_ENCODED_LENGTH = 16
|
|
29
24
|
ENCODED_LENGTH = 26
|
|
@@ -38,12 +33,12 @@ class ULID
|
|
|
38
33
|
|
|
39
34
|
# @see https://github.com/ulid/spec/pull/57
|
|
40
35
|
# Currently not used as a constant, but kept as a reference for now.
|
|
41
|
-
PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /(?<timestamp>[0-7][#{
|
|
36
|
+
PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /(?<timestamp>[0-7][#{CrockfordBase32::ENCODING_STRING}]{#{TIMESTAMP_ENCODED_LENGTH - 1}})(?<randomness>[#{CrockfordBase32::ENCODING_STRING}]{#{RANDOMNESS_ENCODED_LENGTH}})/i.freeze
|
|
42
37
|
|
|
43
38
|
STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /\A#{PATTERN_WITH_CROCKFORD_BASE32_SUBSET.source}\z/i.freeze
|
|
44
39
|
|
|
45
40
|
# Optimized for `ULID.scan`, might be changed the definition with gathered `ULID.scan` spec changed.
|
|
46
|
-
SCANNING_PATTERN = /\b[0-7][#{
|
|
41
|
+
SCANNING_PATTERN = /\b[0-7][#{CrockfordBase32::ENCODING_STRING}]{#{TIMESTAMP_ENCODED_LENGTH - 1}}[#{CrockfordBase32::ENCODING_STRING}]{#{RANDOMNESS_ENCODED_LENGTH}}\b/i.freeze
|
|
47
42
|
|
|
48
43
|
# Similar as Time#inspect since Ruby 2.7, however it is NOT same.
|
|
49
44
|
# Time#inspect trancates needless digits. Keeping full milliseconds with "%3N" will fit for ULID.
|
|
@@ -60,6 +55,16 @@ class ULID
|
|
|
60
55
|
from_milliseconds_and_entropy(milliseconds: milliseconds_from_moment(moment), entropy: entropy)
|
|
61
56
|
end
|
|
62
57
|
|
|
58
|
+
# Almost same as [.generate] except directly returning String without needless object creation
|
|
59
|
+
#
|
|
60
|
+
# @param [Integer, Time] moment
|
|
61
|
+
# @param [Integer] entropy
|
|
62
|
+
# @return [String]
|
|
63
|
+
def self.encode(moment: current_milliseconds, entropy: reasonable_entropy)
|
|
64
|
+
n32_encoded = encode_n32(milliseconds: milliseconds_from_moment(moment), entropy: entropy)
|
|
65
|
+
CrockfordBase32.from_n32(n32_encoded)
|
|
66
|
+
end
|
|
67
|
+
|
|
63
68
|
# Short hand of `ULID.generate(moment: time)`
|
|
64
69
|
# @param [Time] time
|
|
65
70
|
# @return [ULID]
|
|
@@ -166,7 +171,12 @@ class ULID
|
|
|
166
171
|
milliseconds = n32encoded_timestamp.to_i(32)
|
|
167
172
|
entropy = n32encoded_randomness.to_i(32)
|
|
168
173
|
|
|
169
|
-
new(
|
|
174
|
+
new(
|
|
175
|
+
milliseconds: milliseconds,
|
|
176
|
+
entropy: entropy,
|
|
177
|
+
integer: integer,
|
|
178
|
+
encoded: CrockfordBase32.from_n32(n32encoded).freeze
|
|
179
|
+
)
|
|
170
180
|
end
|
|
171
181
|
|
|
172
182
|
# @param [Range<Time>, Range<nil>, Range[ULID]] period
|
|
@@ -254,6 +264,17 @@ class ULID
|
|
|
254
264
|
SecureRandom.random_number(MAX_ENTROPY)
|
|
255
265
|
end
|
|
256
266
|
|
|
267
|
+
private_class_method def self.encode_n32(milliseconds:, entropy:)
|
|
268
|
+
raise(ArgumentError, 'milliseconds and entropy should be an `Integer`') unless Integer === milliseconds && Integer === entropy
|
|
269
|
+
raise(OverflowError, "timestamp overflow: given #{milliseconds}, max: #{MAX_MILLISECONDS}") unless milliseconds <= MAX_MILLISECONDS
|
|
270
|
+
raise(OverflowError, "entropy overflow: given #{entropy}, max: #{MAX_ENTROPY}") unless entropy <= MAX_ENTROPY
|
|
271
|
+
raise(ArgumentError, 'milliseconds and entropy should not be negative') if milliseconds.negative? || entropy.negative?
|
|
272
|
+
|
|
273
|
+
n32encoded_timestamp = milliseconds.to_s(32).rjust(TIMESTAMP_ENCODED_LENGTH, '0')
|
|
274
|
+
n32encoded_randomness = entropy.to_s(32).rjust(RANDOMNESS_ENCODED_LENGTH, '0')
|
|
275
|
+
"#{n32encoded_timestamp}#{n32encoded_randomness}"
|
|
276
|
+
end
|
|
277
|
+
|
|
257
278
|
# @param [String, #to_str] string
|
|
258
279
|
# @return [ULID]
|
|
259
280
|
# @raise [ParserError] if the given format is not correct for ULID specs
|
|
@@ -268,6 +289,36 @@ class ULID
|
|
|
268
289
|
from_integer(CrockfordBase32.decode(string))
|
|
269
290
|
end
|
|
270
291
|
|
|
292
|
+
# @param [String, #to_str] string
|
|
293
|
+
# @return [ULID]
|
|
294
|
+
# @raise [ParserError] if the given format is not correct for ULID specs
|
|
295
|
+
def self.parse_variant_format(string)
|
|
296
|
+
string = String.try_convert(string)
|
|
297
|
+
raise(ArgumentError, 'ULID.parse_variant_format takes only strings') unless string
|
|
298
|
+
|
|
299
|
+
normalized_in_crockford = CrockfordBase32.normalize(string)
|
|
300
|
+
parse(normalized_in_crockford)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Almost same as `ULID.parse(string).to_time` except directly returning Time instance without needless object creation
|
|
304
|
+
#
|
|
305
|
+
# @param [String, #to_str] string
|
|
306
|
+
# @return [Time]
|
|
307
|
+
# @raise [ParserError] if the given format is not correct for ULID specs
|
|
308
|
+
def self.decode_time(string, in: 'UTC')
|
|
309
|
+
in_for_time_at = binding.local_variable_get(:in) # Needed because `in` is a reserved word.
|
|
310
|
+
string = String.try_convert(string)
|
|
311
|
+
raise(ArgumentError, 'ULID.decode_time takes only strings') unless string
|
|
312
|
+
|
|
313
|
+
unless STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET.match?(string)
|
|
314
|
+
raise(ParserError, "given `#{string}` does not match to `#{STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET.inspect}`")
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
timestamp = string.slice(0, TIMESTAMP_ENCODED_LENGTH).freeze || raise(UnexpectedError)
|
|
318
|
+
|
|
319
|
+
Time.at(0, CrockfordBase32.decode(timestamp), :millisecond, in: in_for_time_at)
|
|
320
|
+
end
|
|
321
|
+
|
|
271
322
|
# @param [String, #to_str] string
|
|
272
323
|
# @return [String]
|
|
273
324
|
# @raise [ParserError] if the given format is not correct for ULID specs, even if ignored `orthographical variants of the format`
|
|
@@ -275,23 +326,40 @@ class ULID
|
|
|
275
326
|
string = String.try_convert(string)
|
|
276
327
|
raise(ArgumentError, 'ULID.normalize takes only strings') unless string
|
|
277
328
|
|
|
278
|
-
normalized_in_crockford = CrockfordBase32.normalize(string)
|
|
279
329
|
# Ensure the ULID correctness, because CrockfordBase32 does not always mean to satisfy ULID format
|
|
280
|
-
|
|
330
|
+
parse_variant_format(string).to_s
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# @param [String, #to_str] string
|
|
334
|
+
# @return [Boolean]
|
|
335
|
+
def self.normalized?(string)
|
|
336
|
+
normalized = normalize(string)
|
|
337
|
+
rescue Exception
|
|
338
|
+
false
|
|
339
|
+
else
|
|
340
|
+
normalized == string
|
|
281
341
|
end
|
|
282
342
|
|
|
343
|
+
# @param [String, #to_str] string
|
|
283
344
|
# @return [Boolean]
|
|
284
|
-
def self.
|
|
285
|
-
|
|
345
|
+
def self.valid_as_variant_format?(string)
|
|
346
|
+
parse_variant_format(string)
|
|
286
347
|
rescue Exception
|
|
287
348
|
false
|
|
288
349
|
else
|
|
289
|
-
|
|
350
|
+
true
|
|
290
351
|
end
|
|
291
352
|
|
|
353
|
+
# @deprecated Use [.valid_as_variant_format?] or [.normalized?] instead
|
|
354
|
+
#
|
|
355
|
+
# Returns `true` if it is normalized string.
|
|
356
|
+
# Basically the difference of normalized? is to accept downcase or not. This returns true for downcased ULIDs.
|
|
357
|
+
#
|
|
292
358
|
# @return [Boolean]
|
|
293
|
-
def self.valid?(
|
|
294
|
-
|
|
359
|
+
def self.valid?(string)
|
|
360
|
+
warn_kwargs = (RUBY_VERSION >= '3.0') ? { category: :deprecated } : {}
|
|
361
|
+
Warning.warn('ULID.valid? is deprecated. Use ULID.valid_as_variant_format? or ULID.normalized? instead.', **warn_kwargs)
|
|
362
|
+
string = String.try_convert(string)
|
|
295
363
|
string ? STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET.match?(string) : false
|
|
296
364
|
end
|
|
297
365
|
|
|
@@ -350,37 +418,36 @@ class ULID
|
|
|
350
418
|
# @raise [OverflowError] if the given value is larger than the ULID limit
|
|
351
419
|
# @raise [ArgumentError] if the given milliseconds and/or entropy is negative number
|
|
352
420
|
def self.from_milliseconds_and_entropy(milliseconds:, entropy:)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
integer = (n32encoded_timestamp + n32encoded_randomness).to_i(32)
|
|
361
|
-
|
|
362
|
-
new(milliseconds: milliseconds, entropy: entropy, integer: integer)
|
|
421
|
+
n32_encoded = encode_n32(milliseconds: milliseconds, entropy: entropy)
|
|
422
|
+
new(
|
|
423
|
+
milliseconds: milliseconds,
|
|
424
|
+
entropy: entropy,
|
|
425
|
+
integer: n32_encoded.to_i(32),
|
|
426
|
+
encoded: CrockfordBase32.from_n32(n32_encoded).upcase.freeze
|
|
427
|
+
)
|
|
363
428
|
end
|
|
364
429
|
|
|
365
|
-
# @dynamic milliseconds, entropy
|
|
366
430
|
attr_reader(:milliseconds, :entropy)
|
|
367
431
|
|
|
368
432
|
# @api private
|
|
369
433
|
# @param [Integer] milliseconds
|
|
370
434
|
# @param [Integer] entropy
|
|
371
435
|
# @param [Integer] integer
|
|
436
|
+
# @param [String] encoded
|
|
372
437
|
# @return [void]
|
|
373
|
-
def initialize(milliseconds:, entropy:, integer:)
|
|
438
|
+
def initialize(milliseconds:, entropy:, integer:, encoded:)
|
|
374
439
|
# All arguments check should be done with each constructors, not here
|
|
375
440
|
@integer = integer
|
|
441
|
+
@encoded = encoded
|
|
376
442
|
@milliseconds = milliseconds
|
|
377
443
|
@entropy = entropy
|
|
378
444
|
end
|
|
379
445
|
|
|
380
446
|
# @return [String]
|
|
381
|
-
def
|
|
382
|
-
@
|
|
447
|
+
def encode
|
|
448
|
+
@encoded
|
|
383
449
|
end
|
|
450
|
+
alias_method(:to_s, :encode)
|
|
384
451
|
|
|
385
452
|
# @return [Integer]
|
|
386
453
|
def to_i
|
|
@@ -392,21 +459,20 @@ class ULID
|
|
|
392
459
|
[ULID, @integer].hash
|
|
393
460
|
end
|
|
394
461
|
|
|
395
|
-
# @return [
|
|
462
|
+
# @return [-1, 0, 1, nil]
|
|
396
463
|
def <=>(other)
|
|
397
464
|
(ULID === other) ? (@integer <=> other.to_i) : nil
|
|
398
465
|
end
|
|
399
466
|
|
|
400
467
|
# @return [String]
|
|
401
468
|
def inspect
|
|
402
|
-
@inspect ||= "ULID(#{to_time.strftime(TIME_FORMAT_IN_INSPECT)}: #{
|
|
469
|
+
@inspect ||= "ULID(#{to_time.strftime(TIME_FORMAT_IN_INSPECT)}: #{@encoded})".freeze
|
|
403
470
|
end
|
|
404
471
|
|
|
405
472
|
# @return [Boolean]
|
|
406
473
|
def eql?(other)
|
|
407
474
|
equal?(other) || (ULID === other && @integer == other.to_i)
|
|
408
475
|
end
|
|
409
|
-
# @dynamic ==
|
|
410
476
|
alias_method(:==, :eql?)
|
|
411
477
|
|
|
412
478
|
# Return `true` for same value of ULID, variant formats of strings, same Time in ULID precision(msec).
|
|
@@ -421,11 +487,9 @@ class ULID
|
|
|
421
487
|
@integer == other.to_i
|
|
422
488
|
when String
|
|
423
489
|
begin
|
|
424
|
-
|
|
490
|
+
to_i == ULID.parse_variant_format(other).to_i
|
|
425
491
|
rescue Exception
|
|
426
492
|
false
|
|
427
|
-
else
|
|
428
|
-
to_s == normalized
|
|
429
493
|
end
|
|
430
494
|
when Time
|
|
431
495
|
to_time == ULID.floor(other)
|
|
@@ -460,12 +524,12 @@ class ULID
|
|
|
460
524
|
|
|
461
525
|
# @return [String]
|
|
462
526
|
def timestamp
|
|
463
|
-
@timestamp ||= (
|
|
527
|
+
@timestamp ||= (@encoded.slice(0, TIMESTAMP_ENCODED_LENGTH).freeze || raise(UnexpectedError))
|
|
464
528
|
end
|
|
465
529
|
|
|
466
530
|
# @return [String]
|
|
467
531
|
def randomness
|
|
468
|
-
@randomness ||= (
|
|
532
|
+
@randomness ||= (@encoded.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH).freeze || raise(UnexpectedError))
|
|
469
533
|
end
|
|
470
534
|
|
|
471
535
|
# @note Providing for rough operations. The keys and values is not fixed.
|
|
@@ -491,7 +555,6 @@ class ULID
|
|
|
491
555
|
ULID.from_integer(succ_int)
|
|
492
556
|
end
|
|
493
557
|
end
|
|
494
|
-
# @dynamic next
|
|
495
558
|
alias_method(:next, :succ)
|
|
496
559
|
|
|
497
560
|
# @return [ULID, nil] when called on ULID as `00000000000000000000000000`, returns `nil` instead of ULID
|
|
@@ -526,7 +589,7 @@ class ULID
|
|
|
526
589
|
# @return [void]
|
|
527
590
|
def marshal_load(integer)
|
|
528
591
|
unmarshaled = ULID.from_integer(integer)
|
|
529
|
-
initialize(integer: unmarshaled.to_i, milliseconds: unmarshaled.milliseconds, entropy: unmarshaled.entropy)
|
|
592
|
+
initialize(integer: unmarshaled.to_i, milliseconds: unmarshaled.milliseconds, entropy: unmarshaled.entropy, encoded: unmarshaled.to_s)
|
|
530
593
|
end
|
|
531
594
|
|
|
532
595
|
# @return [self]
|
|
@@ -556,12 +619,9 @@ class ULID
|
|
|
556
619
|
end
|
|
557
620
|
end
|
|
558
621
|
|
|
559
|
-
require_relative('ulid/version')
|
|
560
|
-
require_relative('ulid/crockford_base32')
|
|
561
|
-
require_relative('ulid/monotonic_generator')
|
|
562
622
|
require_relative('ulid/ractor_unshareable_constants')
|
|
563
623
|
|
|
564
624
|
class ULID
|
|
565
625
|
# 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
|
|
626
|
+
private_constant(:TIME_FORMAT_IN_INSPECT, :MIN, :MAX, :RANDOM_INTEGER_GENERATOR)
|
|
567
627
|
end
|
data/sig/ulid.rbs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
class ULID < Object
|
|
2
2
|
VERSION: String
|
|
3
|
-
CROCKFORD_BASE32_ENCODING_STRING: String
|
|
4
3
|
TIMESTAMP_ENCODED_LENGTH: 10
|
|
5
4
|
RANDOMNESS_ENCODED_LENGTH: 16
|
|
6
5
|
ENCODED_LENGTH: 26
|
|
@@ -39,12 +38,11 @@ class ULID < Object
|
|
|
39
38
|
class SetupError < UnexpectedError
|
|
40
39
|
end
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
VARIANT_PATTERN: Regexp
|
|
41
|
+
ENCODING_STRING: String
|
|
42
|
+
CROCKFORD_TR_PATTERN: String
|
|
43
|
+
BASE32_TR_PATTERN: String
|
|
44
|
+
VARIANT_TR_PATTERN: String
|
|
45
|
+
NORMALIZED_TR_PATTERN: String
|
|
48
46
|
|
|
49
47
|
# A private API. Should not be used in your code.
|
|
50
48
|
def self.encode: (Integer integer) -> String
|
|
@@ -54,6 +52,9 @@ class ULID < Object
|
|
|
54
52
|
|
|
55
53
|
# A private API. Should not be used in your code.
|
|
56
54
|
def self.normalize: (String string) -> String
|
|
55
|
+
|
|
56
|
+
# A private API. Should not be used in your code.
|
|
57
|
+
def self.from_n32: (String n32encoded) -> String
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
class MonotonicGenerator
|
|
@@ -78,6 +79,9 @@ class ULID < Object
|
|
|
78
79
|
# The `Thread-safety` is implemented with [Monitor](https://bugs.ruby-lang.org/issues/16255)
|
|
79
80
|
def generate: (?moment: moment) -> ULID
|
|
80
81
|
|
|
82
|
+
# Just providing similar api as `ULID.generate` and `ULID.encode` relation. No performance benefit exists in monotonic generator's one.
|
|
83
|
+
def encode: (?moment: moment) -> String
|
|
84
|
+
|
|
81
85
|
# Returned value is `basically not` Thread-safety
|
|
82
86
|
# If you want to keep Thread-safety, keep to call {#generate} only in same {#synchronize} block
|
|
83
87
|
#
|
|
@@ -102,8 +106,8 @@ class ULID < Object
|
|
|
102
106
|
type randomness_octets = [Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer] | Array[Integer]
|
|
103
107
|
type period = Range[Time] | Range[nil] | Range[ULID]
|
|
104
108
|
|
|
105
|
-
@string: String?
|
|
106
109
|
@integer: Integer
|
|
110
|
+
@encoded: String
|
|
107
111
|
@timestamp: String?
|
|
108
112
|
@randomness: String?
|
|
109
113
|
@inspect: String?
|
|
@@ -145,6 +149,16 @@ class ULID < Object
|
|
|
145
149
|
#
|
|
146
150
|
def self.generate: (?moment: moment, ?entropy: Integer) -> ULID
|
|
147
151
|
|
|
152
|
+
# Retuns encoded and normalzied String.
|
|
153
|
+
# It has same arguments signatures as `.generate`. So can be used for just ID creation usecases without needless object creation.
|
|
154
|
+
#
|
|
155
|
+
# NOTE: Difference of ULID#encode, returned String is NOT frozen.
|
|
156
|
+
#
|
|
157
|
+
def self.encode: (?moment: moment, ?entropy: Integer) -> String
|
|
158
|
+
|
|
159
|
+
# A private API. Should not be used in your code.
|
|
160
|
+
def self.encode_n32: (milliseconds: Integer, entropy: Integer) -> String
|
|
161
|
+
|
|
148
162
|
# Shorthand of `ULID.generate(moment: Time)`
|
|
149
163
|
# See also [ULID.generate](https://kachick.github.io/ruby-ulid/ULID.html#generate-class_method)
|
|
150
164
|
#
|
|
@@ -205,7 +219,7 @@ class ULID < Object
|
|
|
205
219
|
# ```
|
|
206
220
|
def self.floor: (Time time) -> Time
|
|
207
221
|
|
|
208
|
-
#
|
|
222
|
+
# Return ULID instance from encoded String.
|
|
209
223
|
#
|
|
210
224
|
# ```ruby
|
|
211
225
|
# ulid = ULID.parse('01ARZ3NDEKTSV4RRFFQ69G5FAV')
|
|
@@ -213,6 +227,32 @@ class ULID < Object
|
|
|
213
227
|
# ```
|
|
214
228
|
def self.parse: (_ToStr string) -> ULID
|
|
215
229
|
|
|
230
|
+
# Return Time instance from encoded String.
|
|
231
|
+
# See also `ULID.encode` for similar purpose.
|
|
232
|
+
#
|
|
233
|
+
# NOTE: Difference of ULID#to_time, returned Time is NOT frozen.
|
|
234
|
+
#
|
|
235
|
+
# ```ruby
|
|
236
|
+
# time = ULID.decode_time('01ARZ3NDEKTSV4RRFFQ69G5FAV')
|
|
237
|
+
# #=> 2016-07-30 23:54:10.259 UTC
|
|
238
|
+
# ```
|
|
239
|
+
def self.decode_time: (_ToStr string, ?in: String | Integer | nil) -> Time
|
|
240
|
+
|
|
241
|
+
# Get ULID instance from unnormalized String that encoded in Crockford's base32.
|
|
242
|
+
#
|
|
243
|
+
# http://www.crockford.com/base32.html
|
|
244
|
+
#
|
|
245
|
+
# * Ignore Hyphens (-)
|
|
246
|
+
# * Mapping 0 O o => 0, 1 I i L l => 1
|
|
247
|
+
#
|
|
248
|
+
# ```ruby
|
|
249
|
+
# ulid = ULID.parse_variant_format('01G70Y0Y7G-ZLXWDIREXERGSDoD')
|
|
250
|
+
# #=> ULID(2022-07-03 02:25:22.672 UTC: 01G70Y0Y7GZ1XWD1REXERGSD0D)
|
|
251
|
+
# ```
|
|
252
|
+
#
|
|
253
|
+
# See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
|
|
254
|
+
def self.parse_variant_format: (_ToStr string) -> ULID
|
|
255
|
+
|
|
216
256
|
# ```ruby
|
|
217
257
|
# # Currently experimental feature, so needed to load the extension.
|
|
218
258
|
# require 'ulid/uuid'
|
|
@@ -309,14 +349,13 @@ class ULID < Object
|
|
|
309
349
|
# ```
|
|
310
350
|
def self.sample: (?period: period) -> ULID
|
|
311
351
|
| (Integer number, ?period: period?) -> Array[ULID]
|
|
312
|
-
def self.valid?: (untyped) -> bool
|
|
313
352
|
|
|
314
353
|
# Returns normalized string
|
|
315
354
|
#
|
|
316
355
|
# ```ruby
|
|
356
|
+
# ULID.normalize('01G70Y0Y7G-Z1XWDAREXERGSDDD') #=> "01G70Y0Y7GZ1XWDAREXERGSDDD"
|
|
317
357
|
# ULID.normalize('-olarz3-noekisv4rrff-q6ig5fav--') #=> "01ARZ3N0EK1SV4RRFFQ61G5FAV"
|
|
318
|
-
# ULID.
|
|
319
|
-
# ULID.normalized?('01ARZ3N0EK1SV4RRFFQ61G5FAV') #=> true
|
|
358
|
+
# ULID.normalize('01G70Y0Y7G_Z1XWDAREXERGSDDD') #=> ULID::ParserError
|
|
320
359
|
# ```
|
|
321
360
|
#
|
|
322
361
|
# See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
|
|
@@ -325,13 +364,40 @@ class ULID < Object
|
|
|
325
364
|
# Returns `true` if it is normalized string
|
|
326
365
|
#
|
|
327
366
|
# ```ruby
|
|
328
|
-
# ULID.
|
|
329
|
-
# ULID.normalized?('-
|
|
330
|
-
# ULID.normalized?(
|
|
367
|
+
# ULID.normalized?('01G70Y0Y7GZ1XWDAREXERGSDDD') #=> true
|
|
368
|
+
# ULID.normalized?('01G70Y0Y7G-Z1XWDAREXERGSDDD') #=> false
|
|
369
|
+
# ULID.normalized?(ULID.generate.to_s.downcase) #=> false
|
|
370
|
+
# ULID.normalized?('01G70Y0Y7G_Z1XWDAREXERGSDDD') #=> false (Not raising ULID::ParserError)
|
|
371
|
+
# ```
|
|
372
|
+
#
|
|
373
|
+
# See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
|
|
374
|
+
def self.normalized?: (_ToStr string) -> bool
|
|
375
|
+
| (untyped) -> false
|
|
376
|
+
|
|
377
|
+
# Returns `true` if it is valid in ULID format variants
|
|
378
|
+
#
|
|
379
|
+
# ```ruby
|
|
380
|
+
# ULID.valid_as_variant_format?(ULID.generate.to_s.downcase) #=> true
|
|
381
|
+
# ULID.valid_as_variant_format?('01G70Y0Y7G-Z1XWDAREXERGSDDD') #=> true
|
|
382
|
+
# ULID.valid_as_variant_format?('01G70Y0Y7G_Z1XWDAREXERGSDDD') #=> false
|
|
331
383
|
# ```
|
|
332
384
|
#
|
|
333
385
|
# See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
|
|
334
|
-
def self.
|
|
386
|
+
def self.valid_as_variant_format?: (_ToStr string) -> bool
|
|
387
|
+
| (untyped) -> false
|
|
388
|
+
|
|
389
|
+
# DEPRECATED Use valid_as_variant_format? instead
|
|
390
|
+
#
|
|
391
|
+
# Returns `true` if it is normalized string.
|
|
392
|
+
# Basically the difference of normalized? is to accept downcase or not. This returns true for downcased ULIDs.
|
|
393
|
+
#
|
|
394
|
+
# ```ruby
|
|
395
|
+
# ULID.valid?(ULID.generate.to_s.downcase) #=> true
|
|
396
|
+
# ```
|
|
397
|
+
#
|
|
398
|
+
# See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
|
|
399
|
+
def self.valid?: (_ToStr string) -> bool
|
|
400
|
+
| (untyped) -> false
|
|
335
401
|
|
|
336
402
|
# Returns parsed ULIDs from given String for rough operations.
|
|
337
403
|
#
|
|
@@ -389,9 +455,12 @@ class ULID < Object
|
|
|
389
455
|
# ```ruby
|
|
390
456
|
# ulid = ULID.generate
|
|
391
457
|
# #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
|
392
|
-
# ulid.
|
|
458
|
+
# ulid.encode #=> "01F4A5Y1YAQCYAYCTC7GRMJ9AA"
|
|
459
|
+
# ulid.encode.frozen? #=> true
|
|
460
|
+
# ulid.encode.equal?(ulid.to_s) #=> true
|
|
393
461
|
# ```
|
|
394
|
-
def
|
|
462
|
+
def encode: -> String
|
|
463
|
+
alias to_s encode
|
|
395
464
|
|
|
396
465
|
# ```ruby
|
|
397
466
|
# ulid = ULID.generate
|
|
@@ -411,7 +480,12 @@ class ULID < Object
|
|
|
411
480
|
# #=> true
|
|
412
481
|
# ```
|
|
413
482
|
#
|
|
414
|
-
# 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.
|
|
483
|
+
# 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.
|
|
484
|
+
# So preferable than `lexicographically sortable` in actual case.
|
|
485
|
+
#
|
|
486
|
+
# This returns -1 | 0 | 1 for ULIDs. However defined as returning Integer. It is caused on ruby/rbs current definition.
|
|
487
|
+
# https://github.com/ruby/ruby/blob/cd34f56d450f2310cceaf4c5f34d23eddfda58e8/numeric.c#L4646-L4660
|
|
488
|
+
# https://github.com/ruby/rbs/blob/14abbbae8885a09a2ed82de2ef31d67a9c0a108d/core/integer.rbs#L461-L462
|
|
415
489
|
#
|
|
416
490
|
def <=>: (ULID other) -> Integer
|
|
417
491
|
| (untyped other) -> nil
|
|
@@ -535,9 +609,6 @@ class ULID < Object
|
|
|
535
609
|
|
|
536
610
|
private
|
|
537
611
|
|
|
538
|
-
# A private API. Should not be used in your code.
|
|
539
|
-
def self.new: (milliseconds: Integer, entropy: Integer, integer: Integer) -> ULID
|
|
540
|
-
|
|
541
612
|
# A private API. Should not be used in your code.
|
|
542
613
|
def self.reasonable_entropy: -> Integer
|
|
543
614
|
|
|
@@ -548,7 +619,7 @@ class ULID < Object
|
|
|
548
619
|
def self.safe_get_class_name: (untyped object) -> String
|
|
549
620
|
|
|
550
621
|
# A private API. Should not be used in your code.
|
|
551
|
-
def initialize: (milliseconds: Integer, entropy: Integer, integer: Integer) -> void
|
|
622
|
+
def initialize: (milliseconds: Integer, entropy: Integer, integer: Integer, encoded: String) -> void
|
|
552
623
|
|
|
553
624
|
# A private API. Should not be used in your code.
|
|
554
625
|
def cache_all_instance_variables: -> void
|
metadata
CHANGED
|
@@ -1,17 +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.6.1
|
|
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-07-
|
|
11
|
+
date: 2022-07-18 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
|
-
description: " generator,
|
|
14
|
-
|
|
13
|
+
description: " generator, optional monotonicity, parser and tools for ULID (RBS
|
|
14
|
+
included)\n"
|
|
15
15
|
email:
|
|
16
16
|
- kachick1+ruby@gmail.com
|
|
17
17
|
executables: []
|
|
@@ -23,6 +23,7 @@ files:
|
|
|
23
23
|
- lib/ruby-ulid.rb
|
|
24
24
|
- lib/ulid.rb
|
|
25
25
|
- lib/ulid/crockford_base32.rb
|
|
26
|
+
- lib/ulid/errors.rb
|
|
26
27
|
- lib/ulid/monotonic_generator.rb
|
|
27
28
|
- lib/ulid/ractor_unshareable_constants.rb
|
|
28
29
|
- lib/ulid/uuid.rb
|