ruby-ulid 0.1.1 → 0.1.6
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/{LICENSE → LICENSE.txt} +0 -0
- data/README.md +40 -83
- data/lib/ruby-ulid.rb +6 -0
- data/lib/ulid.rb +69 -22
- data/lib/ulid/crockford_base32.rb +25 -1
- data/lib/ulid/monotonic_generator.rb +19 -12
- data/lib/ulid/uuid.rb +3 -2
- data/lib/ulid/version.rb +1 -1
- data/sig/ulid.rbs +433 -19
- metadata +7 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6e67f6449c010a7c85ff8050a081b21e824ed876d7695a4fa22beb25f2a62534
|
4
|
+
data.tar.gz: f0b0607131f8b04dd57c587746cd992a9a01070cacb53857fca6efb7dd11fe45
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5c1e3540e8e4f75c92fbc8347d2898fe8f8f233830a878c67462f8f9c9f065c969a9df8dd93e11bd27baa7a5cb7c1cb1eaf2fdf1e7dba5f5833e6295cd9d083f
|
7
|
+
data.tar.gz: cd70d28d9cba7270ddf61dcf46bba1c4a04328d828ba7ed32a39c68c01f460b36f9f85da74fdb1d1b742309fdffb29d118cc8bc238c1744b61dbdc3a32255475
|
data/{LICENSE → LICENSE.txt}
RENAMED
File without changes
|
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
|
@@ -28,7 +28,7 @@ Instead, herein is proposed ULID:
|
|
28
28
|
- 1.21e+24 unique ULIDs per millisecond
|
29
29
|
- Lexicographically sortable!
|
30
30
|
- Canonically encoded as a 26 character string, as opposed to the 36 character UUID
|
31
|
-
- Uses [Crockford's base32](https://www.crockford.com/base32.html) for better efficiency and readability (5 bits per character)
|
31
|
+
- Uses [Crockford's base32](https://www.crockford.com/base32.html) for better efficiency and readability (5 bits per character)
|
32
32
|
- Case insensitive
|
33
33
|
- No special characters (URL safe)
|
34
34
|
- Monotonic sort order (correctly detects and handles the same millisecond)
|
@@ -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.1.
|
52
|
+
gem 'ruby-ulid', '>= 0.1.6', '< 0.2.0'
|
53
53
|
```
|
54
54
|
|
55
55
|
### Generator and Parser
|
@@ -146,7 +146,7 @@ sample_ulids_by_the_time.take(5) #=>
|
|
146
146
|
ulids.sort == ulids #=> true
|
147
147
|
```
|
148
148
|
|
149
|
-
Same generator does not generate duplicated ULIDs even in multi threads environment. It is implemented with [
|
149
|
+
Same generator does not generate duplicated ULIDs even in multi threads environment. It is implemented with [Monitor](https://bugs.ruby-lang.org/issues/16255)
|
150
150
|
|
151
151
|
### Filtering IDs with `Time`
|
152
152
|
|
@@ -170,7 +170,7 @@ exclude_end = ULID.range(time1...time2) #=> The end of `Range[ULID]` will be the
|
|
170
170
|
|
171
171
|
# Below patterns are acceptable
|
172
172
|
pinpointing = ULID.range(time1..time1) #=> This will match only for all IDs in `time1`
|
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)
|
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)
|
174
174
|
until_the_end = ULID.range(ULID.min.to_time..time1) #=> This is same as above for Ruby 2.6
|
175
175
|
until_the_ulid_limit = ULID.range(time1..) # This will match only for all IDs from `time1` to max value of the ULID limit
|
176
176
|
|
@@ -192,7 +192,7 @@ ULID.floor(time) #=> 2000-01-01 00:00:00.123 UTC
|
|
192
192
|
For rough operations, `ULID.scan` might be useful.
|
193
193
|
|
194
194
|
```ruby
|
195
|
-
json
|
195
|
+
json = <<'JSON'
|
196
196
|
{
|
197
197
|
"id": "01F4GNAV5ZR6FJQ5SFQC7WDSY3",
|
198
198
|
"author": {
|
@@ -217,7 +217,7 @@ json =<<'EOD'
|
|
217
217
|
}
|
218
218
|
]
|
219
219
|
}
|
220
|
-
|
220
|
+
JSON
|
221
221
|
|
222
222
|
ULID.scan(json).to_a
|
223
223
|
#=>
|
@@ -313,7 +313,7 @@ ulids.take(10)
|
|
313
313
|
# ULID(2021-04-29 03:18:24.152 UTC: 01F4DT4Z4RA0QV8WFQGRAG63EH),
|
314
314
|
# ULID(2021-05-02 13:27:16.394 UTC: 01F4PM605ABF5SDVMEHBH8JJ9R)]
|
315
315
|
ULID.sample(10, period: ulid1.to_time..ulid2.to_time)
|
316
|
-
#=>
|
316
|
+
#=>
|
317
317
|
# [ULID(2021-04-29 06:44:41.513 UTC: 01F4E5YPD9XQ3MYXWK8ZJKY8SW),
|
318
318
|
# ULID(2021-05-01 00:35:06.629 UTC: 01F4JNKD85SVK1EAEYSJGF53A2),
|
319
319
|
# ULID(2021-05-02 12:45:28.408 UTC: 01F4PHSEYRG9BWBEWMRW1XE6WW),
|
@@ -326,6 +326,34 @@ ULID.sample(10, period: ulid1.to_time..ulid2.to_time)
|
|
326
326
|
# ULID(2021-04-28 15:05:06.808 UTC: 01F4CG68ZRST94T056KRZ5K9S4)]
|
327
327
|
```
|
328
328
|
|
329
|
+
### ULID specification ambiguity around orthographical variants of the format
|
330
|
+
|
331
|
+
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
|
+
|
333
|
+
>Uses Crockford's base32 for better efficiency and readability (5 bits per character)
|
334
|
+
|
335
|
+
The original `Crockford's base32` maps `I`, `L` to `1`, `O` to `0`.
|
336
|
+
And accepts freestyle inserting `Hyphens (-)`.
|
337
|
+
To consider this patterns or not is different in each implementations.
|
338
|
+
|
339
|
+
Current parser/validator/matcher aims to cover `subset of Crockford's base32`.
|
340
|
+
I have suggested it would be clarified in [ulid/spec#57](https://github.com/ulid/spec/pull/57).
|
341
|
+
|
342
|
+
>Case insensitive
|
343
|
+
|
344
|
+
I can understand it might be considered in actual use-case.
|
345
|
+
But it is a controversial point, discussing in [ulid/spec#3](https://github.com/ulid/spec/issues/3).
|
346
|
+
|
347
|
+
Be that as it may, this gem provides API for handling the nasty possibilities.
|
348
|
+
|
349
|
+
`ULID.normalize` and `ULID.normalized?`
|
350
|
+
|
351
|
+
```ruby
|
352
|
+
ULID.normalize('-olarz3-noekisv4rrff-q6ig5fav--') #=> "01ARZ3N0EK1SV4RRFFQ61G5FAV"
|
353
|
+
ULID.normalized?('-olarz3-noekisv4rrff-q6ig5fav--') #=> false
|
354
|
+
ULID.normalized?('01ARZ3N0EK1SV4RRFFQ61G5FAV') #=> true
|
355
|
+
```
|
356
|
+
|
329
357
|
### UUIDv4 converter for migration use-cases
|
330
358
|
|
331
359
|
`ULID.from_uuidv4` and `ULID#to_uuidv4` is the converter.
|
@@ -335,7 +363,7 @@ The imported timestamp is meaningless. So ULID's benefit will lost.
|
|
335
363
|
# Currently experimental feature, so needed to load the extension.
|
336
364
|
require 'ulid/uuid'
|
337
365
|
|
338
|
-
# Basically reversible
|
366
|
+
# Basically reversible
|
339
367
|
ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39') #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
|
340
368
|
ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
|
341
369
|
|
@@ -403,81 +431,11 @@ NOTE: It is still having precision issue similar as `ulid gem` in the both gener
|
|
403
431
|
1. [Fix to handle timestamp precision in parser](https://github.com/abachman/ulid-ruby/pull/5)
|
404
432
|
1. [Fix to handle timestamp precision in generator](https://github.com/abachman/ulid-ruby/pull/4)
|
405
433
|
|
406
|
-
###
|
407
|
-
|
408
|
-
This runs rough benchmarks
|
409
|
-
|
410
|
-
```console
|
411
|
-
$ rake benchmark_with_other_gems
|
412
|
-
(Do not use `bundle exec`!)
|
413
|
-
```
|
434
|
+
### Compare performance with them
|
414
435
|
|
415
|
-
|
416
|
-
<summary>One of the result at 2021/05/10 on my machine</summary>
|
417
|
-
|
418
|
-
```plaintext
|
419
|
-
#### rafaelsales - ulid
|
420
|
-
cd ./benchmark/compare_with_othergems/rafaelsales && bundle install --quiet && bundle exec ruby -v ./generate.rb
|
421
|
-
ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
|
422
|
-
Warming up --------------------------------------
|
423
|
-
ULID.generate 5.560k i/100ms
|
424
|
-
Calculating -------------------------------------
|
425
|
-
ULID.generate 52.655k (±11.0%) i/s - 261.320k in 5.029719s
|
426
|
-
"`ulid gem - 1.3.0` generated products: 371927 - sample: [\"01F59Y97807D2S67KE6X7ATK7Z\", \"01F59Y9AVRQJFAT5M2N7Z72BVF\", \"01F59Y95Z1042X4Z1K9729BSE3\", \"01F59Y95ZMVDFKD63Y8TT145GQ\", \"01F59Y94YQEZ3PH5STZ8PS1JPG\"]"
|
427
|
-
------------------------------------------------------------------------
|
428
|
-
#### abachman - ulid-ruby
|
429
|
-
cd ./benchmark/compare_with_othergems/abachman && bundle install --quiet && bundle exec ruby -v ./generate.rb
|
430
|
-
ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
|
431
|
-
Warming up --------------------------------------
|
432
|
-
ULID.generate 3.862k i/100ms
|
433
|
-
Calculating -------------------------------------
|
434
|
-
ULID.generate 38.415k (±13.1%) i/s - 189.238k in 5.025788s
|
435
|
-
"`ulid-ruby gem - 1.0.0` generated products: 260625 - sample: [\"01F59Y9H9V17EPXTYNZDCXB9EZ\", \"01F59Y9J4S4XZ68MF5DJDWHTAC\", \"01F59Y9J8887VC8E850QSBDCDX\", \"01F59Y9JEJPD088EYXVHB86W3N\", \"01F59Y9GGAZFXGCB92EQD695CZ\"]"
|
436
|
-
------------------------------------------------------------------------
|
437
|
-
#### kachick - ruby-ulid(This one)
|
438
|
-
cd ./benchmark/compare_with_othergems/kachick && bundle install --quiet && bundle exec ruby -v ./generate.rb
|
439
|
-
ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
|
440
|
-
Warming up --------------------------------------
|
441
|
-
ULID.generate.to_s 3.185k i/100ms
|
442
|
-
Calculating -------------------------------------
|
443
|
-
ULID.generate.to_s 31.934k (± 9.1%) i/s - 159.250k in 5.030707s
|
444
|
-
"`ruby-ulid gem (this one) - 0.1.0` generated products: 223867 - sample: [\"01F59Y9SPZHM6JCTYP50CHGVAX\", \"01F59Y9VB7X0SX32MMKF78KJR3\", \"01F59Y9W0C83RYCNYVH84R4JG3\", \"01F59Y9V218Q3D4YP3W74ET3EW\", \"01F59Y9X6DD8NX99WBGCR7RNXF\"]"
|
445
|
-
```
|
446
|
-
|
447
|
-
In another execution, Changed as below. So there doesn't seem to be a big difference.
|
448
|
-
|
449
|
-
```plaintext
|
450
|
-
#### rafaelsales - ulid
|
451
|
-
cd ./benchmark/compare_with_othergems/rafaelsales && bundle install --quiet && bundle exec ruby -v ./generate.rb
|
452
|
-
ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
|
453
|
-
Warming up --------------------------------------
|
454
|
-
ULID.generate 2.473k i/100ms
|
455
|
-
Calculating -------------------------------------
|
456
|
-
ULID.generate 24.101k (±15.9%) i/s - 118.704k in 5.066190s
|
457
|
-
"`ulid gem - 1.3.0` generated products: 164763 - sample: [\"01F59YEGPFMXXZWC1YQ49TSK8Y\", \"01F59YEFF7VX5WAW91VTCSE2N9\", \"01F59YEEZ5P9428SDYEDYW8D27\", \"01F59YEHVK56DZBJSNSQK6V1W6\", \"01F59YEHE07M98PVV97ABBAKHM\"]"
|
458
|
-
------------------------------------------------------------------------
|
459
|
-
#### abachman - ulid-ruby
|
460
|
-
cd ./benchmark/compare_with_othergems/abachman && bundle install --quiet && bundle exec ruby -v ./generate.rb
|
461
|
-
ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
|
462
|
-
Warming up --------------------------------------
|
463
|
-
ULID.generate 2.620k i/100ms
|
464
|
-
Calculating -------------------------------------
|
465
|
-
ULID.generate 27.571k (±14.2%) i/s - 136.240k in 5.056272s
|
466
|
-
"`ulid-ruby gem - 1.0.0` generated products: 186683 - sample: [\"01F59YEVX6GC9TC0RCZ74RC6Z3\", \"01F59YESJXWYGZ61TXHKRVKS97\", \"01F59YEVQ4QQKBED5T49RTV1MA\", \"01F59YEPJ6MMZY1N63DNW7C4SN\", \"01F59YEQK52K8TKTP1ESC6VC5X\"]"
|
467
|
-
------------------------------------------------------------------------
|
468
|
-
#### kachick - ruby-ulid(This one)
|
469
|
-
cd ./benchmark/compare_with_othergems/kachick && bundle install --quiet && bundle exec ruby -v ./generate.rb
|
470
|
-
ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
|
471
|
-
Warming up --------------------------------------
|
472
|
-
ULID.generate.to_s 3.014k i/100ms
|
473
|
-
Calculating -------------------------------------
|
474
|
-
ULID.generate.to_s 31.612k (±10.1%) i/s - 156.728k in 5.013432s
|
475
|
-
"`ruby-ulid gem (this one) - 0.1.0` generated products: 212293 - sample: [\"01F59YF1WP49TT4GQPDN3E9JTJ\", \"01F59YF1MW1ZDQW93NX4J6RG4G\", \"01F59YF0KRX2CZKHDQQSN5HXHW\", \"01F59YEZVNH8YHP4ZHDK2ZRWSR\", \"01F59YF1J0FV3CVV099SHA2Q9A\"]"
|
476
|
-
```
|
436
|
+
See [Benchmark](https://github.com/kachick/ruby-ulid/wiki/Benchmark).
|
477
437
|
|
478
|
-
|
479
|
-
So I think the results are acceptable.
|
480
|
-
</details>
|
438
|
+
The results are not something to be proud of.
|
481
439
|
|
482
440
|
## References
|
483
441
|
|
@@ -488,4 +446,3 @@ So I think the results are acceptable.
|
|
488
446
|
## Note
|
489
447
|
|
490
448
|
- Another choices for sortable and randomness IDs, [UUIDv6, UUIDv7, UUIDv8 might be the one. (But they are still in draft state)](https://www.ietf.org/archive/id/draft-peabody-dispatch-new-uuid-format-01.html), I will track them in [ruby-ulid#37](https://github.com/kachick/ruby-ulid/issues/37)
|
491
|
-
- Current parser/validator/matcher aims to cover `subset of Crockford's base32`. Suggesting it in [ulid/spec#57](https://github.com/ulid/spec/pull/57). Be that as it may, I might provide special handler or converter for the exception in [ruby-ulid#57](https://github.com/kachick/ruby-ulid/issues/57) and/or [ruby-ulid#78](https://github.com/kachick/ruby-ulid/issues/78)
|
data/lib/ruby-ulid.rb
ADDED
data/lib/ulid.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# coding: us-ascii
|
2
2
|
# frozen_string_literal: true
|
3
|
+
|
3
4
|
# Copyright (C) 2021 Kenichi Kamiya
|
4
5
|
|
5
6
|
require 'securerandom'
|
@@ -60,6 +61,7 @@ class ULID
|
|
60
61
|
# @return [ULID]
|
61
62
|
def self.at(time)
|
62
63
|
raise ArgumentError, 'ULID.at takes only `Time` instance' unless Time === time
|
64
|
+
|
63
65
|
from_milliseconds_and_entropy(milliseconds: milliseconds_from_time(time), entropy: reasonable_entropy)
|
64
66
|
end
|
65
67
|
|
@@ -91,19 +93,21 @@ class ULID
|
|
91
93
|
# * Do not take random generator for the arguments
|
92
94
|
# * Raising error instead of truncating elements for the given number
|
93
95
|
def self.sample(*args, period: nil)
|
94
|
-
int_generator =
|
95
|
-
|
96
|
-
|
96
|
+
int_generator = (
|
97
|
+
if period
|
98
|
+
ulid_range = range(period)
|
99
|
+
min, max, exclude_end = ulid_range.begin.to_i, ulid_range.end.to_i, ulid_range.exclude_end?
|
97
100
|
|
98
|
-
|
99
|
-
|
101
|
+
possibilities = (max - min) + (exclude_end ? 0 : 1)
|
102
|
+
raise ArgumentError, "given range `#{ulid_range.inspect}` does not have possibilities" unless possibilities.positive?
|
100
103
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
-> {
|
105
|
+
SecureRandom.random_number(possibilities) + min
|
106
|
+
}
|
107
|
+
else
|
108
|
+
RANDOM_INTEGER_GENERATOR
|
109
|
+
end
|
110
|
+
)
|
107
111
|
|
108
112
|
case args.size
|
109
113
|
when 0
|
@@ -134,6 +138,7 @@ class ULID
|
|
134
138
|
string = String.try_convert(string)
|
135
139
|
raise ArgumentError, 'ULID.scan takes only strings' unless string
|
136
140
|
return to_enum(__callee__, string) unless block_given?
|
141
|
+
|
137
142
|
string.scan(SCANNING_PATTERN) do |matched|
|
138
143
|
yield parse(matched)
|
139
144
|
end
|
@@ -156,7 +161,7 @@ class ULID
|
|
156
161
|
milliseconds = n32encoded_timestamp.to_i(32)
|
157
162
|
entropy = n32encoded_randomness.to_i(32)
|
158
163
|
|
159
|
-
new
|
164
|
+
new(milliseconds: milliseconds, entropy: entropy, integer: integer)
|
160
165
|
end
|
161
166
|
|
162
167
|
# @param [Range<Time>, Range<nil>, Range[ULID]] period
|
@@ -164,6 +169,7 @@ class ULID
|
|
164
169
|
# @raise [ArgumentError] if the given period is not a `Range[Time]`, `Range[nil]` or `Range[ULID]`
|
165
170
|
def self.range(period)
|
166
171
|
raise ArgumentError, 'ULID.range takes only `Range[Time]`, `Range[nil]` or `Range[ULID]`' unless Range === period
|
172
|
+
|
167
173
|
begin_element, end_element, exclude_end = period.begin, period.end, period.exclude_end?
|
168
174
|
return period if self === begin_element && self === end_element
|
169
175
|
|
@@ -180,11 +186,7 @@ class ULID
|
|
180
186
|
|
181
187
|
case end_element
|
182
188
|
when Time
|
183
|
-
|
184
|
-
end_ulid = min(end_element)
|
185
|
-
else
|
186
|
-
end_ulid = max(end_element)
|
187
|
-
end
|
189
|
+
end_ulid = exclude_end ? min(end_element) : max(end_element)
|
188
190
|
when nil
|
189
191
|
# The end should be max and include end, because nil end means to cover endless ULIDs until the limit
|
190
192
|
end_ulid = MAX
|
@@ -260,9 +262,29 @@ class ULID
|
|
260
262
|
end
|
261
263
|
|
262
264
|
# @param [String, #to_str] string
|
263
|
-
# @return [
|
264
|
-
|
265
|
+
# @return [String]
|
266
|
+
# @raise [ParserError] if the given format is not correct for ULID specs, even if ignored `orthographical variants of the format`
|
267
|
+
def self.normalize(string)
|
265
268
|
string = String.try_convert(string)
|
269
|
+
raise ArgumentError, 'ULID.normalize takes only strings' unless string
|
270
|
+
|
271
|
+
normalized_in_crockford = CrockfordBase32.normalize(string)
|
272
|
+
# Ensure the ULID correctness, because CrockfordBase32 does not always mean to satisfy ULID format
|
273
|
+
parse(normalized_in_crockford).to_s
|
274
|
+
end
|
275
|
+
|
276
|
+
# @return [Boolean]
|
277
|
+
def self.normalized?(object)
|
278
|
+
normalized = normalize(object)
|
279
|
+
rescue Exception
|
280
|
+
false
|
281
|
+
else
|
282
|
+
normalized == object
|
283
|
+
end
|
284
|
+
|
285
|
+
# @return [Boolean]
|
286
|
+
def self.valid?(object)
|
287
|
+
string = String.try_convert(object)
|
266
288
|
string ? STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET.match?(string) : false
|
267
289
|
end
|
268
290
|
|
@@ -290,8 +312,19 @@ class ULID
|
|
290
312
|
private_class_method def self.safe_get_class_name(object)
|
291
313
|
fallback = 'UnknownObject'
|
292
314
|
|
315
|
+
# This class getter implementation used https://github.com/rspec/rspec-support/blob/4ad8392d0787a66f9c351d9cf6c7618e18b3d0f2/lib/rspec/support.rb#L83-L89 as a reference, thank you!
|
316
|
+
# ref: https://twitter.com/_kachick/status/1400064896759304196
|
317
|
+
klass = (
|
318
|
+
begin
|
319
|
+
object.class
|
320
|
+
rescue NoMethodError
|
321
|
+
singleton_class = class << object; self; end
|
322
|
+
singleton_class.ancestors.detect { |ancestor| !ancestor.equal?(singleton_class) }
|
323
|
+
end
|
324
|
+
)
|
325
|
+
|
293
326
|
begin
|
294
|
-
name = String.try_convert(
|
327
|
+
name = String.try_convert(klass.name)
|
295
328
|
rescue Exception
|
296
329
|
fallback
|
297
330
|
else
|
@@ -315,7 +348,7 @@ class ULID
|
|
315
348
|
n32encoded_randomness = entropy.to_s(32).rjust(RANDOMNESS_ENCODED_LENGTH, '0')
|
316
349
|
integer = (n32encoded_timestamp + n32encoded_randomness).to_i(32)
|
317
350
|
|
318
|
-
new
|
351
|
+
new(milliseconds: milliseconds, entropy: entropy, integer: integer)
|
319
352
|
end
|
320
353
|
|
321
354
|
attr_reader :milliseconds, :entropy
|
@@ -386,7 +419,7 @@ class ULID
|
|
386
419
|
def octets
|
387
420
|
digits = @integer.digits(256)
|
388
421
|
(OCTETS_LENGTH - digits.size).times do
|
389
|
-
digits.push
|
422
|
+
digits.push(0)
|
390
423
|
end
|
391
424
|
digits.reverse!
|
392
425
|
end
|
@@ -457,6 +490,20 @@ class ULID
|
|
457
490
|
super
|
458
491
|
end
|
459
492
|
|
493
|
+
# @api private
|
494
|
+
# @return [Integer]
|
495
|
+
def marshal_dump
|
496
|
+
@integer
|
497
|
+
end
|
498
|
+
|
499
|
+
# @api private
|
500
|
+
# @param [Integer] integer
|
501
|
+
# @return [void]
|
502
|
+
def marshal_load(integer)
|
503
|
+
unmarshaled = ULID.from_integer(integer)
|
504
|
+
initialize(integer: unmarshaled.to_i, milliseconds: unmarshaled.milliseconds, entropy: unmarshaled.entropy)
|
505
|
+
end
|
506
|
+
|
460
507
|
# @return [self]
|
461
508
|
def to_ulid
|
462
509
|
self
|
@@ -1,9 +1,12 @@
|
|
1
1
|
# coding: us-ascii
|
2
2
|
# frozen_string_literal: true
|
3
|
+
|
3
4
|
# Copyright (C) 2021 Kenichi Kamiya
|
4
5
|
|
5
6
|
class ULID
|
6
|
-
#
|
7
|
+
# @see https://www.crockford.com/base32.html
|
8
|
+
#
|
9
|
+
# This module supporting only `subset of original crockford for actual use-case` in ULID context.
|
7
10
|
# Original decoding spec allows other characters.
|
8
11
|
# But I think ULID should allow `subset` of Crockford's Base32.
|
9
12
|
# See below
|
@@ -46,11 +49,24 @@ class ULID
|
|
46
49
|
end
|
47
50
|
end.freeze
|
48
51
|
raise SetupError, 'obvious bug exists in the mapping algorithm' unless N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys == crockford_base32_mappings.keys
|
52
|
+
|
49
53
|
CROCKFORD_BASE32_CHAR_PATTERN = /[#{N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys.join}]/.freeze
|
50
54
|
|
51
55
|
CROCKFORD_BASE32_CHAR_BY_N32_CHAR = N32_CHAR_BY_CROCKFORD_BASE32_CHAR.invert.freeze
|
52
56
|
N32_CHAR_PATTERN = /[#{CROCKFORD_BASE32_CHAR_BY_N32_CHAR.keys.join}]/.freeze
|
53
57
|
|
58
|
+
STANDARD_BY_VARIANT = {
|
59
|
+
'L' => '1',
|
60
|
+
'l' => '1',
|
61
|
+
'I' => '1',
|
62
|
+
'i' => '1',
|
63
|
+
'O' => '0',
|
64
|
+
'o' => '0',
|
65
|
+
'-' => ''
|
66
|
+
}.freeze
|
67
|
+
VARIANT_PATTERN = /[#{STANDARD_BY_VARIANT.keys.join}]/.freeze
|
68
|
+
|
69
|
+
# @api private
|
54
70
|
# @param [String] string
|
55
71
|
# @return [Integer]
|
56
72
|
def self.decode(string)
|
@@ -58,11 +74,19 @@ class ULID
|
|
58
74
|
n32encoded.to_i(32)
|
59
75
|
end
|
60
76
|
|
77
|
+
# @api private
|
61
78
|
# @param [Integer] integer
|
62
79
|
# @return [String]
|
63
80
|
def self.encode(integer)
|
64
81
|
n32encoded = integer.to_s(32)
|
65
82
|
n32encoded.upcase.gsub(N32_CHAR_PATTERN, CROCKFORD_BASE32_CHAR_BY_N32_CHAR).rjust(ENCODED_LENGTH, '0')
|
66
83
|
end
|
84
|
+
|
85
|
+
# @api private
|
86
|
+
# @param [String] string
|
87
|
+
# @return [String]
|
88
|
+
def self.normalize(string)
|
89
|
+
string.gsub(VARIANT_PATTERN, STANDARD_BY_VARIANT)
|
90
|
+
end
|
67
91
|
end
|
68
92
|
end
|
@@ -1,16 +1,19 @@
|
|
1
1
|
# coding: us-ascii
|
2
2
|
# frozen_string_literal: true
|
3
|
+
|
3
4
|
# Copyright (C) 2021 Kenichi Kamiya
|
4
5
|
|
5
6
|
class ULID
|
6
7
|
class MonotonicGenerator
|
8
|
+
include MonitorMixin
|
9
|
+
|
7
10
|
# @return [ULID, nil]
|
8
11
|
attr_reader :prev
|
9
12
|
|
10
13
|
undef_method :instance_variable_set
|
11
14
|
|
12
15
|
def initialize
|
13
|
-
|
16
|
+
super()
|
14
17
|
@prev = nil
|
15
18
|
end
|
16
19
|
|
@@ -26,7 +29,7 @@ class ULID
|
|
26
29
|
# @raise [UnexpectedError] if the generated ULID is an invalid value in monotonicity spec.
|
27
30
|
# Basically will not happen. Just means this feature prefers error rather than invalid value.
|
28
31
|
def generate(moment: ULID.current_milliseconds)
|
29
|
-
|
32
|
+
synchronize do
|
30
33
|
unless @prev
|
31
34
|
@prev = ULID.generate(moment: moment)
|
32
35
|
return @prev
|
@@ -34,19 +37,23 @@ class ULID
|
|
34
37
|
|
35
38
|
milliseconds = ULID.milliseconds_from_moment(moment)
|
36
39
|
|
37
|
-
ulid =
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
40
|
+
ulid = (
|
41
|
+
if @prev.milliseconds < milliseconds
|
42
|
+
ULID.generate(moment: milliseconds)
|
43
|
+
else
|
44
|
+
ULID.from_milliseconds_and_entropy(milliseconds: @prev.milliseconds, entropy: @prev.entropy.succ)
|
45
|
+
end
|
46
|
+
)
|
42
47
|
|
43
48
|
unless ulid > @prev
|
44
49
|
base_message = "monotonicity broken from unexpected reasons # generated: #{ulid.inspect}, prev: #{@prev.inspect}"
|
45
|
-
additional_information =
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
+
additional_information = (
|
51
|
+
if Thread.list == [Thread.main]
|
52
|
+
'# NOTE: looks single thread only exist'
|
53
|
+
else
|
54
|
+
'# NOTE: ran on multi threads, so this might from concurrency issue'
|
55
|
+
end
|
56
|
+
)
|
50
57
|
|
51
58
|
raise UnexpectedError, base_message + additional_information
|
52
59
|
end
|
data/lib/ulid/uuid.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# coding: us-ascii
|
2
2
|
# frozen_string_literal: true
|
3
|
+
|
3
4
|
# Copyright (C) 2021 Kenichi Kamiya
|
4
5
|
|
5
6
|
# Extracted features around UUID from some reasons
|
@@ -8,7 +9,7 @@
|
|
8
9
|
# * https://github.com/kachick/ruby-ulid/issues/76
|
9
10
|
class ULID
|
10
11
|
# Imported from https://stackoverflow.com/a/38191104/1212807, thank you!
|
11
|
-
UUIDV4_PATTERN = /\A[0-9A-F]{8}-[0-9A-F]{4}-
|
12
|
+
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
|
12
13
|
private_constant :UUIDV4_PATTERN
|
13
14
|
|
14
15
|
# @param [String, #to_str] uuid
|
@@ -18,7 +19,7 @@ class ULID
|
|
18
19
|
uuid = String.try_convert(uuid)
|
19
20
|
raise ArgumentError, 'ULID.from_uuidv4 takes only strings' unless uuid
|
20
21
|
|
21
|
-
prefix_trimmed = uuid.
|
22
|
+
prefix_trimmed = uuid.delete_prefix('urn:uuid:')
|
22
23
|
unless UUIDV4_PATTERN.match?(prefix_trimmed)
|
23
24
|
raise ParserError, "given `#{uuid}` does not match to `#{UUIDV4_PATTERN.inspect}`"
|
24
25
|
end
|
data/lib/ulid/version.rb
CHANGED
data/sig/ulid.rbs
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
class ULID
|
1
|
+
class ULID < Object
|
3
2
|
VERSION: String
|
4
3
|
CROCKFORD_BASE32_ENCODING_STRING: String
|
5
4
|
TIMESTAMP_ENCODED_LENGTH: 10
|
@@ -44,22 +43,57 @@ class ULID
|
|
44
43
|
CROCKFORD_BASE32_CHAR_PATTERN: Regexp
|
45
44
|
CROCKFORD_BASE32_CHAR_BY_N32_CHAR: Hash[String, String]
|
46
45
|
N32_CHAR_PATTERN: Regexp
|
46
|
+
STANDARD_BY_VARIANT: Hash[String, String]
|
47
|
+
VARIANT_PATTERN: Regexp
|
47
48
|
|
49
|
+
# A private API. Should not be used in your code.
|
48
50
|
def self.encode: (Integer integer) -> String
|
51
|
+
|
52
|
+
# A private API. Should not be used in your code.
|
49
53
|
def self.decode: (String string) -> Integer
|
54
|
+
|
55
|
+
# A private API. Should not be used in your code.
|
56
|
+
def self.normalize: (String string) -> String
|
50
57
|
end
|
51
58
|
|
52
59
|
class MonotonicGenerator
|
53
|
-
|
60
|
+
include MonitorMixin
|
61
|
+
|
62
|
+
# Returned value is `basically not` Thread-safety
|
63
|
+
# If you want to keep Thread-safety, keep to call {#generate} only in same {#synchronize} block
|
64
|
+
#
|
65
|
+
# ```ruby
|
66
|
+
# generator.synchronize do
|
67
|
+
# generator.prev
|
68
|
+
# generator.inspect
|
69
|
+
# generator.generate
|
70
|
+
# end
|
71
|
+
# ```
|
54
72
|
attr_reader prev: ULID | nil
|
73
|
+
|
74
|
+
# A private API. Should not be used in your code.
|
55
75
|
def initialize: -> void
|
76
|
+
|
77
|
+
# See [How to keep `Sortable` even if in same timestamp](https://github.com/kachick/ruby-ulid#how-to-keep-sortable-even-if-in-same-timestamp)
|
78
|
+
# The `Thread-safety` is implemented with [Monitor](https://bugs.ruby-lang.org/issues/16255)
|
56
79
|
def generate: (?moment: moment) -> ULID
|
80
|
+
|
81
|
+
# Returned value is `basically not` Thread-safety
|
82
|
+
# If you want to keep Thread-safety, keep to call {#generate} only in same {#synchronize} block
|
83
|
+
#
|
84
|
+
# ```ruby
|
85
|
+
# generator.synchronize do
|
86
|
+
# generator.prev
|
87
|
+
# generator.inspect
|
88
|
+
# generator.generate
|
89
|
+
# end
|
90
|
+
# ```
|
57
91
|
def inspect: -> String
|
58
92
|
alias to_s inspect
|
59
93
|
def freeze: -> void
|
60
94
|
end
|
61
95
|
|
62
|
-
interface
|
96
|
+
interface _ToULID
|
63
97
|
def to_ulid: () -> ULID
|
64
98
|
end
|
65
99
|
|
@@ -75,57 +109,437 @@ class ULID
|
|
75
109
|
@inspect: String?
|
76
110
|
@time: Time?
|
77
111
|
|
78
|
-
|
79
|
-
|
112
|
+
# Retuns a ULID
|
113
|
+
#
|
114
|
+
# They are sortable when generated in different timestamp with milliseconds precision
|
115
|
+
#
|
116
|
+
# ```ruby
|
117
|
+
# ulids = 1000.times.map do
|
118
|
+
# sleep(0.001)
|
119
|
+
# ULID.generate
|
120
|
+
# end
|
121
|
+
# ulids.uniq(&:to_time).size #=> 1000
|
122
|
+
# ulids.sort == ulids #=> true
|
123
|
+
# ```
|
124
|
+
#
|
125
|
+
# `ULID.generate` can take fixed `Time` instance.
|
126
|
+
# See also the short hand [ULID.at](https://kachick.github.io/ruby-ulid/ULID.html#at-class_method)
|
127
|
+
#
|
128
|
+
# ```ruby
|
129
|
+
# time = Time.at(946684800).utc #=> 2000-01-01 00:00:00 UTC
|
130
|
+
# ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB00N018DCPJA4H9379P)
|
131
|
+
# ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB006WQT3JTMN0T14EBP)
|
132
|
+
# ```
|
133
|
+
#
|
134
|
+
# The basic generator prefers `randomness`, it does not guarantee `sortable` for same milliseconds ULIDs.
|
135
|
+
#
|
136
|
+
# ```ruby
|
137
|
+
# ulids = 10000.times.map do
|
138
|
+
# ULID.generate
|
139
|
+
# end
|
140
|
+
# ulids.uniq(&:to_time).size #=> 35 (the size is not fixed, might be changed in environment)
|
141
|
+
# ulids.sort == ulids #=> false
|
142
|
+
# ```
|
143
|
+
#
|
144
|
+
# If you want to keep sortable even if in same timestamp, See also [ULID::MonotonicGenerator](https://github.com/kachick/ruby-ulid#how-to-keep-sortable-even-if-in-same-timestamp)
|
145
|
+
#
|
146
|
+
def self.generate: (?moment: moment, ?entropy: Integer) -> ULID
|
147
|
+
|
148
|
+
# Shorthand of `ULID.generate(moment: Time)`
|
149
|
+
# See also [ULID.generate](https://kachick.github.io/ruby-ulid/ULID.html#generate-class_method)
|
150
|
+
#
|
151
|
+
# ```ruby
|
152
|
+
# time = Time.at(946684800).utc #=> 2000-01-01 00:00:00 UTC
|
153
|
+
# ULID.at(time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB002W5BGWWKN76N22H6)
|
154
|
+
#
|
155
|
+
# ulids = 1000.times.map do |n|
|
156
|
+
# ULID.at(time + n)
|
157
|
+
# end
|
158
|
+
# ulids.sort == ulids #=> true
|
159
|
+
# ```
|
160
|
+
def self.at: (Time time) -> ULID
|
161
|
+
|
162
|
+
# A private API. Should not be used in your code.
|
80
163
|
def self.current_milliseconds: -> Integer
|
164
|
+
|
165
|
+
# A private API. Should not be used in your code.
|
81
166
|
def self.milliseconds_from_moment: (moment moment) -> Integer
|
167
|
+
|
168
|
+
# `ULID` can be element of the `Range`. If you generated the IDs in monotonic generator, ID based filtering is easy and reliable
|
169
|
+
#
|
170
|
+
# ```ruby
|
171
|
+
# include_end = ulid1..ulid2
|
172
|
+
# exclude_end = ulid1...ulid2
|
173
|
+
#
|
174
|
+
# ulids.grep(one_of_the_above)
|
175
|
+
# ulids.grep_v(one_of_the_above)
|
176
|
+
# ```
|
177
|
+
#
|
178
|
+
# When want to filter ULIDs with `Time`, we should consider to handle the precision.
|
179
|
+
# So this gem provides `ULID.range` to generate reasonable `Range[ULID]` from `Range[Time]`
|
180
|
+
#
|
181
|
+
# ```ruby
|
182
|
+
# # Both of below, The begin of `Range[ULID]` will be the minimum in the floored milliseconds of the time1
|
183
|
+
# include_end = ULID.range(time1..time2) #=> The end of `Range[ULID]` will be the maximum in the floored milliseconds of the time2
|
184
|
+
# exclude_end = ULID.range(time1...time2) #=> The end of `Range[ULID]` will be the minimum in the floored milliseconds of the time2
|
185
|
+
#
|
186
|
+
# # Below patterns are acceptable
|
187
|
+
# pinpointing = ULID.range(time1..time1) #=> This will match only for all IDs in `time1`
|
188
|
+
# 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)
|
189
|
+
# until_the_end = ULID.range(ULID.min.to_time..time1) #=> This is same as above for Ruby 2.6
|
190
|
+
# until_the_ulid_limit = ULID.range(time1..) # This will match only for all IDs from `time1` to max value of the ULID limit
|
191
|
+
#
|
192
|
+
# # So you can use the generated range objects as below
|
193
|
+
# ulids.grep(one_of_the_above)
|
194
|
+
# ulids.grep_v(one_of_the_above)
|
195
|
+
# #=> I hope the results should be actually you want!
|
196
|
+
# ```
|
197
|
+
#
|
82
198
|
def self.range: (period period) -> Range[ULID]
|
199
|
+
|
200
|
+
# Returns new `Time` with truncating excess precisions in ULID spec.
|
201
|
+
#
|
202
|
+
# ```ruby
|
203
|
+
# time = Time.at(946684800, Rational('123456.789')).utc
|
204
|
+
# #=> 2000-01-01 00:00:00.123456789 UTC
|
205
|
+
# ULID.floor(time) #=> 2000-01-01 00:00:00.123 UTC
|
206
|
+
# ```
|
83
207
|
def self.floor: (Time time) -> Time
|
84
|
-
|
208
|
+
|
209
|
+
# Get ULID instance from encoded String.
|
210
|
+
#
|
211
|
+
# ```ruby
|
212
|
+
# ulid = ULID.parse('01ARZ3NDEKTSV4RRFFQ69G5FAV')
|
213
|
+
# #=> ULID(2016-07-30 23:54:10.259 UTC: 01ARZ3NDEKTSV4RRFFQ69G5FAV)
|
214
|
+
# ```
|
215
|
+
def self.parse: (_ToStr string) -> ULID
|
216
|
+
|
217
|
+
# ```ruby
|
218
|
+
# # Currently experimental feature, so needed to load the extension.
|
219
|
+
# require 'ulid/uuid'
|
220
|
+
#
|
221
|
+
# # Basically reversible
|
222
|
+
# ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
|
223
|
+
# #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
|
224
|
+
# ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
|
225
|
+
# ```
|
226
|
+
#
|
227
|
+
# See also [Why this is experimental?](https://github.com/kachick/ruby-ulid/issues/76)
|
85
228
|
def self.from_uuidv4: (String uuid) -> ULID
|
86
|
-
def self.from_integer: (Integer integer) ->
|
229
|
+
def self.from_integer: (Integer integer) -> ULID
|
230
|
+
|
231
|
+
# Returns termination values for ULID spec.
|
232
|
+
#
|
233
|
+
# ```ruby
|
234
|
+
# ULID.min
|
235
|
+
# #=> ULID(1970-01-01 00:00:00.000 UTC: 00000000000000000000000000)
|
236
|
+
# ```
|
237
|
+
#
|
238
|
+
# It can take `Time` instance as an optional argument.
|
239
|
+
# Then returns ULID that has minimum value of randomness part in the timestamp.
|
240
|
+
#
|
241
|
+
# ```ruby
|
242
|
+
# time = Time.at(946684800, Rational('123456.789')).utc
|
243
|
+
# #=> 2000-01-01 00:00:00.123456789 UTC
|
244
|
+
# ULID.min(time)
|
245
|
+
# #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3V0000000000000000)
|
246
|
+
# ```
|
247
|
+
#
|
248
|
+
# See also [ULID.max](https://kachick.github.io/ruby-ulid/ULID.html#max-class_method)
|
87
249
|
def self.min: (?moment moment) -> ULID
|
250
|
+
|
251
|
+
# Returns termination values for ULID spec.
|
252
|
+
#
|
253
|
+
# ```ruby
|
254
|
+
# ULID.max
|
255
|
+
# #=> ULID(10889-08-02 05:31:50.655 UTC: 7ZZZZZZZZZZZZZZZZZZZZZZZZZ)
|
256
|
+
# ```
|
257
|
+
#
|
258
|
+
# It can take `Time` instance as an optional argument.
|
259
|
+
# Then returns ULID that has maximum value of randomness part in the timestamp.
|
260
|
+
#
|
261
|
+
# ```ruby
|
262
|
+
# time = Time.at(946684800, Rational('123456.789')).utc
|
263
|
+
# #=> 2000-01-01 00:00:00.123456789 UTC
|
264
|
+
# ULID.max(time)
|
265
|
+
# #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3VZZZZZZZZZZZZZZZZ)
|
266
|
+
# ```
|
267
|
+
#
|
268
|
+
# See also [ULID.min](https://kachick.github.io/ruby-ulid/ULID.html#min-class_method)
|
88
269
|
def self.max: (?moment moment) -> ULID
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
270
|
+
|
271
|
+
# Returns random ULIDs.
|
272
|
+
#
|
273
|
+
# Basically ignores generating time.
|
274
|
+
#
|
275
|
+
# ```ruby
|
276
|
+
# ULID.sample #=> ULID(2545-07-26 06:51:20.085 UTC: 0GGKQ45GMNMZR6N8A8GFG0ZXST)
|
277
|
+
# ULID.sample #=> ULID(5098-07-26 21:31:06.946 UTC: 2SSBNGGYA272J7BMDCG4Z6EEM5)
|
278
|
+
# ULID.sample(0) #=> []
|
279
|
+
# ULID.sample(1) #=> [ULID(2241-04-16 03:31:18.440 UTC: 07S52YWZ98AZ8T565MD9VRYMQH)]
|
280
|
+
# ULID.sample(5)
|
281
|
+
# #=>
|
282
|
+
# #[ULID(5701-04-29 12:41:19.647 UTC: 3B2YH2DV0ZYDDATGTYSKMM1CMT),
|
283
|
+
# # ULID(2816-08-01 01:21:46.612 UTC: 0R9GT6RZKMK3RG02Q2HAFVKEY2),
|
284
|
+
# # ULID(10408-10-05 17:06:27.848 UTC: 7J6CPTEEC86Y24EQ4F1Y93YYN0),
|
285
|
+
# # ULID(2741-09-02 16:24:18.803 UTC: 0P4Q4V34KKAJW46QW47WQB5463),
|
286
|
+
# # ULID(2665-03-16 14:50:22.724 UTC: 0KYFW9DWM4CEGFNTAC6YFAVVJ6)]
|
287
|
+
# ```
|
288
|
+
#
|
289
|
+
# You can specify a range object for the timestamp restriction, See also [ULID.range](https://kachick.github.io/ruby-ulid/ULID.html#range-class_method).
|
290
|
+
#
|
291
|
+
# ```ruby
|
292
|
+
# ulid1 = ULID.parse('01F4A5Y1YAQCYAYCTC7GRMJ9AA') #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
293
|
+
# ulid2 = ULID.parse('01F4PTVCSN9ZPFKYTY2DDJVRK4') #=> ULID(2021-05-02 15:23:48.917 UTC: 01F4PTVCSN9ZPFKYTY2DDJVRK4)
|
294
|
+
# ulids = ULID.sample(1000, period: ulid1..ulid2)
|
295
|
+
# ulids.uniq.size #=> 1000
|
296
|
+
# ulids.take(10)
|
297
|
+
# #=>
|
298
|
+
# #[ULID(2021-05-02 06:57:19.954 UTC: 01F4NXW02JNB8H0J0TK48JD39X),
|
299
|
+
# # ULID(2021-05-02 07:06:07.458 UTC: 01F4NYC372GVP7NS0YAYQGT4VZ),
|
300
|
+
# # ULID(2021-05-01 06:16:35.791 UTC: 01F4K94P6F6P68K0H64WRDSFKW),
|
301
|
+
# # ULID(2021-04-27 22:17:37.844 UTC: 01F4APHGSMFJZQTGXKZBFFBPJP),
|
302
|
+
# # ULID(2021-04-28 20:17:55.357 UTC: 01F4D231MXQJXAR8G2JZHEJNH3),
|
303
|
+
# # ULID(2021-04-30 07:18:54.307 UTC: 01F4GTA2332AS2VPHC4FMKC7R5),
|
304
|
+
# # ULID(2021-05-02 12:26:03.480 UTC: 01F4PGNXARG554Y3HYVBDW4T9S),
|
305
|
+
# # ULID(2021-04-29 09:52:15.107 UTC: 01F4EGP483ZX2747FQPWQNPPMW),
|
306
|
+
# # ULID(2021-04-29 03:18:24.152 UTC: 01F4DT4Z4RA0QV8WFQGRAG63EH),
|
307
|
+
# # ULID(2021-05-02 13:27:16.394 UTC: 01F4PM605ABF5SDVMEHBH8JJ9R)]
|
308
|
+
# ULID.sample(10, period: ulid1.to_time..ulid2.to_time)
|
309
|
+
# #=>
|
310
|
+
# # [ULID(2021-04-29 06:44:41.513 UTC: 01F4E5YPD9XQ3MYXWK8ZJKY8SW),
|
311
|
+
# # ULID(2021-05-01 00:35:06.629 UTC: 01F4JNKD85SVK1EAEYSJGF53A2),
|
312
|
+
# # ULID(2021-05-02 12:45:28.408 UTC: 01F4PHSEYRG9BWBEWMRW1XE6WW),
|
313
|
+
# # ULID(2021-05-01 03:06:09.130 UTC: 01F4JY7ZBABCBMX16XH2Q4JW4W),
|
314
|
+
# # ULID(2021-04-29 21:38:58.109 UTC: 01F4FS45DX4049JEQK4W6TER6G),
|
315
|
+
# # ULID(2021-04-29 17:14:14.116 UTC: 01F4F9ZDQ449BE8BBZFEHYQWG2),
|
316
|
+
# # ULID(2021-04-30 16:18:08.205 UTC: 01F4HS5DPD1HWDVJNJ6YKJXKSK),
|
317
|
+
# # ULID(2021-04-30 10:31:33.602 UTC: 01F4H5ATF2A1CSQF0XV5NKZ288),
|
318
|
+
# # ULID(2021-04-28 16:49:06.484 UTC: 01F4CP4PDM214Q6H3KJP7DYJRR),
|
319
|
+
# # ULID(2021-04-28 15:05:06.808 UTC: 01F4CG68ZRST94T056KRZ5K9S4)]
|
320
|
+
# ```
|
321
|
+
def self.sample: (?period: period) -> ULID
|
322
|
+
| (Integer number, ?period: period) -> Array[ULID]
|
323
|
+
def self.valid?: (untyped) -> bool
|
324
|
+
|
325
|
+
# Returns normalized string
|
326
|
+
#
|
327
|
+
# ```ruby
|
328
|
+
# ULID.normalize('-olarz3-noekisv4rrff-q6ig5fav--') #=> "01ARZ3N0EK1SV4RRFFQ61G5FAV"
|
329
|
+
# ULID.normalized?('-olarz3-noekisv4rrff-q6ig5fav--') #=> false
|
330
|
+
# ULID.normalized?('01ARZ3N0EK1SV4RRFFQ61G5FAV') #=> true
|
331
|
+
# ```
|
332
|
+
#
|
333
|
+
# 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.normalize: (_ToStr string) -> String
|
335
|
+
|
336
|
+
# Returns `true` if it is normalized string
|
337
|
+
#
|
338
|
+
# ```ruby
|
339
|
+
# ULID.normalize('-olarz3-noekisv4rrff-q6ig5fav--') #=> "01ARZ3N0EK1SV4RRFFQ61G5FAV"
|
340
|
+
# ULID.normalized?('-olarz3-noekisv4rrff-q6ig5fav--') #=> false
|
341
|
+
# ULID.normalized?('01ARZ3N0EK1SV4RRFFQ61G5FAV') #=> true
|
342
|
+
# ```
|
343
|
+
#
|
344
|
+
# See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
|
345
|
+
def self.normalized?: (untyped) -> bool
|
346
|
+
|
347
|
+
# Returns parsed ULIDs from given String for rough operations.
|
348
|
+
#
|
349
|
+
# ```ruby
|
350
|
+
# json =<<'EOD'
|
351
|
+
# {
|
352
|
+
# "id": "01F4GNAV5ZR6FJQ5SFQC7WDSY3",
|
353
|
+
# "author": {
|
354
|
+
# "id": "01F4GNBXW1AM2KWW52PVT3ZY9X",
|
355
|
+
# "name": "kachick"
|
356
|
+
# },
|
357
|
+
# "title": "My awesome blog post",
|
358
|
+
# "comments": [
|
359
|
+
# {
|
360
|
+
# "id": "01F4GNCNC3CH0BCRZBPPDEKBKS",
|
361
|
+
# "commenter": {
|
362
|
+
# "id": "01F4GNBXW1AM2KWW52PVT3ZY9X",
|
363
|
+
# "name": "kachick"
|
364
|
+
# }
|
365
|
+
# },
|
366
|
+
# {
|
367
|
+
# "id": "01F4GNCXAMXQ1SGBH5XCR6ZH0M",
|
368
|
+
# "commenter": {
|
369
|
+
# "id": "01F4GND4RYYSKNAADHQ9BNXAWJ",
|
370
|
+
# "name": "pankona"
|
371
|
+
# }
|
372
|
+
# }
|
373
|
+
# ]
|
374
|
+
# }
|
375
|
+
# EOD
|
376
|
+
#
|
377
|
+
# ULID.scan(json).to_a
|
378
|
+
# #=>
|
379
|
+
# # [ULID(2021-04-30 05:51:57.119 UTC: 01F4GNAV5ZR6FJQ5SFQC7WDSY3),
|
380
|
+
# # ULID(2021-04-30 05:52:32.641 UTC: 01F4GNBXW1AM2KWW52PVT3ZY9X),
|
381
|
+
# # ULID(2021-04-30 05:52:56.707 UTC: 01F4GNCNC3CH0BCRZBPPDEKBKS),
|
382
|
+
# # ULID(2021-04-30 05:52:32.641 UTC: 01F4GNBXW1AM2KWW52PVT3ZY9X),
|
383
|
+
# # ULID(2021-04-30 05:53:04.852 UTC: 01F4GNCXAMXQ1SGBH5XCR6ZH0M),
|
384
|
+
# # ULID(2021-04-30 05:53:12.478 UTC: 01F4GND4RYYSKNAADHQ9BNXAWJ)]
|
385
|
+
# ```
|
386
|
+
def self.scan: (_ToStr string) -> Enumerator[self, singleton(ULID)]
|
387
|
+
| (_ToStr string) { (ULID ulid) -> void } -> singleton(ULID)
|
388
|
+
def self.from_milliseconds_and_entropy: (milliseconds: Integer, entropy: Integer) -> ULID
|
389
|
+
def self.try_convert: (_ToULID) -> ULID
|
96
390
|
| (untyped) -> nil
|
391
|
+
|
392
|
+
# ```ruby
|
393
|
+
# ulid = ULID.generate
|
394
|
+
# #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
395
|
+
# ulid.milliseconds #=> 1619544442826
|
396
|
+
# ```
|
97
397
|
attr_reader milliseconds: Integer
|
98
398
|
attr_reader entropy: Integer
|
99
|
-
|
399
|
+
|
400
|
+
# ```ruby
|
401
|
+
# ulid = ULID.generate
|
402
|
+
# #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
403
|
+
# ulid.to_s #=> "01F4A5Y1YAQCYAYCTC7GRMJ9AA"
|
404
|
+
# ```
|
100
405
|
def to_s: -> String
|
406
|
+
|
407
|
+
# ```ruby
|
408
|
+
# ulid = ULID.generate
|
409
|
+
# #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
410
|
+
# ulid.to_i #=> 1957909092946624190749577070267409738
|
411
|
+
# ```
|
101
412
|
def to_i: -> Integer
|
102
413
|
alias hash to_i
|
414
|
+
|
415
|
+
# Basically same as String based sort.
|
416
|
+
#
|
417
|
+
# ```ruby
|
418
|
+
# ulids = ULID.sample(10000); nil
|
419
|
+
# ulids.map(&:to_s).sort == ulids.sort.map(&:to_s)
|
420
|
+
# #=> true
|
421
|
+
# ```
|
422
|
+
#
|
423
|
+
# To be precise, this sorting unaffected with `case sensitive or not` and might handle [ulid/spec#57](https://github.com/ulid/spec/pull/57) in future. So preferable than `lexicographically sortable` in actual case.
|
424
|
+
#
|
103
425
|
def <=>: (ULID other) -> Integer
|
104
426
|
| (untyped other) -> nil
|
427
|
+
|
428
|
+
# ```ruby
|
429
|
+
# ulid = ULID.generate
|
430
|
+
# ulid.inspect #=> "ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)"
|
431
|
+
# ```
|
105
432
|
def inspect: -> String
|
433
|
+
|
434
|
+
# ```ruby
|
435
|
+
# ULID.parse('4NNB20D9C1ME2NGMTX51ERZJX0') == ULID.parse('4nnb20d9c1me2ngmtx51erzjx0')
|
436
|
+
# #=> true
|
437
|
+
# ```
|
106
438
|
def eql?: (untyped other) -> bool
|
107
439
|
alias == eql?
|
108
440
|
def ===: (untyped other) -> bool
|
441
|
+
|
442
|
+
# ```ruby
|
443
|
+
# ulid = ULID.generate
|
444
|
+
# #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
445
|
+
# ulid.to_time #=> 2021-04-27 17:27:22.826 UTC
|
446
|
+
# ```
|
109
447
|
def to_time: -> Time
|
448
|
+
|
449
|
+
# ```ruby
|
450
|
+
# ulid = ULID.generate
|
451
|
+
# #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
452
|
+
# ulid.timestamp #=> "01F4A5Y1YA"
|
453
|
+
# ```
|
110
454
|
def timestamp: -> String
|
455
|
+
|
456
|
+
# ```ruby
|
457
|
+
# ulid = ULID.generate
|
458
|
+
# #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
459
|
+
# ulid.randomness #=> "QCYAYCTC7GRMJ9AA"
|
460
|
+
# ```
|
111
461
|
def randomness: -> String
|
462
|
+
|
463
|
+
# Intentionally avoiding to use `Record type` ref: https://github.com/ruby/rbs/blob/4fb4c33b2325d1a73d79ff7aaeb49f21cec1e0e5/docs/syntax.md#record-type
|
464
|
+
# Because the returning values are not fixed.
|
112
465
|
def patterns: -> Hash[Symbol, Regexp | String]
|
113
466
|
def octets: -> octets
|
114
467
|
def timestamp_octets: -> timestamp_octets
|
115
468
|
def randomness_octets: -> randomness_octets
|
469
|
+
|
470
|
+
# ```ruby
|
471
|
+
# # Currently experimental feature, so needed to load the extension.
|
472
|
+
# require 'ulid/uuid'
|
473
|
+
#
|
474
|
+
# # Basically reversible
|
475
|
+
# ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
|
476
|
+
# #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
|
477
|
+
# ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
|
478
|
+
# ```
|
479
|
+
#
|
480
|
+
# See also [Why this is experimental?](https://github.com/kachick/ruby-ulid/issues/76)
|
116
481
|
def to_uuidv4: -> String
|
117
|
-
|
118
|
-
|
482
|
+
|
483
|
+
# Returns next(successor) ULID.
|
484
|
+
# Especially `ULID#succ` makes it possible `Range[ULID]#each`.
|
485
|
+
#
|
486
|
+
# NOTE: But basically `Range[ULID]#each` should not be used, incrementing 128 bits IDs are not reasonable operation in most case
|
487
|
+
#
|
488
|
+
# ```ruby
|
489
|
+
# ULID.parse('01BX5ZZKBKZZZZZZZZZZZZZZZY').next.to_s #=> "01BX5ZZKBKZZZZZZZZZZZZZZZZ"
|
490
|
+
# ULID.parse('01BX5ZZKBKZZZZZZZZZZZZZZZZ').next.to_s #=> "01BX5ZZKBM0000000000000000"
|
491
|
+
# ULID.parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').next #=> nil
|
492
|
+
# ```
|
493
|
+
#
|
494
|
+
# See also [ULID#pred](https://kachick.github.io/ruby-ulid/ULID.html#pred-instance_method)
|
495
|
+
def succ: -> ULID?
|
496
|
+
alias next succ
|
497
|
+
|
498
|
+
# Returns predecessor ULID.
|
499
|
+
#
|
500
|
+
# ```ruby
|
501
|
+
# ULID.parse('01BX5ZZKBK0000000000000001').pred.to_s #=> "01BX5ZZKBK0000000000000000"
|
502
|
+
# ULID.parse('01BX5ZZKBK0000000000000000').pred.to_s #=> "01BX5ZZKBJZZZZZZZZZZZZZZZZ"
|
503
|
+
# ULID.parse('00000000000000000000000000').pred #=> nil
|
504
|
+
# ```
|
505
|
+
#
|
506
|
+
# See also [ULID#succ](https://kachick.github.io/ruby-ulid/ULID.html#succ-instance_method)
|
119
507
|
def pred: -> ULID?
|
120
508
|
def freeze: -> self
|
509
|
+
|
510
|
+
# A private API. Should not be used in your code.
|
511
|
+
def marshal_dump: -> Integer
|
512
|
+
|
513
|
+
# A private API. Should not be used in your code.
|
514
|
+
def marshal_load: (Integer integer) -> void
|
515
|
+
|
516
|
+
# Returns `self`
|
121
517
|
def to_ulid: -> self
|
518
|
+
|
519
|
+
# Returns `self`. Not a new instance.
|
122
520
|
def dup: -> self
|
123
|
-
|
521
|
+
|
522
|
+
# Same API as [Object#clone](https://github.com/ruby/rbs/blob/4fb4c33b2325d1a73d79ff7aaeb49f21cec1e0e5/core/object.rbs#L79)
|
523
|
+
# But actually returns `self`. Not a new instance.
|
124
524
|
def clone: (?freeze: bool) -> self
|
125
525
|
|
126
526
|
private
|
527
|
+
|
528
|
+
# A private API. Should not be used in your code.
|
529
|
+
def self.new: (milliseconds: Integer, entropy: Integer, integer: Integer) -> self
|
530
|
+
|
531
|
+
# A private API. Should not be used in your code.
|
127
532
|
def self.reasonable_entropy: -> Integer
|
533
|
+
|
534
|
+
# A private API. Should not be used in your code.
|
128
535
|
def self.milliseconds_from_time: (Time time) -> Integer
|
536
|
+
|
537
|
+
# A private API. Should not be used in your code.
|
129
538
|
def self.safe_get_class_name: (untyped object) -> String
|
539
|
+
|
540
|
+
# A private API. Should not be used in your code.
|
541
|
+
def initialize: (milliseconds: Integer, entropy: Integer, integer: Integer) -> void
|
542
|
+
|
543
|
+
# A private API. Should not be used in your code.
|
130
544
|
def cache_all_instance_variables: -> void
|
131
545
|
end
|
metadata
CHANGED
@@ -1,69 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-ulid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kenichi Kamiya
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05
|
12
|
-
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: rbs
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.2.0
|
20
|
-
type: :development
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: 1.2.0
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: benchmark-ips
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - ">="
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: 2.8.4
|
34
|
-
- - "<"
|
35
|
-
- !ruby/object:Gem::Version
|
36
|
-
version: '3'
|
37
|
-
type: :development
|
38
|
-
prerelease: false
|
39
|
-
version_requirements: !ruby/object:Gem::Requirement
|
40
|
-
requirements:
|
41
|
-
- - ">="
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
version: 2.8.4
|
44
|
-
- - "<"
|
45
|
-
- !ruby/object:Gem::Version
|
46
|
-
version: '3'
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: yard
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
-
requirements:
|
51
|
-
- - ">="
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: 0.9.26
|
54
|
-
- - "<"
|
55
|
-
- !ruby/object:Gem::Version
|
56
|
-
version: '2'
|
57
|
-
type: :development
|
58
|
-
prerelease: false
|
59
|
-
version_requirements: !ruby/object:Gem::Requirement
|
60
|
-
requirements:
|
61
|
-
- - ">="
|
62
|
-
- !ruby/object:Gem::Version
|
63
|
-
version: 0.9.26
|
64
|
-
- - "<"
|
65
|
-
- !ruby/object:Gem::Version
|
66
|
-
version: '2'
|
11
|
+
date: 2021-07-05 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
67
13
|
description: |2
|
68
14
|
The ULID(Universally Unique Lexicographically Sortable Identifier) has useful specs for applications (e.g. `Database key`), especially possess all `uniqueness`, `randomness`, `extractable timestamps` and `sortable` features.
|
69
15
|
This gem aims to provide the generator, monotonic generator, parser and handy manipulation features around the ULID.
|
@@ -74,8 +20,9 @@ executables: []
|
|
74
20
|
extensions: []
|
75
21
|
extra_rdoc_files: []
|
76
22
|
files:
|
77
|
-
- LICENSE
|
23
|
+
- LICENSE.txt
|
78
24
|
- README.md
|
25
|
+
- lib/ruby-ulid.rb
|
79
26
|
- lib/ulid.rb
|
80
27
|
- lib/ulid/crockford_base32.rb
|
81
28
|
- lib/ulid/monotonic_generator.rb
|
@@ -89,6 +36,7 @@ metadata:
|
|
89
36
|
documentation_uri: https://kachick.github.io/ruby-ulid/
|
90
37
|
homepage_uri: https://github.com/kachick/ruby-ulid
|
91
38
|
source_code_uri: https://github.com/kachick/ruby-ulid
|
39
|
+
bug_tracker_uri: https://github.com/kachick/ruby-ulid/issues
|
92
40
|
post_install_message:
|
93
41
|
rdoc_options: []
|
94
42
|
require_paths:
|
@@ -107,5 +55,5 @@ requirements: []
|
|
107
55
|
rubygems_version: 3.2.15
|
108
56
|
signing_key:
|
109
57
|
specification_version: 4
|
110
|
-
summary:
|
58
|
+
summary: ULID manipulation library
|
111
59
|
test_files: []
|