ruby-ulid 0.0.17 → 0.0.18
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 +32 -5
- data/lib/ulid.rb +98 -97
- data/lib/ulid/monotonic_generator.rb +1 -1
- data/lib/ulid/uuid.rb +37 -0
- data/lib/ulid/version.rb +1 -1
- data/sig/ulid.rbs +9 -14
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b1641b0894c0140c3a9c6f59fa8abcfca386362ad1aaa92fe196f3bf5f75cf1
|
4
|
+
data.tar.gz: 932fa04dceb504330289d4236f9670fecd702989504bdd402873d60c0bb1c0e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f073c7b240ef96b511c84c6bc5649c483fc97c62a3892f58531b6f36e8235e49dcd865433fe522eec48a3532c98c4deec8713f453a4b2a117157788eabddaf6b
|
7
|
+
data.tar.gz: a2486bbefd62d0d019c15044c9d67b6aabc33e370f4ee88239c1e2b33a40738c736fcc9221dd3ce457eabf83a3580be5dc501fef0c359345a453331bdd62d139
|
data/README.md
CHANGED
@@ -49,7 +49,7 @@ Should be installed!
|
|
49
49
|
Add this line to your application/library's `Gemfile` is needed in basic use-case
|
50
50
|
|
51
51
|
```ruby
|
52
|
-
gem 'ruby-ulid', '0.0.
|
52
|
+
gem 'ruby-ulid', '0.0.18'
|
53
53
|
```
|
54
54
|
|
55
55
|
### Generator and Parser
|
@@ -62,7 +62,11 @@ require 'ulid'
|
|
62
62
|
|
63
63
|
ulid = ULID.generate #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
|
64
64
|
ulid.to_time #=> 2021-04-27 17:27:22.826 UTC
|
65
|
+
ulid.milliseconds #=> 1619544442826
|
65
66
|
ulid.to_s #=> "01F4A5Y1YAQCYAYCTC7GRMJ9AA"
|
67
|
+
ulid.timestamp #=> "01F4A5Y1YA"
|
68
|
+
ulid.randomness #=> "QCYAYCTC7GRMJ9AA"
|
69
|
+
ulid.to_i #=> 1957909092946624190749577070267409738
|
66
70
|
ulid.octets #=> [1, 121, 20, 95, 7, 202, 187, 60, 175, 51, 76, 60, 49, 73, 37, 74]
|
67
71
|
```
|
68
72
|
|
@@ -86,15 +90,16 @@ ulids.uniq(&:to_time).size #=> 1000
|
|
86
90
|
ulids.sort == ulids #=> true
|
87
91
|
```
|
88
92
|
|
89
|
-
`ULID.generate` can take fixed `Time` instance
|
93
|
+
`ULID.generate` can take fixed `Time` instance. The shorthand is `ULID.at`
|
90
94
|
|
91
95
|
```ruby
|
92
96
|
time = Time.at(946684800).utc #=> 2000-01-01 00:00:00 UTC
|
93
97
|
ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB00N018DCPJA4H9379P)
|
94
98
|
ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB006WQT3JTMN0T14EBP)
|
99
|
+
ULID.at(time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB002W5BGWWKN76N22H6)
|
95
100
|
|
96
101
|
ulids = 1000.times.map do |n|
|
97
|
-
ULID.
|
102
|
+
ULID.at(time + n)
|
98
103
|
end
|
99
104
|
ulids.sort == ulids #=> true
|
100
105
|
```
|
@@ -173,6 +178,13 @@ ulids.grep_v(one_of_the_above)
|
|
173
178
|
#=> I hope the results should be actually you want!
|
174
179
|
```
|
175
180
|
|
181
|
+
If you want to manually handle the Time objects, `ULID.floor` returns new `Time` with truncating excess precisions in ULID spec.
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
time = Time.at(946684800, Rational('123456.789')).utc #=> 2000-01-01 00:00:00.123456789 UTC
|
185
|
+
ULID.floor(time) #=> 2000-01-01 00:00:00.123 UTC
|
186
|
+
```
|
187
|
+
|
176
188
|
### Scanner for string (e.g. `JSON`)
|
177
189
|
|
178
190
|
For rough operations, `ULID.scan` might be useful.
|
@@ -215,6 +227,18 @@ ULID.scan(json).to_a
|
|
215
227
|
# ULID(2021-04-30 05:53:12.478 UTC: 01F4GND4RYYSKNAADHQ9BNXAWJ)]
|
216
228
|
```
|
217
229
|
|
230
|
+
`ULID#patterns` is a util for text based operations.
|
231
|
+
The results and spec are not fixed. Should not be used except snippets/console operation
|
232
|
+
|
233
|
+
```ruby
|
234
|
+
ULID.parse('01F4GNBXW1AM2KWW52PVT3ZY9X').patterns
|
235
|
+
#=> returns like a fallowing Hash
|
236
|
+
{
|
237
|
+
named_captures: /(?<timestamp>01F4GNBXW1)(?<randomness>AM2KWW52PVT3ZY9X)/i,
|
238
|
+
strict_named_captures: /\A(?<timestamp>01F4GNBXW1)(?<randomness>AM2KWW52PVT3ZY9X)\z/i
|
239
|
+
}
|
240
|
+
```
|
241
|
+
|
218
242
|
### Some methods to help manipulations
|
219
243
|
|
220
244
|
`ULID.min` and `ULID.max` return termination values for ULID spec.
|
@@ -266,9 +290,12 @@ ULID.sample(5)
|
|
266
290
|
### UUIDv4 converter for migration use-cases
|
267
291
|
|
268
292
|
`ULID.from_uuidv4` and `ULID#to_uuidv4` is the converter.
|
269
|
-
The imported timestamp is meaningless. So ULID's benefit will lost
|
293
|
+
The imported timestamp is meaningless. So ULID's benefit will lost.
|
270
294
|
|
271
295
|
```ruby
|
296
|
+
# Currently experimental feature, so needed to load the extension.
|
297
|
+
require 'ulid/uuid'
|
298
|
+
|
272
299
|
# Basically reversible
|
273
300
|
ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39') #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
|
274
301
|
ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
|
@@ -322,7 +349,7 @@ Major methods can be replaced as below.
|
|
322
349
|
-ULID.generate
|
323
350
|
+ULID.generate.to_s
|
324
351
|
-ULID.at(time)
|
325
|
-
+ULID.
|
352
|
+
+ULID.at(time).to_s
|
326
353
|
-ULID.time(string)
|
327
354
|
+ULID.parse(string).to_time
|
328
355
|
-ULID.min_ulid_at(time)
|
data/lib/ulid.rb
CHANGED
@@ -17,33 +17,44 @@ class ULID
|
|
17
17
|
class ParserError < Error; end
|
18
18
|
class SetupError < ScriptError; end
|
19
19
|
|
20
|
+
# `Subset` of Crockford's Base32. Just excluded I, L, O, U, -.
|
21
|
+
# refs:
|
22
|
+
# * https://www.crockford.com/base32.html
|
23
|
+
# * https://github.com/ulid/spec/pull/57
|
24
|
+
# * https://github.com/kachick/ruby-ulid/issues/57
|
20
25
|
encoding_string = '0123456789ABCDEFGHJKMNPQRSTVWXYZ'
|
21
|
-
|
22
|
-
# @see https://www.crockford.com/base32.html
|
23
|
-
ENCODING_CHARS = encoding_string.chars.map(&:freeze).freeze
|
26
|
+
encoding_chars = encoding_string.chars.map(&:freeze).freeze
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
TIMESTAMP_ENCODED_LENGTH = 10
|
29
|
+
RANDOMNESS_ENCODED_LENGTH = 16
|
30
|
+
ENCODED_LENGTH = TIMESTAMP_ENCODED_LENGTH + RANDOMNESS_ENCODED_LENGTH
|
28
31
|
TIMESTAMP_OCTETS_LENGTH = 6
|
29
32
|
RANDOMNESS_OCTETS_LENGTH = 10
|
30
33
|
OCTETS_LENGTH = TIMESTAMP_OCTETS_LENGTH + RANDOMNESS_OCTETS_LENGTH
|
31
34
|
MAX_MILLISECONDS = 281474976710655
|
32
35
|
MAX_ENTROPY = 1208925819614629174706175
|
33
36
|
MAX_INTEGER = 340282366920938463463374607431768211455
|
34
|
-
PATTERN = /(?<timestamp>[0-7][#{encoding_string}]{#{
|
37
|
+
PATTERN = /(?<timestamp>[0-7][#{encoding_string}]{#{TIMESTAMP_ENCODED_LENGTH - 1}})(?<randomness>[#{encoding_string}]{#{RANDOMNESS_ENCODED_LENGTH}})/i.freeze
|
35
38
|
STRICT_PATTERN = /\A#{PATTERN.source}\z/i.freeze
|
36
39
|
|
37
|
-
# Imported from https://stackoverflow.com/a/38191104/1212807, thank you!
|
38
|
-
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
|
39
|
-
|
40
40
|
# Same as Time#inspect since Ruby 2.7, just to keep backward compatibility
|
41
41
|
# @see https://bugs.ruby-lang.org/issues/15958
|
42
42
|
TIME_FORMAT_IN_INSPECT = '%Y-%m-%d %H:%M:%S.%3N %Z'
|
43
43
|
|
44
44
|
UNDEFINED = BasicObject.new
|
45
|
+
# @return [String]
|
46
|
+
def UNDEFINED.to_s
|
47
|
+
'ULID::UNDEFINED'
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String]
|
51
|
+
def UNDEFINED.inspect
|
52
|
+
to_s
|
53
|
+
end
|
45
54
|
Kernel.instance_method(:freeze).bind(UNDEFINED).call
|
46
55
|
|
56
|
+
private_class_method :new
|
57
|
+
|
47
58
|
# @param [Integer, Time] moment
|
48
59
|
# @param [Integer] entropy
|
49
60
|
# @return [ULID]
|
@@ -51,6 +62,14 @@ class ULID
|
|
51
62
|
new milliseconds: milliseconds_from_moment(moment), entropy: entropy
|
52
63
|
end
|
53
64
|
|
65
|
+
# Short hand of `ULID.generate(moment: time)`
|
66
|
+
# @param [Time] time
|
67
|
+
# @return [ULID]
|
68
|
+
def self.at(time)
|
69
|
+
raise ArgumentError, 'ULID.at takes only `Time` instance' unless Time === time
|
70
|
+
new milliseconds: milliseconds_from_time(time), entropy: reasonable_entropy
|
71
|
+
end
|
72
|
+
|
54
73
|
# @param [Integer, Time] moment
|
55
74
|
# @return [ULID]
|
56
75
|
def self.min(moment: 0)
|
@@ -101,38 +120,23 @@ class ULID
|
|
101
120
|
self
|
102
121
|
end
|
103
122
|
|
104
|
-
# @param [String, #to_str] uuid
|
105
|
-
# @return [ULID]
|
106
|
-
# @raise [ParserError] if the given format is not correct for UUIDv4 specs
|
107
|
-
def self.from_uuidv4(uuid)
|
108
|
-
begin
|
109
|
-
uuid = uuid.to_str
|
110
|
-
prefix_trimmed = uuid.sub(/\Aurn:uuid:/, '')
|
111
|
-
raise "given string is not matched to pattern #{UUIDV4_PATTERN.inspect}" unless UUIDV4_PATTERN.match?(prefix_trimmed)
|
112
|
-
normalized = prefix_trimmed.gsub(/[^0-9A-Fa-f]/, '')
|
113
|
-
from_integer(normalized.to_i(16))
|
114
|
-
rescue => err
|
115
|
-
raise ParserError, "parsing failure as #{err.inspect} for given #{uuid}"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
123
|
# @param [Integer, #to_int] integer
|
120
124
|
# @return [ULID]
|
121
125
|
# @raise [OverflowError] if the given integer is larger than the ULID limit
|
122
126
|
# @raise [ArgumentError] if the given integer is negative number
|
123
|
-
# @todo Need optimized for performance
|
124
127
|
def self.from_integer(integer)
|
125
128
|
integer = integer.to_int
|
126
129
|
raise OverflowError, "integer overflow: given #{integer}, max: #{MAX_INTEGER}" unless integer <= MAX_INTEGER
|
127
130
|
raise ArgumentError, "integer should not be negative: given: #{integer}" if integer.negative?
|
128
131
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
132
|
+
n32encoded = integer.to_s(32).rjust(ENCODED_LENGTH, '0')
|
133
|
+
n32encoded_timestamp = n32encoded.slice(0, TIMESTAMP_ENCODED_LENGTH)
|
134
|
+
n32encoded_randomness = n32encoded.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH)
|
135
|
+
|
136
|
+
milliseconds = n32encoded_timestamp.to_i(32)
|
137
|
+
entropy = n32encoded_randomness.to_i(32)
|
134
138
|
|
135
|
-
new milliseconds: milliseconds, entropy: entropy
|
139
|
+
new milliseconds: milliseconds, entropy: entropy, integer: integer
|
136
140
|
end
|
137
141
|
|
138
142
|
# @param [Range<Time>, Range<nil>] time_range
|
@@ -239,7 +243,7 @@ class ULID
|
|
239
243
|
'Z' => 31
|
240
244
|
}.freeze
|
241
245
|
|
242
|
-
N32_CHAR_BY_CROCKFORD_BASE32_CHAR =
|
246
|
+
N32_CHAR_BY_CROCKFORD_BASE32_CHAR = encoding_chars.each_with_object({}) do |encoding_char, map|
|
243
247
|
if n = crockford_base32_mappings[encoding_char]
|
244
248
|
char_32 = n32_char_by_number.fetch(n)
|
245
249
|
map[encoding_char] = char_32
|
@@ -258,21 +262,12 @@ class ULID
|
|
258
262
|
begin
|
259
263
|
string = string.to_str
|
260
264
|
raise "given argument does not match to `#{STRICT_PATTERN.inspect}`" unless STRICT_PATTERN.match?(string)
|
261
|
-
n32encoded = convert_crockford_base32_to_n32(string.upcase)
|
262
|
-
timestamp = n32encoded.slice(0, TIMESTAMP_PART_LENGTH)
|
263
|
-
randomness = n32encoded.slice(TIMESTAMP_PART_LENGTH, RANDOMNESS_PART_LENGTH)
|
264
|
-
milliseconds = timestamp.to_i(32)
|
265
|
-
entropy = randomness.to_i(32)
|
266
265
|
rescue => err
|
267
266
|
raise ParserError, "parsing failure as #{err.inspect} for given #{string.inspect}"
|
268
267
|
end
|
269
268
|
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
# @api private
|
274
|
-
private_class_method def self.convert_crockford_base32_to_n32(string)
|
275
|
-
string.gsub(CROCKFORD_BASE32_CHAR_PATTERN, N32_CHAR_BY_CROCKFORD_BASE32_CHAR)
|
269
|
+
n32encoded = string.upcase.gsub(CROCKFORD_BASE32_CHAR_PATTERN, N32_CHAR_BY_CROCKFORD_BASE32_CHAR)
|
270
|
+
from_integer(n32encoded.to_i(32))
|
276
271
|
end
|
277
272
|
|
278
273
|
# @return [Boolean]
|
@@ -284,18 +279,6 @@ class ULID
|
|
284
279
|
true
|
285
280
|
end
|
286
281
|
|
287
|
-
# @api private
|
288
|
-
# @param [Integer] integer
|
289
|
-
# @param [Integer] length
|
290
|
-
# @return [Array<Integer>]
|
291
|
-
def self.octets_from_integer(integer, length:)
|
292
|
-
digits = integer.digits(256)
|
293
|
-
(length - digits.size).times do
|
294
|
-
digits.push 0
|
295
|
-
end
|
296
|
-
digits.reverse!
|
297
|
-
end
|
298
|
-
|
299
282
|
# @api private
|
300
283
|
# @see The logics taken from https://bugs.ruby-lang.org/issues/14401, thanks!
|
301
284
|
# @param [Array<Integer>] reversed_digits
|
@@ -309,6 +292,15 @@ class ULID
|
|
309
292
|
num
|
310
293
|
end
|
311
294
|
|
295
|
+
# @api private
|
296
|
+
# @param [MonotonicGenerator] generator
|
297
|
+
# @return [ULID]
|
298
|
+
def self.from_monotonic_generator(generator)
|
299
|
+
raise ArgumentError, 'this method provided only for MonotonicGenerator' unless MonotonicGenerator === generator
|
300
|
+
new milliseconds: generator.latest_milliseconds, entropy: generator.latest_entropy
|
301
|
+
end
|
302
|
+
|
303
|
+
# @api private
|
312
304
|
# @return [ArgumentError]
|
313
305
|
private_class_method def self.argument_error_for_range_building(argument)
|
314
306
|
ArgumentError.new "ULID.range takes only `Range[Time]` or `Range[nil]`, given: #{argument.inspect}"
|
@@ -319,15 +311,21 @@ class ULID
|
|
319
311
|
# @api private
|
320
312
|
# @param [Integer] milliseconds
|
321
313
|
# @param [Integer] entropy
|
314
|
+
# @param [Integer] integer
|
322
315
|
# @return [void]
|
323
316
|
# @raise [OverflowError] if the given value is larger than the ULID limit
|
324
317
|
# @raise [ArgumentError] if the given milliseconds and/or entropy is negative number
|
325
|
-
def initialize(milliseconds:, entropy:)
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
318
|
+
def initialize(milliseconds:, entropy:, integer: UNDEFINED)
|
319
|
+
if UNDEFINED.equal?(integer)
|
320
|
+
milliseconds = milliseconds.to_int
|
321
|
+
entropy = entropy.to_int
|
322
|
+
|
323
|
+
raise OverflowError, "timestamp overflow: given #{milliseconds}, max: #{MAX_MILLISECONDS}" unless milliseconds <= MAX_MILLISECONDS
|
324
|
+
raise OverflowError, "entropy overflow: given #{entropy}, max: #{MAX_ENTROPY}" unless entropy <= MAX_ENTROPY
|
325
|
+
raise ArgumentError, 'milliseconds and entropy should not be negative' if milliseconds.negative? || entropy.negative?
|
326
|
+
else
|
327
|
+
@integer = integer
|
328
|
+
end
|
331
329
|
|
332
330
|
@milliseconds = milliseconds
|
333
331
|
@entropy = entropy
|
@@ -335,18 +333,22 @@ class ULID
|
|
335
333
|
|
336
334
|
# @return [String]
|
337
335
|
def to_s
|
338
|
-
@string ||=
|
336
|
+
@string ||= to_i.to_s(32).upcase.gsub(N32_CHAR_PATTERN, CROCKFORD_BASE32_CHAR_BY_N32_CHAR).rjust(ENCODED_LENGTH, '0').freeze
|
339
337
|
end
|
340
338
|
|
341
339
|
# @return [Integer]
|
342
340
|
def to_i
|
343
|
-
@integer ||=
|
341
|
+
@integer ||= begin
|
342
|
+
n32encoded_timestamp = milliseconds.to_s(32).rjust(TIMESTAMP_ENCODED_LENGTH, '0')
|
343
|
+
n32encoded_randomness = entropy.to_s(32).rjust(RANDOMNESS_ENCODED_LENGTH, '0')
|
344
|
+
(n32encoded_timestamp + n32encoded_randomness).to_i(32)
|
345
|
+
end
|
344
346
|
end
|
345
347
|
alias_method :hash, :to_i
|
346
348
|
|
347
349
|
# @return [Integer, nil]
|
348
350
|
def <=>(other)
|
349
|
-
|
351
|
+
(ULID === other) ? (to_i <=> other.to_i) : nil
|
350
352
|
end
|
351
353
|
|
352
354
|
# @return [String]
|
@@ -356,7 +358,7 @@ class ULID
|
|
356
358
|
|
357
359
|
# @return [Boolean]
|
358
360
|
def eql?(other)
|
359
|
-
|
361
|
+
equal?(other) || (ULID === other && to_i == other.to_i)
|
360
362
|
end
|
361
363
|
alias_method :==, :eql?
|
362
364
|
|
@@ -389,39 +391,49 @@ class ULID
|
|
389
391
|
|
390
392
|
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer)]
|
391
393
|
def octets
|
392
|
-
@octets ||= (
|
394
|
+
@octets ||= octets_from_integer(to_i).freeze
|
393
395
|
end
|
394
396
|
|
395
397
|
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer)]
|
396
398
|
def timestamp_octets
|
397
|
-
@timestamp_octets ||=
|
399
|
+
@timestamp_octets ||= octets.slice(0, TIMESTAMP_OCTETS_LENGTH).freeze
|
398
400
|
end
|
399
401
|
|
400
402
|
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer)]
|
401
403
|
def randomness_octets
|
402
|
-
@randomness_octets ||=
|
404
|
+
@randomness_octets ||= octets.slice(TIMESTAMP_OCTETS_LENGTH, RANDOMNESS_OCTETS_LENGTH).freeze
|
403
405
|
end
|
404
406
|
|
405
407
|
# @return [String]
|
406
408
|
def timestamp
|
407
|
-
@timestamp ||=
|
409
|
+
@timestamp ||= to_s.slice(0, TIMESTAMP_ENCODED_LENGTH).freeze
|
408
410
|
end
|
409
411
|
|
410
412
|
# @return [String]
|
411
413
|
def randomness
|
412
|
-
@randomness ||=
|
414
|
+
@randomness ||= to_s.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH).freeze
|
415
|
+
end
|
416
|
+
|
417
|
+
# @note Providing for rough operations. The keys and values is not fixed.
|
418
|
+
# @return [Hash{Symbol => Regexp, String}]
|
419
|
+
def patterns
|
420
|
+
named_captures = /(?<timestamp>#{timestamp})(?<randomness>#{randomness})/i.freeze
|
421
|
+
{
|
422
|
+
named_captures: named_captures,
|
423
|
+
strict_named_captures: /\A#{named_captures.source}\z/i.freeze
|
424
|
+
}
|
413
425
|
end
|
414
426
|
|
415
|
-
# @deprecated
|
427
|
+
# @deprecated Use {#patterns} instead. ref: https://github.com/kachick/ruby-ulid/issues/84
|
416
428
|
# @return [Regexp]
|
417
429
|
def pattern
|
418
|
-
|
430
|
+
patterns.fetch(:named_captures)
|
419
431
|
end
|
420
432
|
|
421
|
-
# @deprecated
|
433
|
+
# @deprecated Use {#patterns} instead. ref: https://github.com/kachick/ruby-ulid/issues/84
|
422
434
|
# @return [Regexp]
|
423
435
|
def strict_pattern
|
424
|
-
|
436
|
+
patterns.fetch(:strict_named_captures)
|
425
437
|
end
|
426
438
|
|
427
439
|
# @return [ULID, nil] when called on ULID as `7ZZZZZZZZZZZZZZZZZZZZZZZZZ`, returns `nil` instead of ULID
|
@@ -439,17 +451,6 @@ class ULID
|
|
439
451
|
@pred ||= self.class.from_integer(pre_int)
|
440
452
|
end
|
441
453
|
|
442
|
-
# @return [String]
|
443
|
-
def to_uuidv4
|
444
|
-
@uuidv4 ||= begin
|
445
|
-
# This code referenced https://github.com/ruby/ruby/blob/121fa24a3451b45c41ac0a661b64e9fc8600e589/lib/securerandom.rb#L221-L241
|
446
|
-
array = octets.pack('C*').unpack('NnnnnN')
|
447
|
-
array[2] = (array[2] & 0x0fff) | 0x4000
|
448
|
-
array[3] = (array[3] & 0x3fff) | 0x8000
|
449
|
-
('%08x-%04x-%04x-%04x-%04x%08x' % array).freeze
|
450
|
-
end
|
451
|
-
end
|
452
|
-
|
453
454
|
# @return [self]
|
454
455
|
def freeze
|
455
456
|
# Need to cache before freezing, because frozen objects can't assign instance variables
|
@@ -459,14 +460,14 @@ class ULID
|
|
459
460
|
|
460
461
|
private
|
461
462
|
|
462
|
-
# @
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
463
|
+
# @param [Integer] integer
|
464
|
+
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer)]
|
465
|
+
def octets_from_integer(integer)
|
466
|
+
digits = integer.digits(256)
|
467
|
+
(OCTETS_LENGTH - digits.size).times do
|
468
|
+
digits.push 0
|
469
|
+
end
|
470
|
+
digits.reverse!
|
470
471
|
end
|
471
472
|
|
472
473
|
# @return [void]
|
@@ -476,8 +477,8 @@ class ULID
|
|
476
477
|
to_i
|
477
478
|
succ
|
478
479
|
pred
|
479
|
-
|
480
|
-
|
480
|
+
timestamp
|
481
|
+
randomness
|
481
482
|
end
|
482
483
|
end
|
483
484
|
|
@@ -488,5 +489,5 @@ class ULID
|
|
488
489
|
MIN = parse('00000000000000000000000000').freeze
|
489
490
|
MAX = parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').freeze
|
490
491
|
|
491
|
-
private_constant :
|
492
|
+
private_constant :TIME_FORMAT_IN_INSPECT, :MIN, :MAX, :CROCKFORD_BASE32_CHAR_PATTERN, :N32_CHAR_BY_CROCKFORD_BASE32_CHAR, :CROCKFORD_BASE32_CHAR_BY_N32_CHAR, :N32_CHAR_PATTERN, :UNDEFINED
|
492
493
|
end
|
data/lib/ulid/uuid.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# coding: us-ascii
|
2
|
+
# frozen_string_literal: true
|
3
|
+
# Copyright (C) 2021 Kenichi Kamiya
|
4
|
+
|
5
|
+
# Extracted features around UUID from some reasons
|
6
|
+
# ref:
|
7
|
+
# * https://github.com/kachick/ruby-ulid/issues/105
|
8
|
+
# * https://github.com/kachick/ruby-ulid/issues/76
|
9
|
+
class ULID
|
10
|
+
# Imported from https://stackoverflow.com/a/38191104/1212807, thank you!
|
11
|
+
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
|
+
private_constant :UUIDV4_PATTERN
|
13
|
+
|
14
|
+
# @param [String, #to_str] uuid
|
15
|
+
# @return [ULID]
|
16
|
+
# @raise [ParserError] if the given format is not correct for UUIDv4 specs
|
17
|
+
def self.from_uuidv4(uuid)
|
18
|
+
begin
|
19
|
+
uuid = uuid.to_str
|
20
|
+
prefix_trimmed = uuid.sub(/\Aurn:uuid:/, '')
|
21
|
+
raise "given string is not matched to pattern #{UUIDV4_PATTERN.inspect}" unless UUIDV4_PATTERN.match?(prefix_trimmed)
|
22
|
+
normalized = prefix_trimmed.gsub(/[^0-9A-Fa-f]/, '')
|
23
|
+
from_integer(normalized.to_i(16))
|
24
|
+
rescue => err
|
25
|
+
raise ParserError, "parsing failure as #{err.inspect} for given #{uuid}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [String]
|
30
|
+
def to_uuidv4
|
31
|
+
# This code referenced https://github.com/ruby/ruby/blob/121fa24a3451b45c41ac0a661b64e9fc8600e589/lib/securerandom.rb#L221-L241
|
32
|
+
array = octets.pack('C*').unpack('NnnnnN')
|
33
|
+
array[2] = (array[2] & 0x0fff) | 0x4000
|
34
|
+
array[3] = (array[3] & 0x3fff) | 0x8000
|
35
|
+
('%08x-%04x-%04x-%04x-%04x%08x' % array).freeze
|
36
|
+
end
|
37
|
+
end
|
data/lib/ulid/version.rb
CHANGED
data/sig/ulid.rbs
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
# Classes
|
2
2
|
class ULID
|
3
3
|
VERSION: String
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
ENCODED_ID_LENGTH: 26
|
4
|
+
TIMESTAMP_ENCODED_LENGTH: 10
|
5
|
+
RANDOMNESS_ENCODED_LENGTH: 16
|
6
|
+
ENCODED_LENGTH: 26
|
8
7
|
TIMESTAMP_OCTETS_LENGTH: 6
|
9
8
|
RANDOMNESS_OCTETS_LENGTH: 10
|
10
9
|
OCTETS_LENGTH: 16
|
@@ -64,12 +63,9 @@ class ULID
|
|
64
63
|
@inspect: String?
|
65
64
|
@time: Time?
|
66
65
|
@next: ULID?
|
67
|
-
@pattern: Regexp?
|
68
|
-
@strict_pattern: Regexp?
|
69
|
-
@uuidv4: String?
|
70
|
-
@matchdata: MatchData?
|
71
66
|
|
72
67
|
def self.generate: (?moment: moment, ?entropy: Integer) -> ULID
|
68
|
+
def self.at: (Time time) -> ULID
|
73
69
|
def self.current_milliseconds: -> Integer
|
74
70
|
def self.milliseconds_from_time: (Time time) -> Integer
|
75
71
|
def self.milliseconds_from_moment: (moment moment) -> Integer
|
@@ -86,16 +82,17 @@ class ULID
|
|
86
82
|
def self.valid?: (untyped string) -> bool
|
87
83
|
def self.scan: (String string) -> Enumerator[ULID, singleton(ULID)]
|
88
84
|
| (String string) { (ULID ulid) -> void } -> singleton(ULID)
|
89
|
-
def self.octets_from_integer: (Integer integer
|
85
|
+
def self.octets_from_integer: (Integer integer) -> octets
|
90
86
|
def self.inverse_of_digits: (Array[Integer] reversed_digits) -> Integer
|
87
|
+
def self.from_monotonic_generator: (MonotonicGenerator generator) -> ULID
|
91
88
|
attr_reader milliseconds: Integer
|
92
89
|
attr_reader entropy: Integer
|
93
|
-
def initialize: (milliseconds: Integer, entropy: Integer) -> void
|
90
|
+
def initialize: (milliseconds: Integer, entropy: Integer, ?integer: Integer) -> void
|
94
91
|
def to_s: -> String
|
95
92
|
def to_i: -> Integer
|
96
93
|
alias hash to_i
|
97
94
|
def <=>: (ULID other) -> Integer
|
98
|
-
| (untyped other) ->
|
95
|
+
| (untyped other) -> nil
|
99
96
|
def inspect: -> String
|
100
97
|
def eql?: (untyped other) -> bool
|
101
98
|
alias == eql?
|
@@ -103,6 +100,7 @@ class ULID
|
|
103
100
|
def to_time: -> Time
|
104
101
|
def timestamp: -> String
|
105
102
|
def randomness: -> String
|
103
|
+
def patterns: -> Hash[Symbol, Regexp | String]
|
106
104
|
def pattern: -> Regexp
|
107
105
|
def strict_pattern: -> Regexp
|
108
106
|
def octets: -> octets
|
@@ -115,9 +113,6 @@ class ULID
|
|
115
113
|
def freeze: -> self
|
116
114
|
|
117
115
|
private
|
118
|
-
def self.convert_crockford_base32_to_n32: (String) -> String
|
119
116
|
def self.argument_error_for_range_building: (untyped argument) -> ArgumentError
|
120
|
-
def convert_n32_to_crockford_base32: (String) -> String
|
121
|
-
def matchdata: -> MatchData
|
122
117
|
def cache_all_instance_variables: -> void
|
123
118
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-ulid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.18
|
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-
|
11
|
+
date: 2021-05-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rbs
|
@@ -78,6 +78,7 @@ files:
|
|
78
78
|
- README.md
|
79
79
|
- lib/ulid.rb
|
80
80
|
- lib/ulid/monotonic_generator.rb
|
81
|
+
- lib/ulid/uuid.rb
|
81
82
|
- lib/ulid/version.rb
|
82
83
|
- sig/ulid.rbs
|
83
84
|
homepage: https://github.com/kachick/ruby-ulid
|