ruby-ulid 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +86 -98
- data/lib/ulid/crockford_base32.rb +12 -12
- data/lib/ulid/errors.rb +1 -0
- data/lib/ulid/monotonic_generator.rb +15 -15
- data/lib/ulid/utils.rb +6 -24
- data/lib/ulid/uuid/fields.rb +41 -0
- data/lib/ulid/uuid.rb +32 -31
- data/lib/ulid/version.rb +1 -1
- data/lib/ulid.rb +130 -134
- data/sig/ulid.rbs +149 -88
- metadata +8 -8
data/lib/ulid/uuid.rb
CHANGED
@@ -1,45 +1,46 @@
|
|
1
1
|
# coding: us-ascii
|
2
2
|
# frozen_string_literal: true
|
3
|
+
# shareable_constant_value: literal
|
3
4
|
|
4
5
|
# Copyright (C) 2021 Kenichi Kamiya
|
5
6
|
|
6
7
|
require_relative('errors')
|
8
|
+
require_relative('utils')
|
9
|
+
require_relative('uuid/fields')
|
7
10
|
|
8
|
-
# Extracted features around UUID from some reasons
|
9
|
-
# ref:
|
10
|
-
# * https://github.com/kachick/ruby-ulid/issues/105
|
11
|
-
# * https://github.com/kachick/ruby-ulid/issues/76
|
12
11
|
class ULID
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
12
|
+
module UUID
|
13
|
+
BASE_PATTERN = /\A[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\z/i
|
14
|
+
# Imported from https://stackoverflow.com/a/38191104/1212807, thank you!
|
15
|
+
V4_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
|
16
|
+
|
17
|
+
def self.parse_any_to_int(uuidish)
|
18
|
+
encoded = String.try_convert(uuidish)
|
19
|
+
raise(ArgumentError, 'should pass a string for UUID parser') unless encoded
|
20
|
+
|
21
|
+
prefix_trimmed = encoded.delete_prefix('urn:uuid:')
|
22
|
+
unless BASE_PATTERN.match?(prefix_trimmed)
|
23
|
+
raise(ParserError, "given `#{encoded}` does not match to `#{BASE_PATTERN.inspect}`")
|
24
|
+
end
|
25
|
+
|
26
|
+
normalized = prefix_trimmed.gsub(/[^0-9A-Fa-f]/, '')
|
27
|
+
Integer(normalized, 16, exception: true)
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
30
|
+
def self.parse_v4_to_int(uuid)
|
31
|
+
encoded = String.try_convert(uuid)
|
32
|
+
raise(ArgumentError, 'should pass a string for UUID parser') unless encoded
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
ref2, ref3 = array[2], array[3]
|
39
|
-
raise unless Integer === ref2 && Integer === ref3
|
34
|
+
prefix_trimmed = encoded.delete_prefix('urn:uuid:')
|
35
|
+
unless V4_PATTERN.match?(prefix_trimmed)
|
36
|
+
raise(ParserError, "given `#{encoded}` does not match to `#{V4_PATTERN.inspect}`")
|
37
|
+
end
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
('%08x-%04x-%04x-%04x-%04x%08x' % array).freeze
|
39
|
+
parse_any_to_int(encoded)
|
40
|
+
end
|
44
41
|
end
|
42
|
+
|
43
|
+
Ractor.make_shareable(UUID)
|
44
|
+
|
45
|
+
private_constant(:UUID)
|
45
46
|
end
|
data/lib/ulid/version.rb
CHANGED
data/lib/ulid.rb
CHANGED
@@ -9,6 +9,7 @@ require_relative('ulid/version')
|
|
9
9
|
require_relative('ulid/errors')
|
10
10
|
require_relative('ulid/crockford_base32')
|
11
11
|
require_relative('ulid/utils')
|
12
|
+
require_relative('ulid/uuid')
|
12
13
|
require_relative('ulid/monotonic_generator')
|
13
14
|
|
14
15
|
# @see https://github.com/ulid/spec
|
@@ -23,8 +24,6 @@ class ULID
|
|
23
24
|
RANDOMNESS_ENCODED_LENGTH = 16
|
24
25
|
ENCODED_LENGTH = 26
|
25
26
|
|
26
|
-
TIMESTAMP_OCTETS_LENGTH = 6
|
27
|
-
RANDOMNESS_OCTETS_LENGTH = 10
|
28
27
|
OCTETS_LENGTH = 16
|
29
28
|
|
30
29
|
MAX_MILLISECONDS = 281474976710655
|
@@ -33,12 +32,12 @@ class ULID
|
|
33
32
|
|
34
33
|
# @see https://github.com/ulid/spec/pull/57
|
35
34
|
# Currently not used as a constant, but kept as a reference for now.
|
36
|
-
PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /(?<timestamp>[0-7][#{CrockfordBase32::ENCODING_STRING}]{#{TIMESTAMP_ENCODED_LENGTH - 1}})(?<randomness>[#{CrockfordBase32::ENCODING_STRING}]{#{RANDOMNESS_ENCODED_LENGTH}})/i
|
35
|
+
PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /(?<timestamp>[0-7][#{CrockfordBase32::ENCODING_STRING}]{#{TIMESTAMP_ENCODED_LENGTH - 1}})(?<randomness>[#{CrockfordBase32::ENCODING_STRING}]{#{RANDOMNESS_ENCODED_LENGTH}})/i
|
37
36
|
|
38
|
-
STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /\A#{PATTERN_WITH_CROCKFORD_BASE32_SUBSET.source}\z/i
|
37
|
+
STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET = /\A#{PATTERN_WITH_CROCKFORD_BASE32_SUBSET.source}\z/i
|
39
38
|
|
40
39
|
# Optimized for `ULID.scan`, might be changed the definition with gathered `ULID.scan` spec changed.
|
41
|
-
SCANNING_PATTERN = /\b[0-7][#{CrockfordBase32::ENCODING_STRING}]{#{TIMESTAMP_ENCODED_LENGTH - 1}}[#{CrockfordBase32::ENCODING_STRING}]{#{RANDOMNESS_ENCODED_LENGTH}}\b/i
|
40
|
+
SCANNING_PATTERN = /\b[0-7][#{CrockfordBase32::ENCODING_STRING}]{#{TIMESTAMP_ENCODED_LENGTH - 1}}[#{CrockfordBase32::ENCODING_STRING}]{#{RANDOMNESS_ENCODED_LENGTH}}\b/i
|
42
41
|
|
43
42
|
# Similar as Time#inspect since Ruby 2.7, however it is NOT same.
|
44
43
|
# Time#inspect trancates needless digits. Keeping full milliseconds with "%3N" will fit for ULID.
|
@@ -50,17 +49,19 @@ class ULID
|
|
50
49
|
SecureRandom.random_number(MAX_INTEGER)
|
51
50
|
}.freeze
|
52
51
|
|
53
|
-
Utils.
|
52
|
+
Utils.make_sharable_constants(self)
|
54
53
|
|
55
54
|
private_constant(
|
56
55
|
:PATTERN_WITH_CROCKFORD_BASE32_SUBSET,
|
57
56
|
:STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET,
|
58
57
|
:SCANNING_PATTERN,
|
59
58
|
:TIME_FORMAT_IN_INSPECT,
|
60
|
-
:RANDOM_INTEGER_GENERATOR
|
59
|
+
:RANDOM_INTEGER_GENERATOR,
|
60
|
+
:OCTETS_LENGTH,
|
61
|
+
:UUID
|
61
62
|
)
|
62
63
|
|
63
|
-
private_class_method(:new)
|
64
|
+
private_class_method(:new, :allocate)
|
64
65
|
|
65
66
|
# @param [Integer, Time] moment
|
66
67
|
# @param [Integer] entropy
|
@@ -69,12 +70,12 @@ class ULID
|
|
69
70
|
# @raise [ArgumentError] if the given milliseconds and/or entropy is negative number
|
70
71
|
def self.generate(moment: Utils.current_milliseconds, entropy: Utils.reasonable_entropy)
|
71
72
|
milliseconds = Utils.milliseconds_from_moment(moment)
|
72
|
-
|
73
|
+
base32hex = Utils.encode_base32hex(milliseconds:, entropy:)
|
73
74
|
new(
|
74
|
-
milliseconds
|
75
|
-
entropy
|
76
|
-
integer:
|
77
|
-
encoded: CrockfordBase32.
|
75
|
+
milliseconds:,
|
76
|
+
entropy:,
|
77
|
+
integer: Integer(base32hex, 32, exception: true),
|
78
|
+
encoded: CrockfordBase32.from_base32hex(base32hex).freeze
|
78
79
|
)
|
79
80
|
end
|
80
81
|
|
@@ -84,8 +85,8 @@ class ULID
|
|
84
85
|
# @param [Integer] entropy
|
85
86
|
# @return [String]
|
86
87
|
def self.encode(moment: Utils.current_milliseconds, entropy: Utils.reasonable_entropy)
|
87
|
-
|
88
|
-
CrockfordBase32.
|
88
|
+
base32hex = Utils.encode_base32hex(milliseconds: Utils.milliseconds_from_moment(moment), entropy:)
|
89
|
+
CrockfordBase32.from_base32hex(base32hex)
|
89
90
|
end
|
90
91
|
|
91
92
|
# Short hand of `ULID.generate(moment: time)`
|
@@ -100,13 +101,13 @@ class ULID
|
|
100
101
|
# @param [Time, Integer] moment
|
101
102
|
# @return [ULID]
|
102
103
|
def self.min(moment=0)
|
103
|
-
0.equal?(moment) ? MIN : generate(moment
|
104
|
+
0.equal?(moment) ? MIN : generate(moment:, entropy: 0)
|
104
105
|
end
|
105
106
|
|
106
107
|
# @param [Time, Integer] moment
|
107
108
|
# @return [ULID]
|
108
109
|
def self.max(moment=MAX_MILLISECONDS)
|
109
|
-
MAX_MILLISECONDS.equal?(moment) ? MAX : generate(moment
|
110
|
+
MAX_MILLISECONDS.equal?(moment) ? MAX : generate(moment:, entropy: MAX_ENTROPY)
|
110
111
|
end
|
111
112
|
|
112
113
|
# @param [Range<Time>, Range<nil>, Range[ULID], nil] period
|
@@ -181,20 +182,20 @@ class ULID
|
|
181
182
|
raise(OverflowError, "integer overflow: given #{integer}, max: #{MAX_INTEGER}") unless integer <= MAX_INTEGER
|
182
183
|
raise(ArgumentError, "integer should not be negative: given: #{integer}") if integer.negative?
|
183
184
|
|
184
|
-
|
185
|
-
|
186
|
-
|
185
|
+
base32hex = integer.to_s(32).rjust(ENCODED_LENGTH, '0')
|
186
|
+
base32hex_timestamp = base32hex.slice(0, TIMESTAMP_ENCODED_LENGTH)
|
187
|
+
base32hex_randomness = base32hex.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH)
|
187
188
|
|
188
|
-
raise(UnexpectedError) unless
|
189
|
+
raise(UnexpectedError) unless base32hex_timestamp && base32hex_randomness
|
189
190
|
|
190
|
-
milliseconds =
|
191
|
-
entropy =
|
191
|
+
milliseconds = Integer(base32hex_timestamp, 32, exception: true)
|
192
|
+
entropy = Integer(base32hex_randomness, 32, exception: true)
|
192
193
|
|
193
194
|
new(
|
194
|
-
milliseconds
|
195
|
-
entropy
|
196
|
-
integer
|
197
|
-
encoded: CrockfordBase32.
|
195
|
+
milliseconds:,
|
196
|
+
entropy:,
|
197
|
+
integer:,
|
198
|
+
encoded: CrockfordBase32.from_base32hex(base32hex).freeze
|
198
199
|
)
|
199
200
|
end
|
200
201
|
|
@@ -205,12 +206,10 @@ class ULID
|
|
205
206
|
raise(ArgumentError, 'ULID.range takes only `Range[Time]`, `Range[nil]` or `Range[ULID]`') unless Range === period
|
206
207
|
|
207
208
|
begin_element, end_element, exclude_end = period.begin, period.end, period.exclude_end?
|
208
|
-
new_begin, new_end = false, false
|
209
209
|
|
210
210
|
begin_ulid = (
|
211
211
|
case begin_element
|
212
212
|
when Time
|
213
|
-
new_begin = true
|
214
213
|
min(begin_element)
|
215
214
|
when nil
|
216
215
|
MIN
|
@@ -224,7 +223,6 @@ class ULID
|
|
224
223
|
end_ulid = (
|
225
224
|
case end_element
|
226
225
|
when Time
|
227
|
-
new_end = true
|
228
226
|
exclude_end ? min(end_element) : max(end_element)
|
229
227
|
when nil
|
230
228
|
exclude_end = false
|
@@ -237,9 +235,6 @@ class ULID
|
|
237
235
|
end
|
238
236
|
)
|
239
237
|
|
240
|
-
begin_ulid.freeze if new_begin
|
241
|
-
end_ulid.freeze if new_end
|
242
|
-
|
243
238
|
Range.new(begin_ulid, end_ulid, exclude_end)
|
244
239
|
end
|
245
240
|
|
@@ -283,7 +278,7 @@ class ULID
|
|
283
278
|
# @return [Time]
|
284
279
|
# @raise [ParserError] if the given format is not correct for ULID specs
|
285
280
|
def self.decode_time(string, in: 'UTC')
|
286
|
-
in_for_time_at =
|
281
|
+
in_for_time_at = { in: }.fetch(:in)
|
287
282
|
string = String.try_convert(string)
|
288
283
|
raise(ArgumentError, 'ULID.decode_time takes only strings') unless string
|
289
284
|
|
@@ -304,7 +299,7 @@ class ULID
|
|
304
299
|
raise(ArgumentError, 'ULID.normalize takes only strings') unless string
|
305
300
|
|
306
301
|
# Ensure the ULID correctness, because CrockfordBase32 does not always mean to satisfy ULID format
|
307
|
-
parse_variant_format(string).
|
302
|
+
parse_variant_format(string).encode
|
308
303
|
end
|
309
304
|
|
310
305
|
# @param [String, #to_str] string
|
@@ -327,19 +322,6 @@ class ULID
|
|
327
322
|
true
|
328
323
|
end
|
329
324
|
|
330
|
-
# @deprecated Use [.valid_as_variant_format?] or [.normalized?] instead
|
331
|
-
#
|
332
|
-
# Returns `true` if it is normalized string.
|
333
|
-
# Basically the difference of normalized? is to accept downcase or not. This returns true for downcased ULIDs.
|
334
|
-
#
|
335
|
-
# @return [Boolean]
|
336
|
-
def self.valid?(string)
|
337
|
-
warn_kwargs = (RUBY_VERSION >= '3.0') ? { category: :deprecated } : {}
|
338
|
-
Warning.warn('ULID.valid? is deprecated. Use ULID.valid_as_variant_format? or ULID.normalized? instead.', **warn_kwargs)
|
339
|
-
string = String.try_convert(string)
|
340
|
-
string ? STRICT_PATTERN_WITH_CROCKFORD_BASE32_SUBSET.match?(string) : false
|
341
|
-
end
|
342
|
-
|
343
325
|
# @param [ULID, #to_ulid] object
|
344
326
|
# @return [ULID, nil]
|
345
327
|
# @raise [TypeError] if `object.to_ulid` did not return ULID instance
|
@@ -359,7 +341,36 @@ class ULID
|
|
359
341
|
end
|
360
342
|
end
|
361
343
|
|
362
|
-
|
344
|
+
# @param [String, #to_str] uuidish
|
345
|
+
# @return [ULID]
|
346
|
+
# @raise [ParserError] if the given format is not correct for UUID`ish` format
|
347
|
+
def self.from_uuidish(uuidish)
|
348
|
+
from_integer(UUID.parse_any_to_int(uuidish))
|
349
|
+
end
|
350
|
+
|
351
|
+
# @param [String, #to_str] uuid
|
352
|
+
# @return [ULID]
|
353
|
+
# @raise [ParserError] if the given format is not correct for UUIDv4 specs
|
354
|
+
def self.from_uuidv4(uuid)
|
355
|
+
from_integer(UUID.parse_v4_to_int(uuid))
|
356
|
+
end
|
357
|
+
|
358
|
+
attr_reader(:milliseconds, :entropy, :encoded)
|
359
|
+
protected(:encoded)
|
360
|
+
|
361
|
+
# @param [Integer] milliseconds
|
362
|
+
# @param [Integer] entropy
|
363
|
+
# @param [Integer] integer
|
364
|
+
# @param [String] encoded
|
365
|
+
# @return [void]
|
366
|
+
def initialize(milliseconds:, entropy:, integer:, encoded:)
|
367
|
+
# All arguments check should be done with each constructors, not here
|
368
|
+
@integer = integer
|
369
|
+
@encoded = encoded
|
370
|
+
@milliseconds = milliseconds
|
371
|
+
@entropy = entropy
|
372
|
+
freeze
|
373
|
+
end
|
363
374
|
|
364
375
|
# @return [String]
|
365
376
|
def encode
|
@@ -384,7 +395,7 @@ class ULID
|
|
384
395
|
|
385
396
|
# @return [String]
|
386
397
|
def inspect
|
387
|
-
|
398
|
+
"ULID(#{to_time.strftime(TIME_FORMAT_IN_INSPECT)}: #{@encoded})"
|
388
399
|
end
|
389
400
|
|
390
401
|
# @return [Boolean]
|
@@ -417,83 +428,64 @@ class ULID
|
|
417
428
|
end
|
418
429
|
|
419
430
|
# @return [Time]
|
420
|
-
|
421
|
-
|
431
|
+
# @param [String, Integer, nil] in
|
432
|
+
def to_time(in: 'UTC')
|
433
|
+
Time.at(0, @milliseconds, :millisecond, in: { in: }.fetch(:in))
|
422
434
|
end
|
423
435
|
|
424
436
|
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer)]
|
425
437
|
def octets
|
426
438
|
digits = @integer.digits(256)
|
427
|
-
(OCTETS_LENGTH - digits.size).
|
428
|
-
digits.push(0)
|
429
|
-
end
|
430
|
-
digits.reverse!
|
431
|
-
end
|
432
|
-
|
433
|
-
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer)]
|
434
|
-
def timestamp_octets
|
435
|
-
octets.slice(0, TIMESTAMP_OCTETS_LENGTH) || raise(UnexpectedError)
|
436
|
-
end
|
437
|
-
|
438
|
-
# @return [Array(Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer, Integer)]
|
439
|
-
def randomness_octets
|
440
|
-
octets.slice(TIMESTAMP_OCTETS_LENGTH, RANDOMNESS_OCTETS_LENGTH) || raise(UnexpectedError)
|
439
|
+
digits.fill(0, digits.size, OCTETS_LENGTH - digits.size).reverse
|
441
440
|
end
|
442
441
|
|
443
442
|
# @return [String]
|
444
443
|
def timestamp
|
445
|
-
@
|
444
|
+
@encoded.slice(0, TIMESTAMP_ENCODED_LENGTH) || raise(UnexpectedError)
|
446
445
|
end
|
447
446
|
|
448
447
|
# @return [String]
|
449
448
|
def randomness
|
450
|
-
@
|
449
|
+
@encoded.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH) || raise(UnexpectedError)
|
451
450
|
end
|
452
451
|
|
453
|
-
# @
|
454
|
-
# @return [
|
455
|
-
def
|
456
|
-
|
457
|
-
{
|
458
|
-
named_captures: named_captures,
|
459
|
-
strict_named_captures: /\A#{named_captures.source}\z/i.freeze
|
460
|
-
}
|
461
|
-
end
|
452
|
+
# @param [Integer] other
|
453
|
+
# @return [ULID, nil] when returning URID might be greater than `7ZZZZZZZZZZZZZZZZZZZZZZZZZ`, returns `nil` instead of ULID
|
454
|
+
def +(other)
|
455
|
+
raise(ArgumentError, 'ULID#+ takes only integers') unless Integer === other
|
462
456
|
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
457
|
+
new_int = @integer + other
|
458
|
+
case new_int
|
459
|
+
when MAX_INTEGER
|
460
|
+
MAX
|
461
|
+
when 0
|
462
|
+
MIN
|
463
|
+
else
|
464
|
+
if new_int > MAX_INTEGER || new_int < 0
|
470
465
|
nil
|
466
|
+
else
|
467
|
+
ULID.from_integer(new_int)
|
471
468
|
end
|
472
|
-
else
|
473
|
-
ULID.from_integer(succ_int)
|
474
469
|
end
|
475
470
|
end
|
471
|
+
|
472
|
+
# @param [Integer] other
|
473
|
+
# @return [ULID, nil] when returning URID might be less than `00000000000000000000000000`, returns `nil` instead of ULID
|
474
|
+
def -(other)
|
475
|
+
raise(ArgumentError, 'ULID#- takes only integers') unless Integer === other
|
476
|
+
|
477
|
+
self + -other
|
478
|
+
end
|
479
|
+
|
480
|
+
# @return [ULID, nil] when called on ULID as `7ZZZZZZZZZZZZZZZZZZZZZZZZZ`, returns `nil` instead of ULID
|
481
|
+
def succ
|
482
|
+
self + 1
|
483
|
+
end
|
476
484
|
alias_method(:next, :succ)
|
477
485
|
|
478
486
|
# @return [ULID, nil] when called on ULID as `00000000000000000000000000`, returns `nil` instead of ULID
|
479
487
|
def pred
|
480
|
-
|
481
|
-
if pred_int <= 0
|
482
|
-
if pred_int == 0
|
483
|
-
MIN
|
484
|
-
else
|
485
|
-
nil
|
486
|
-
end
|
487
|
-
else
|
488
|
-
ULID.from_integer(pred_int)
|
489
|
-
end
|
490
|
-
end
|
491
|
-
|
492
|
-
# @return [self]
|
493
|
-
def freeze
|
494
|
-
# Need to cache before freezing, because frozen objects can't assign instance variables
|
495
|
-
cache_all_instance_variables
|
496
|
-
super
|
488
|
+
self - 1
|
497
489
|
end
|
498
490
|
|
499
491
|
# @return [Integer]
|
@@ -509,7 +501,7 @@ class ULID
|
|
509
501
|
integer: unmarshaled.to_i,
|
510
502
|
milliseconds: unmarshaled.milliseconds,
|
511
503
|
entropy: unmarshaled.entropy,
|
512
|
-
encoded: unmarshaled.
|
504
|
+
encoded: unmarshaled.encoded
|
513
505
|
)
|
514
506
|
end
|
515
507
|
|
@@ -518,45 +510,49 @@ class ULID
|
|
518
510
|
self
|
519
511
|
end
|
520
512
|
|
521
|
-
#
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
def clone(freeze: true)
|
528
|
-
self
|
513
|
+
# Generate a UUID-like string that does not set the version and variants field.
|
514
|
+
# It means wrong in UUIDv4 spec, but reversible
|
515
|
+
#
|
516
|
+
# @return [String]
|
517
|
+
def to_uuidish
|
518
|
+
UUID::Fields.raw_from_octets(octets).to_s.freeze
|
529
519
|
end
|
530
520
|
|
531
|
-
|
521
|
+
# Generate a UUIDv4-like string that sets the version and variants field.
|
522
|
+
# It may conform to the UUID specification, but it is irreversible with the source ULID and may conflict with some other ULIDs.
|
523
|
+
# You can specify `force` keyword argument to turn off the irreversible check
|
524
|
+
#
|
525
|
+
# @raise [IrreversibleUUIDError] if the converted UUID cannot be reversible with the replacing above 2 fields
|
526
|
+
# @see https://github.com/kachick/ruby-ulid/issues/76
|
527
|
+
# @param [bool] force
|
528
|
+
# @return [String]
|
529
|
+
def to_uuidv4(force: false)
|
530
|
+
v4 = UUID::Fields.forced_v4_from_octets(octets)
|
531
|
+
unless force
|
532
|
+
uuidish = UUID::Fields.raw_from_octets(octets)
|
533
|
+
raise(IrreversibleUUIDError) unless uuidish == v4
|
534
|
+
end
|
532
535
|
|
533
|
-
|
536
|
+
v4.to_s.freeze
|
537
|
+
end
|
534
538
|
|
535
|
-
# @
|
536
|
-
|
537
|
-
|
538
|
-
# @param [String] encoded
|
539
|
-
# @return [void]
|
540
|
-
def initialize(milliseconds:, entropy:, integer:, encoded:)
|
541
|
-
# All arguments check should be done with each constructors, not here
|
542
|
-
@integer = integer
|
543
|
-
@encoded = encoded
|
544
|
-
@milliseconds = milliseconds
|
545
|
-
@entropy = entropy
|
539
|
+
# @return [ULID]
|
540
|
+
def dup
|
541
|
+
super.freeze
|
546
542
|
end
|
547
543
|
|
548
|
-
# @return [
|
549
|
-
def
|
550
|
-
|
551
|
-
|
552
|
-
|
544
|
+
# @return [ULID]
|
545
|
+
def clone(freeze: true)
|
546
|
+
raise(ArgumentError, 'unfreezing ULID is an unexpected operation') unless freeze == true
|
547
|
+
|
548
|
+
super
|
553
549
|
end
|
554
550
|
|
555
|
-
MIN = parse('00000000000000000000000000')
|
556
|
-
MAX = parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ')
|
551
|
+
MIN = parse('00000000000000000000000000')
|
552
|
+
MAX = parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ')
|
557
553
|
|
558
|
-
|
559
|
-
|
554
|
+
Ractor.make_shareable(MIN)
|
555
|
+
Ractor.make_shareable(MAX)
|
560
556
|
|
561
557
|
private_constant(:MIN, :MAX)
|
562
558
|
end
|