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.
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
- # Imported from https://stackoverflow.com/a/38191104/1212807, thank you!
14
- 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
15
- Utils.make_sharable_value(UUIDV4_PATTERN)
16
- private_constant(:UUIDV4_PATTERN)
17
-
18
- # @param [String, #to_str] uuid
19
- # @return [ULID]
20
- # @raise [ParserError] if the given format is not correct for UUIDv4 specs
21
- def self.from_uuidv4(uuid)
22
- uuid = String.try_convert(uuid)
23
- raise(ArgumentError, 'ULID.from_uuidv4 takes only strings') unless uuid
24
-
25
- prefix_trimmed = uuid.delete_prefix('urn:uuid:')
26
- unless UUIDV4_PATTERN.match?(prefix_trimmed)
27
- raise(ParserError, "given `#{uuid}` does not match to `#{UUIDV4_PATTERN.inspect}`")
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
- normalized = prefix_trimmed.gsub(/[^0-9A-Fa-f]/, '')
31
- from_integer(normalized.to_i(16))
32
- end
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
- # @return [String]
35
- def to_uuidv4
36
- # This code referenced https://github.com/ruby/ruby/blob/121fa24a3451b45c41ac0a661b64e9fc8600e589/lib/securerandom.rb#L221-L241
37
- array = octets.pack('C*').unpack('NnnnnN')
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
- array[2] = (ref2 & 0x0fff) | 0x4000
42
- array[3] = (ref3 & 0x3fff) | 0x8000
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
@@ -3,5 +3,5 @@
3
3
  # shareable_constant_value: literal
4
4
 
5
5
  class ULID
6
- VERSION = '0.7.0'
6
+ VERSION = '0.9.0'
7
7
  end
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.freeze
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.freeze
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.freeze
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.make_sharable_constantans(self)
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
- base32_encoded = Utils.encode_base32(milliseconds: milliseconds, entropy: entropy)
73
+ base32hex = Utils.encode_base32hex(milliseconds:, entropy:)
73
74
  new(
74
- milliseconds: milliseconds,
75
- entropy: entropy,
76
- integer: base32_encoded.to_i(32),
77
- encoded: CrockfordBase32.from_base32(base32_encoded).freeze
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
- base32_encoded = Utils.encode_base32(milliseconds: Utils.milliseconds_from_moment(moment), entropy: entropy)
88
- CrockfordBase32.from_base32(base32_encoded)
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: moment, entropy: 0)
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: moment, entropy: MAX_ENTROPY)
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
- base32encoded = integer.to_s(32).rjust(ENCODED_LENGTH, '0')
185
- base32encoded_timestamp = base32encoded.slice(0, TIMESTAMP_ENCODED_LENGTH)
186
- base32encoded_randomness = base32encoded.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH)
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 base32encoded_timestamp && base32encoded_randomness
189
+ raise(UnexpectedError) unless base32hex_timestamp && base32hex_randomness
189
190
 
190
- milliseconds = base32encoded_timestamp.to_i(32)
191
- entropy = base32encoded_randomness.to_i(32)
191
+ milliseconds = Integer(base32hex_timestamp, 32, exception: true)
192
+ entropy = Integer(base32hex_randomness, 32, exception: true)
192
193
 
193
194
  new(
194
- milliseconds: milliseconds,
195
- entropy: entropy,
196
- integer: integer,
197
- encoded: CrockfordBase32.from_base32(base32encoded).freeze
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 = binding.local_variable_get(:in) # Needed because `in` is a reserved word.
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).to_s
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
- attr_reader(:milliseconds, :entropy)
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
- @inspect ||= "ULID(#{to_time.strftime(TIME_FORMAT_IN_INSPECT)}: #{@encoded})".freeze
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
- def to_time
421
- @time ||= Time.at(0, @milliseconds, :millisecond, in: 'UTC').freeze
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).times do
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
- @timestamp ||= (@encoded.slice(0, TIMESTAMP_ENCODED_LENGTH).freeze || raise(UnexpectedError))
444
+ @encoded.slice(0, TIMESTAMP_ENCODED_LENGTH) || raise(UnexpectedError)
446
445
  end
447
446
 
448
447
  # @return [String]
449
448
  def randomness
450
- @randomness ||= (@encoded.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH).freeze || raise(UnexpectedError))
449
+ @encoded.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH) || raise(UnexpectedError)
451
450
  end
452
451
 
453
- # @note Providing for rough operations. The keys and values is not fixed.
454
- # @return [Hash{Symbol => Regexp, String}]
455
- def patterns
456
- named_captures = /(?<timestamp>#{timestamp})(?<randomness>#{randomness})/i.freeze
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
- # @return [ULID, nil] when called on ULID as `7ZZZZZZZZZZZZZZZZZZZZZZZZZ`, returns `nil` instead of ULID
464
- def succ
465
- succ_int = @integer.succ
466
- if succ_int >= MAX_INTEGER
467
- if succ_int == MAX_INTEGER
468
- MAX
469
- else
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
- pred_int = @integer.pred
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.to_s
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
- # @return [self]
522
- def dup
523
- self
524
- end
525
-
526
- # @return [self]
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
- undef_method(:instance_variable_set)
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
- private
536
+ v4.to_s.freeze
537
+ end
534
538
 
535
- # @param [Integer] milliseconds
536
- # @param [Integer] entropy
537
- # @param [Integer] integer
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 [void]
549
- def cache_all_instance_variables
550
- inspect
551
- timestamp
552
- randomness
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').freeze
556
- MAX = parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').freeze
551
+ MIN = parse('00000000000000000000000000')
552
+ MAX = parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ')
557
553
 
558
- Utils.make_sharable_value(MIN)
559
- Utils.make_sharable_value(MAX)
554
+ Ractor.make_shareable(MIN)
555
+ Ractor.make_shareable(MAX)
560
556
 
561
557
  private_constant(:MIN, :MAX)
562
558
  end