ruby-ulid 0.1.0 → 0.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0aff39d0f8044f373d8dcdea149f26501e0118dfc0876f060b1b757a4d2a49e
4
- data.tar.gz: 02df0ea7a5fe5f8dabd1179988cfe4c2574debf3ad7200d11445eef932dd7443
3
+ metadata.gz: 01dca4f18f5bb3168b135847b6a1171d00a6ceb03b1d2ef68b6df212975cafc1
4
+ data.tar.gz: e1b0cb033ded88591d4817395507f517f46b7d3dd3d3a72021a216dcfef09d12
5
5
  SHA512:
6
- metadata.gz: ab036ed40e4f2740de9d37e44d2c6d4d233fa0071f95383accb2334c4d1f85b30b24eea902e0e29c34fefc8749b8caf5d1f9df6c52f93fe2e76c89aa0912d11e
7
- data.tar.gz: d8ac11f63e5b301ed8b1eacd0d1211fd4b3a0bd79ccae8fe87050833cf305a7345da1dc136b95ecccfcafc6110cf9a281afb0f15db1fe2e01984bf320f82b7e5
6
+ metadata.gz: c9c1a60687c05cfa99ce37f4e692938bff96dd15afa6b0adce40de5b897303b6e57301f069299c06d726bdd7a425e1ed581b7b60e15efc88d69abc417c577005
7
+ data.tar.gz: 71f333e7a6d6169f346b0e8fba65c9413fa24c32ce6799623e11b789544fb057fa0f67f83ed94a896cbac9df123ee4f9c9598735953217bd6fec332df659ee9f
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
  ![ULIDlogo](https://raw.githubusercontent.com/kachick/ruby-ulid/main/logo.png)
12
12
 
13
- ![Build Status](https://github.com/kachick/ruby-ulid/actions/workflows/test.yml/badge.svg?branch=main)
13
+ ![Build Status](https://github.com/kachick/ruby-ulid/actions/workflows/test_behaviors.yml/badge.svg?branch=main)
14
14
  [![Gem Version](https://badge.fury.io/rb/ruby-ulid.png)](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) # See also exists issues in [Note](#note)
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.0', '< 0.2.0'
52
+ gem 'ruby-ulid', '>= 0.1.5', '< 0.2.0'
53
53
  ```
54
54
 
55
55
  ### Generator and Parser
@@ -146,6 +146,8 @@ 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 [Monitor](https://bugs.ruby-lang.org/issues/16255)
150
+
149
151
  ### Filtering IDs with `Time`
150
152
 
151
153
  `ULID` can be element of the `Range`. If you generated the IDs in monotonic generator, ID based filtering is easy and reliable
@@ -168,7 +170,7 @@ exclude_end = ULID.range(time1...time2) #=> The end of `Range[ULID]` will be the
168
170
 
169
171
  # Below patterns are acceptable
170
172
  pinpointing = ULID.range(time1..time1) #=> This will match only for all IDs in `time1`
171
- 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)
172
174
  until_the_end = ULID.range(ULID.min.to_time..time1) #=> This is same as above for Ruby 2.6
173
175
  until_the_ulid_limit = ULID.range(time1..) # This will match only for all IDs from `time1` to max value of the ULID limit
174
176
 
@@ -190,7 +192,7 @@ ULID.floor(time) #=> 2000-01-01 00:00:00.123 UTC
190
192
  For rough operations, `ULID.scan` might be useful.
191
193
 
192
194
  ```ruby
193
- json =<<'EOD'
195
+ json = <<'JSON'
194
196
  {
195
197
  "id": "01F4GNAV5ZR6FJQ5SFQC7WDSY3",
196
198
  "author": {
@@ -215,7 +217,7 @@ json =<<'EOD'
215
217
  }
216
218
  ]
217
219
  }
218
- EOD
220
+ JSON
219
221
 
220
222
  ULID.scan(json).to_a
221
223
  #=>
@@ -311,7 +313,7 @@ ulids.take(10)
311
313
  # ULID(2021-04-29 03:18:24.152 UTC: 01F4DT4Z4RA0QV8WFQGRAG63EH),
312
314
  # ULID(2021-05-02 13:27:16.394 UTC: 01F4PM605ABF5SDVMEHBH8JJ9R)]
313
315
  ULID.sample(10, period: ulid1.to_time..ulid2.to_time)
314
- #=>
316
+ #=>
315
317
  # [ULID(2021-04-29 06:44:41.513 UTC: 01F4E5YPD9XQ3MYXWK8ZJKY8SW),
316
318
  # ULID(2021-05-01 00:35:06.629 UTC: 01F4JNKD85SVK1EAEYSJGF53A2),
317
319
  # ULID(2021-05-02 12:45:28.408 UTC: 01F4PHSEYRG9BWBEWMRW1XE6WW),
@@ -324,6 +326,34 @@ ULID.sample(10, period: ulid1.to_time..ulid2.to_time)
324
326
  # ULID(2021-04-28 15:05:06.808 UTC: 01F4CG68ZRST94T056KRZ5K9S4)]
325
327
  ```
326
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
+
327
357
  ### UUIDv4 converter for migration use-cases
328
358
 
329
359
  `ULID.from_uuidv4` and `ULID#to_uuidv4` is the converter.
@@ -333,7 +363,7 @@ The imported timestamp is meaningless. So ULID's benefit will lost.
333
363
  # Currently experimental feature, so needed to load the extension.
334
364
  require 'ulid/uuid'
335
365
 
336
- # Basically reversible
366
+ # Basically reversible
337
367
  ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39') #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
338
368
  ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
339
369
 
@@ -401,6 +431,12 @@ NOTE: It is still having precision issue similar as `ulid gem` in the both gener
401
431
  1. [Fix to handle timestamp precision in parser](https://github.com/abachman/ulid-ruby/pull/5)
402
432
  1. [Fix to handle timestamp precision in generator](https://github.com/abachman/ulid-ruby/pull/4)
403
433
 
434
+ ### Compare performance with them
435
+
436
+ See [Benchmark](https://github.com/kachick/ruby-ulid/wiki/Benchmark).
437
+
438
+ The results are not something to be proud of.
439
+
404
440
  ## References
405
441
 
406
442
  - [Repository](https://github.com/kachick/ruby-ulid)
@@ -410,4 +446,3 @@ NOTE: It is still having precision issue similar as `ulid gem` in the both gener
410
446
  ## Note
411
447
 
412
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)
413
- - 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/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'
@@ -15,6 +16,7 @@ class ULID
15
16
  class Error < StandardError; end
16
17
  class OverflowError < Error; end
17
18
  class ParserError < Error; end
19
+ class UnexpectedError < Error; end
18
20
 
19
21
  # Excluded I, L, O, U, -.
20
22
  # This is the encoding patterns.
@@ -59,6 +61,7 @@ class ULID
59
61
  # @return [ULID]
60
62
  def self.at(time)
61
63
  raise ArgumentError, 'ULID.at takes only `Time` instance' unless Time === time
64
+
62
65
  from_milliseconds_and_entropy(milliseconds: milliseconds_from_time(time), entropy: reasonable_entropy)
63
66
  end
64
67
 
@@ -90,19 +93,21 @@ class ULID
90
93
  # * Do not take random generator for the arguments
91
94
  # * Raising error instead of truncating elements for the given number
92
95
  def self.sample(*args, period: nil)
93
- int_generator = if period
94
- ulid_range = range(period)
95
- min, max, exclude_end = ulid_range.begin.to_i, ulid_range.end.to_i, ulid_range.exclude_end?
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?
96
100
 
97
- possibilities = (max - min) + (exclude_end ? 0 : 1)
98
- raise ArgumentError, "given range `#{ulid_range.inspect}` does not have possibilities" unless possibilities.positive?
101
+ possibilities = (max - min) + (exclude_end ? 0 : 1)
102
+ raise ArgumentError, "given range `#{ulid_range.inspect}` does not have possibilities" unless possibilities.positive?
99
103
 
100
- -> {
101
- SecureRandom.random_number(possibilities) + min
102
- }
103
- else
104
- RANDOM_INTEGER_GENERATOR
105
- end
104
+ -> {
105
+ SecureRandom.random_number(possibilities) + min
106
+ }
107
+ else
108
+ RANDOM_INTEGER_GENERATOR
109
+ end
110
+ )
106
111
 
107
112
  case args.size
108
113
  when 0
@@ -133,6 +138,7 @@ class ULID
133
138
  string = String.try_convert(string)
134
139
  raise ArgumentError, 'ULID.scan takes only strings' unless string
135
140
  return to_enum(__callee__, string) unless block_given?
141
+
136
142
  string.scan(SCANNING_PATTERN) do |matched|
137
143
  yield parse(matched)
138
144
  end
@@ -155,7 +161,7 @@ class ULID
155
161
  milliseconds = n32encoded_timestamp.to_i(32)
156
162
  entropy = n32encoded_randomness.to_i(32)
157
163
 
158
- new milliseconds: milliseconds, entropy: entropy, integer: integer
164
+ new(milliseconds: milliseconds, entropy: entropy, integer: integer)
159
165
  end
160
166
 
161
167
  # @param [Range<Time>, Range<nil>, Range[ULID]] period
@@ -163,6 +169,7 @@ class ULID
163
169
  # @raise [ArgumentError] if the given period is not a `Range[Time]`, `Range[nil]` or `Range[ULID]`
164
170
  def self.range(period)
165
171
  raise ArgumentError, 'ULID.range takes only `Range[Time]`, `Range[nil]` or `Range[ULID]`' unless Range === period
172
+
166
173
  begin_element, end_element, exclude_end = period.begin, period.end, period.exclude_end?
167
174
  return period if self === begin_element && self === end_element
168
175
 
@@ -179,11 +186,7 @@ class ULID
179
186
 
180
187
  case end_element
181
188
  when Time
182
- if exclude_end
183
- end_ulid = min(end_element)
184
- else
185
- end_ulid = max(end_element)
186
- end
189
+ end_ulid = exclude_end ? min(end_element) : max(end_element)
187
190
  when nil
188
191
  # The end should be max and include end, because nil end means to cover endless ULIDs until the limit
189
192
  end_ulid = MAX
@@ -239,9 +242,8 @@ class ULID
239
242
  end
240
243
  end
241
244
 
242
- # @api private
243
245
  # @return [Integer]
244
- def self.reasonable_entropy
246
+ private_class_method def self.reasonable_entropy
245
247
  SecureRandom.random_number(MAX_ENTROPY)
246
248
  end
247
249
 
@@ -260,12 +262,65 @@ class ULID
260
262
  end
261
263
 
262
264
  # @param [String, #to_str] string
263
- # @return [Boolean]
264
- def self.valid?(string)
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
 
291
+ # @param [ULID, #to_ulid] object
292
+ # @return [ULID, nil]
293
+ # @raise [TypeError] if `object.to_ulid` did not return ULID instance
294
+ def self.try_convert(object)
295
+ begin
296
+ converted = object.to_ulid
297
+ rescue NoMethodError
298
+ nil
299
+ else
300
+ if ULID === converted
301
+ converted
302
+ else
303
+ object_class_name = safe_get_class_name(object)
304
+ converted_class_name = safe_get_class_name(converted)
305
+ raise TypeError, "can't convert #{object_class_name} to ULID (#{object_class_name}#to_ulid gives #{converted_class_name})"
306
+ end
307
+ end
308
+ end
309
+
310
+ # @param [BasicObject] object
311
+ # @return [String]
312
+ private_class_method def self.safe_get_class_name(object)
313
+ fallback = 'UnknownObject'
314
+
315
+ begin
316
+ name = String.try_convert(object.class.name)
317
+ rescue Exception
318
+ fallback
319
+ else
320
+ name || fallback
321
+ end
322
+ end
323
+
269
324
  # @api private
270
325
  # @param [Integer] milliseconds
271
326
  # @param [Integer] entropy
@@ -282,7 +337,7 @@ class ULID
282
337
  n32encoded_randomness = entropy.to_s(32).rjust(RANDOMNESS_ENCODED_LENGTH, '0')
283
338
  integer = (n32encoded_timestamp + n32encoded_randomness).to_i(32)
284
339
 
285
- new milliseconds: milliseconds, entropy: entropy, integer: integer
340
+ new(milliseconds: milliseconds, entropy: entropy, integer: integer)
286
341
  end
287
342
 
288
343
  attr_reader :milliseconds, :entropy
@@ -353,7 +408,7 @@ class ULID
353
408
  def octets
354
409
  digits = @integer.digits(256)
355
410
  (OCTETS_LENGTH - digits.size).times do
356
- digits.push 0
411
+ digits.push(0)
357
412
  end
358
413
  digits.reverse!
359
414
  end
@@ -424,6 +479,25 @@ class ULID
424
479
  super
425
480
  end
426
481
 
482
+ # @api private
483
+ # @return [Integer]
484
+ def marshal_dump
485
+ @integer
486
+ end
487
+
488
+ # @api private
489
+ # @param [Integer] integer
490
+ # @return [void]
491
+ def marshal_load(integer)
492
+ unmarshaled = ULID.from_integer(integer)
493
+ initialize(integer: unmarshaled.to_i, milliseconds: unmarshaled.milliseconds, entropy: unmarshaled.entropy)
494
+ end
495
+
496
+ # @return [self]
497
+ def to_ulid
498
+ self
499
+ end
500
+
427
501
  # @return [self]
428
502
  def dup
429
503
  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
- # Currently supporting only for `subset` for actual use-case`
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,43 +1,69 @@
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
7
- # @api private
8
- attr_accessor :latest_milliseconds, :latest_entropy
8
+ include MonitorMixin
9
+
10
+ # @return [ULID, nil]
11
+ attr_reader :prev
12
+
13
+ undef_method :instance_variable_set
9
14
 
10
15
  def initialize
11
- @mutex = Thread::Mutex.new
12
- reset
16
+ super()
17
+ @prev = nil
13
18
  end
14
19
 
20
+ # @return [String]
21
+ def inspect
22
+ "ULID::MonotonicGenerator(prev: #{@prev.inspect})"
23
+ end
24
+ alias_method :to_s, :inspect
25
+
15
26
  # @param [Time, Integer] moment
16
27
  # @return [ULID]
17
28
  # @raise [OverflowError] if the entropy part is larger than the ULID limit in same milliseconds
18
- # @raise [ArgumentError] if the given moment(milliseconds) is negative number
29
+ # @raise [UnexpectedError] if the generated ULID is an invalid value in monotonicity spec.
30
+ # Basically will not happen. Just means this feature prefers error rather than invalid value.
19
31
  def generate(moment: ULID.current_milliseconds)
20
- milliseconds = ULID.milliseconds_from_moment(moment)
21
- raise ArgumentError, "milliseconds should not be negative: given: #{milliseconds}" if milliseconds.negative?
22
-
23
- @mutex.synchronize do
24
- if @latest_milliseconds < milliseconds
25
- @latest_milliseconds = milliseconds
26
- @latest_entropy = ULID.reasonable_entropy
27
- else
28
- @latest_entropy += 1
32
+ synchronize do
33
+ unless @prev
34
+ @prev = ULID.generate(moment: moment)
35
+ return @prev
29
36
  end
30
- ULID.from_milliseconds_and_entropy(milliseconds: @latest_milliseconds, entropy: @latest_entropy)
37
+
38
+ milliseconds = ULID.milliseconds_from_moment(moment)
39
+
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
+ )
47
+
48
+ unless ulid > @prev
49
+ base_message = "monotonicity broken from unexpected reasons # generated: #{ulid.inspect}, prev: #{@prev.inspect}"
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
+ )
57
+
58
+ raise UnexpectedError, base_message + additional_information
59
+ end
60
+
61
+ @prev = ulid
62
+ ulid
31
63
  end
32
64
  end
33
65
 
34
- # @api private
35
- # @return [void]
36
- def reset
37
- @latest_milliseconds = 0
38
- @latest_entropy = ULID.reasonable_entropy
39
- nil
40
- end
66
+ undef_method :freeze
41
67
 
42
68
  # @raise [TypeError] always raises exception and does not freeze self
43
69
  # @return [void]