ruby-ulid 0.0.17 → 0.0.18
Sign up to get free protection for your applications and to get access to all the features.
- 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
|