ruby-ulid 0.1.1 → 0.1.6

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: d9517a4c0fe5e9feec91b2d8e19366baed6b8f1c1ccb42640389c1ad11066854
4
- data.tar.gz: 24344ffd2549f7eae76f6f344169eb74baf3168e05499a9d7577a18fea63fc4d
3
+ metadata.gz: 6e67f6449c010a7c85ff8050a081b21e824ed876d7695a4fa22beb25f2a62534
4
+ data.tar.gz: f0b0607131f8b04dd57c587746cd992a9a01070cacb53857fca6efb7dd11fe45
5
5
  SHA512:
6
- metadata.gz: 5420da3cddc622a02a9d84ba398c17f59c733eea62d122aef0e5fb39dfade8e76cb3e4608814a0afb5c2e0251c6ebbbb2a10c6b27ea68b4ff8e32ad9f2e31ee3
7
- data.tar.gz: 7bb4f8f941ed3f3ec2d5ebef03b4559b1ba2b07eaf5a9970ee3936ba6157bf5ff818eabc63847ba973536910bd6e8b8c65ae3382b74a368af85e228fec6524dd
6
+ metadata.gz: 5c1e3540e8e4f75c92fbc8347d2898fe8f8f233830a878c67462f8f9c9f065c969a9df8dd93e11bd27baa7a5cb7c1cb1eaf2fdf1e7dba5f5833e6295cd9d083f
7
+ data.tar.gz: cd70d28d9cba7270ddf61dcf46bba1c4a04328d828ba7ed32a39c68c01f460b36f9f85da74fdb1d1b742309fdffb29d118cc8bc238c1744b61dbdc3a32255475
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.1', '< 0.2.0'
52
+ gem 'ruby-ulid', '>= 0.1.6', '< 0.2.0'
53
53
  ```
54
54
 
55
55
  ### Generator and Parser
@@ -146,7 +146,7 @@ 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 [Thread::Mutex](https://github.com/ruby/ruby/blob/5f8bca32571fa9c651f6903d36f66082363f8879/thread_sync.c#L1572-L1582)
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
150
 
151
151
  ### Filtering IDs with `Time`
152
152
 
@@ -170,7 +170,7 @@ exclude_end = ULID.range(time1...time2) #=> The end of `Range[ULID]` will be the
170
170
 
171
171
  # Below patterns are acceptable
172
172
  pinpointing = ULID.range(time1..time1) #=> This will match only for all IDs in `time1`
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)
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)
174
174
  until_the_end = ULID.range(ULID.min.to_time..time1) #=> This is same as above for Ruby 2.6
175
175
  until_the_ulid_limit = ULID.range(time1..) # This will match only for all IDs from `time1` to max value of the ULID limit
176
176
 
@@ -192,7 +192,7 @@ ULID.floor(time) #=> 2000-01-01 00:00:00.123 UTC
192
192
  For rough operations, `ULID.scan` might be useful.
193
193
 
194
194
  ```ruby
195
- json =<<'EOD'
195
+ json = <<'JSON'
196
196
  {
197
197
  "id": "01F4GNAV5ZR6FJQ5SFQC7WDSY3",
198
198
  "author": {
@@ -217,7 +217,7 @@ json =<<'EOD'
217
217
  }
218
218
  ]
219
219
  }
220
- EOD
220
+ JSON
221
221
 
222
222
  ULID.scan(json).to_a
223
223
  #=>
@@ -313,7 +313,7 @@ ulids.take(10)
313
313
  # ULID(2021-04-29 03:18:24.152 UTC: 01F4DT4Z4RA0QV8WFQGRAG63EH),
314
314
  # ULID(2021-05-02 13:27:16.394 UTC: 01F4PM605ABF5SDVMEHBH8JJ9R)]
315
315
  ULID.sample(10, period: ulid1.to_time..ulid2.to_time)
316
- #=>
316
+ #=>
317
317
  # [ULID(2021-04-29 06:44:41.513 UTC: 01F4E5YPD9XQ3MYXWK8ZJKY8SW),
318
318
  # ULID(2021-05-01 00:35:06.629 UTC: 01F4JNKD85SVK1EAEYSJGF53A2),
319
319
  # ULID(2021-05-02 12:45:28.408 UTC: 01F4PHSEYRG9BWBEWMRW1XE6WW),
@@ -326,6 +326,34 @@ ULID.sample(10, period: ulid1.to_time..ulid2.to_time)
326
326
  # ULID(2021-04-28 15:05:06.808 UTC: 01F4CG68ZRST94T056KRZ5K9S4)]
327
327
  ```
328
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
+
329
357
  ### UUIDv4 converter for migration use-cases
330
358
 
331
359
  `ULID.from_uuidv4` and `ULID#to_uuidv4` is the converter.
@@ -335,7 +363,7 @@ The imported timestamp is meaningless. So ULID's benefit will lost.
335
363
  # Currently experimental feature, so needed to load the extension.
336
364
  require 'ulid/uuid'
337
365
 
338
- # Basically reversible
366
+ # Basically reversible
339
367
  ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39') #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
340
368
  ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
341
369
 
@@ -403,81 +431,11 @@ NOTE: It is still having precision issue similar as `ulid gem` in the both gener
403
431
  1. [Fix to handle timestamp precision in parser](https://github.com/abachman/ulid-ruby/pull/5)
404
432
  1. [Fix to handle timestamp precision in generator](https://github.com/abachman/ulid-ruby/pull/4)
405
433
 
406
- ### Generating benchmarks
407
-
408
- This runs rough benchmarks
409
-
410
- ```console
411
- $ rake benchmark_with_other_gems
412
- (Do not use `bundle exec`!)
413
- ```
434
+ ### Compare performance with them
414
435
 
415
- <details>
416
- <summary>One of the result at 2021/05/10 on my machine</summary>
417
-
418
- ```plaintext
419
- #### rafaelsales - ulid
420
- cd ./benchmark/compare_with_othergems/rafaelsales && bundle install --quiet && bundle exec ruby -v ./generate.rb
421
- ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
422
- Warming up --------------------------------------
423
- ULID.generate 5.560k i/100ms
424
- Calculating -------------------------------------
425
- ULID.generate 52.655k (±11.0%) i/s - 261.320k in 5.029719s
426
- "`ulid gem - 1.3.0` generated products: 371927 - sample: [\"01F59Y97807D2S67KE6X7ATK7Z\", \"01F59Y9AVRQJFAT5M2N7Z72BVF\", \"01F59Y95Z1042X4Z1K9729BSE3\", \"01F59Y95ZMVDFKD63Y8TT145GQ\", \"01F59Y94YQEZ3PH5STZ8PS1JPG\"]"
427
- ------------------------------------------------------------------------
428
- #### abachman - ulid-ruby
429
- cd ./benchmark/compare_with_othergems/abachman && bundle install --quiet && bundle exec ruby -v ./generate.rb
430
- ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
431
- Warming up --------------------------------------
432
- ULID.generate 3.862k i/100ms
433
- Calculating -------------------------------------
434
- ULID.generate 38.415k (±13.1%) i/s - 189.238k in 5.025788s
435
- "`ulid-ruby gem - 1.0.0` generated products: 260625 - sample: [\"01F59Y9H9V17EPXTYNZDCXB9EZ\", \"01F59Y9J4S4XZ68MF5DJDWHTAC\", \"01F59Y9J8887VC8E850QSBDCDX\", \"01F59Y9JEJPD088EYXVHB86W3N\", \"01F59Y9GGAZFXGCB92EQD695CZ\"]"
436
- ------------------------------------------------------------------------
437
- #### kachick - ruby-ulid(This one)
438
- cd ./benchmark/compare_with_othergems/kachick && bundle install --quiet && bundle exec ruby -v ./generate.rb
439
- ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
440
- Warming up --------------------------------------
441
- ULID.generate.to_s 3.185k i/100ms
442
- Calculating -------------------------------------
443
- ULID.generate.to_s 31.934k (± 9.1%) i/s - 159.250k in 5.030707s
444
- "`ruby-ulid gem (this one) - 0.1.0` generated products: 223867 - sample: [\"01F59Y9SPZHM6JCTYP50CHGVAX\", \"01F59Y9VB7X0SX32MMKF78KJR3\", \"01F59Y9W0C83RYCNYVH84R4JG3\", \"01F59Y9V218Q3D4YP3W74ET3EW\", \"01F59Y9X6DD8NX99WBGCR7RNXF\"]"
445
- ```
446
-
447
- In another execution, Changed as below. So there doesn't seem to be a big difference.
448
-
449
- ```plaintext
450
- #### rafaelsales - ulid
451
- cd ./benchmark/compare_with_othergems/rafaelsales && bundle install --quiet && bundle exec ruby -v ./generate.rb
452
- ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
453
- Warming up --------------------------------------
454
- ULID.generate 2.473k i/100ms
455
- Calculating -------------------------------------
456
- ULID.generate 24.101k (±15.9%) i/s - 118.704k in 5.066190s
457
- "`ulid gem - 1.3.0` generated products: 164763 - sample: [\"01F59YEGPFMXXZWC1YQ49TSK8Y\", \"01F59YEFF7VX5WAW91VTCSE2N9\", \"01F59YEEZ5P9428SDYEDYW8D27\", \"01F59YEHVK56DZBJSNSQK6V1W6\", \"01F59YEHE07M98PVV97ABBAKHM\"]"
458
- ------------------------------------------------------------------------
459
- #### abachman - ulid-ruby
460
- cd ./benchmark/compare_with_othergems/abachman && bundle install --quiet && bundle exec ruby -v ./generate.rb
461
- ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
462
- Warming up --------------------------------------
463
- ULID.generate 2.620k i/100ms
464
- Calculating -------------------------------------
465
- ULID.generate 27.571k (±14.2%) i/s - 136.240k in 5.056272s
466
- "`ulid-ruby gem - 1.0.0` generated products: 186683 - sample: [\"01F59YEVX6GC9TC0RCZ74RC6Z3\", \"01F59YESJXWYGZ61TXHKRVKS97\", \"01F59YEVQ4QQKBED5T49RTV1MA\", \"01F59YEPJ6MMZY1N63DNW7C4SN\", \"01F59YEQK52K8TKTP1ESC6VC5X\"]"
467
- ------------------------------------------------------------------------
468
- #### kachick - ruby-ulid(This one)
469
- cd ./benchmark/compare_with_othergems/kachick && bundle install --quiet && bundle exec ruby -v ./generate.rb
470
- ruby 3.0.1p64 (2021-04-05 revision 0fb782ee38) [x86_64-darwin20]
471
- Warming up --------------------------------------
472
- ULID.generate.to_s 3.014k i/100ms
473
- Calculating -------------------------------------
474
- ULID.generate.to_s 31.612k (±10.1%) i/s - 156.728k in 5.013432s
475
- "`ruby-ulid gem (this one) - 0.1.0` generated products: 212293 - sample: [\"01F59YF1WP49TT4GQPDN3E9JTJ\", \"01F59YF1MW1ZDQW93NX4J6RG4G\", \"01F59YF0KRX2CZKHDQQSN5HXHW\", \"01F59YEZVNH8YHP4ZHDK2ZRWSR\", \"01F59YF1J0FV3CVV099SHA2Q9A\"]"
476
- ```
436
+ See [Benchmark](https://github.com/kachick/ruby-ulid/wiki/Benchmark).
477
437
 
478
- I have an excuse, This gem does not aim `faster than other`.
479
- So I think the results are acceptable.
480
- </details>
438
+ The results are not something to be proud of.
481
439
 
482
440
  ## References
483
441
 
@@ -488,4 +446,3 @@ So I think the results are acceptable.
488
446
  ## Note
489
447
 
490
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)
491
- - 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/ruby-ulid.rb ADDED
@@ -0,0 +1,6 @@
1
+ # coding: us-ascii
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright (C) 2021 Kenichi Kamiya
5
+
6
+ require_relative 'ulid'
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'
@@ -60,6 +61,7 @@ class ULID
60
61
  # @return [ULID]
61
62
  def self.at(time)
62
63
  raise ArgumentError, 'ULID.at takes only `Time` instance' unless Time === time
64
+
63
65
  from_milliseconds_and_entropy(milliseconds: milliseconds_from_time(time), entropy: reasonable_entropy)
64
66
  end
65
67
 
@@ -91,19 +93,21 @@ class ULID
91
93
  # * Do not take random generator for the arguments
92
94
  # * Raising error instead of truncating elements for the given number
93
95
  def self.sample(*args, period: nil)
94
- int_generator = if period
95
- ulid_range = range(period)
96
- 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?
97
100
 
98
- possibilities = (max - min) + (exclude_end ? 0 : 1)
99
- 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?
100
103
 
101
- -> {
102
- SecureRandom.random_number(possibilities) + min
103
- }
104
- else
105
- RANDOM_INTEGER_GENERATOR
106
- end
104
+ -> {
105
+ SecureRandom.random_number(possibilities) + min
106
+ }
107
+ else
108
+ RANDOM_INTEGER_GENERATOR
109
+ end
110
+ )
107
111
 
108
112
  case args.size
109
113
  when 0
@@ -134,6 +138,7 @@ class ULID
134
138
  string = String.try_convert(string)
135
139
  raise ArgumentError, 'ULID.scan takes only strings' unless string
136
140
  return to_enum(__callee__, string) unless block_given?
141
+
137
142
  string.scan(SCANNING_PATTERN) do |matched|
138
143
  yield parse(matched)
139
144
  end
@@ -156,7 +161,7 @@ class ULID
156
161
  milliseconds = n32encoded_timestamp.to_i(32)
157
162
  entropy = n32encoded_randomness.to_i(32)
158
163
 
159
- new milliseconds: milliseconds, entropy: entropy, integer: integer
164
+ new(milliseconds: milliseconds, entropy: entropy, integer: integer)
160
165
  end
161
166
 
162
167
  # @param [Range<Time>, Range<nil>, Range[ULID]] period
@@ -164,6 +169,7 @@ class ULID
164
169
  # @raise [ArgumentError] if the given period is not a `Range[Time]`, `Range[nil]` or `Range[ULID]`
165
170
  def self.range(period)
166
171
  raise ArgumentError, 'ULID.range takes only `Range[Time]`, `Range[nil]` or `Range[ULID]`' unless Range === period
172
+
167
173
  begin_element, end_element, exclude_end = period.begin, period.end, period.exclude_end?
168
174
  return period if self === begin_element && self === end_element
169
175
 
@@ -180,11 +186,7 @@ class ULID
180
186
 
181
187
  case end_element
182
188
  when Time
183
- if exclude_end
184
- end_ulid = min(end_element)
185
- else
186
- end_ulid = max(end_element)
187
- end
189
+ end_ulid = exclude_end ? min(end_element) : max(end_element)
188
190
  when nil
189
191
  # The end should be max and include end, because nil end means to cover endless ULIDs until the limit
190
192
  end_ulid = MAX
@@ -260,9 +262,29 @@ 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
 
@@ -290,8 +312,19 @@ class ULID
290
312
  private_class_method def self.safe_get_class_name(object)
291
313
  fallback = 'UnknownObject'
292
314
 
315
+ # This class getter implementation used https://github.com/rspec/rspec-support/blob/4ad8392d0787a66f9c351d9cf6c7618e18b3d0f2/lib/rspec/support.rb#L83-L89 as a reference, thank you!
316
+ # ref: https://twitter.com/_kachick/status/1400064896759304196
317
+ klass = (
318
+ begin
319
+ object.class
320
+ rescue NoMethodError
321
+ singleton_class = class << object; self; end
322
+ singleton_class.ancestors.detect { |ancestor| !ancestor.equal?(singleton_class) }
323
+ end
324
+ )
325
+
293
326
  begin
294
- name = String.try_convert(object.class.name)
327
+ name = String.try_convert(klass.name)
295
328
  rescue Exception
296
329
  fallback
297
330
  else
@@ -315,7 +348,7 @@ class ULID
315
348
  n32encoded_randomness = entropy.to_s(32).rjust(RANDOMNESS_ENCODED_LENGTH, '0')
316
349
  integer = (n32encoded_timestamp + n32encoded_randomness).to_i(32)
317
350
 
318
- new milliseconds: milliseconds, entropy: entropy, integer: integer
351
+ new(milliseconds: milliseconds, entropy: entropy, integer: integer)
319
352
  end
320
353
 
321
354
  attr_reader :milliseconds, :entropy
@@ -386,7 +419,7 @@ class ULID
386
419
  def octets
387
420
  digits = @integer.digits(256)
388
421
  (OCTETS_LENGTH - digits.size).times do
389
- digits.push 0
422
+ digits.push(0)
390
423
  end
391
424
  digits.reverse!
392
425
  end
@@ -457,6 +490,20 @@ class ULID
457
490
  super
458
491
  end
459
492
 
493
+ # @api private
494
+ # @return [Integer]
495
+ def marshal_dump
496
+ @integer
497
+ end
498
+
499
+ # @api private
500
+ # @param [Integer] integer
501
+ # @return [void]
502
+ def marshal_load(integer)
503
+ unmarshaled = ULID.from_integer(integer)
504
+ initialize(integer: unmarshaled.to_i, milliseconds: unmarshaled.milliseconds, entropy: unmarshaled.entropy)
505
+ end
506
+
460
507
  # @return [self]
461
508
  def to_ulid
462
509
  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,16 +1,19 @@
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
8
+ include MonitorMixin
9
+
7
10
  # @return [ULID, nil]
8
11
  attr_reader :prev
9
12
 
10
13
  undef_method :instance_variable_set
11
14
 
12
15
  def initialize
13
- @mutex = Thread::Mutex.new
16
+ super()
14
17
  @prev = nil
15
18
  end
16
19
 
@@ -26,7 +29,7 @@ class ULID
26
29
  # @raise [UnexpectedError] if the generated ULID is an invalid value in monotonicity spec.
27
30
  # Basically will not happen. Just means this feature prefers error rather than invalid value.
28
31
  def generate(moment: ULID.current_milliseconds)
29
- @mutex.synchronize do
32
+ synchronize do
30
33
  unless @prev
31
34
  @prev = ULID.generate(moment: moment)
32
35
  return @prev
@@ -34,19 +37,23 @@ class ULID
34
37
 
35
38
  milliseconds = ULID.milliseconds_from_moment(moment)
36
39
 
37
- ulid = if @prev.milliseconds < milliseconds
38
- ULID.generate(moment: milliseconds)
39
- else
40
- ULID.from_milliseconds_and_entropy(milliseconds: @prev.milliseconds, entropy: @prev.entropy.succ)
41
- end
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
+ )
42
47
 
43
48
  unless ulid > @prev
44
49
  base_message = "monotonicity broken from unexpected reasons # generated: #{ulid.inspect}, prev: #{@prev.inspect}"
45
- additional_information = if Thread.list == [Thread.main]
46
- '# NOTE: looks single thread only exist'
47
- else
48
- '# NOTE: ran on multi threads, so this might from concurrency issue'
49
- end
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
+ )
50
57
 
51
58
  raise UnexpectedError, base_message + additional_information
52
59
  end
data/lib/ulid/uuid.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
  # Extracted features around UUID from some reasons
@@ -8,7 +9,7 @@
8
9
  # * https://github.com/kachick/ruby-ulid/issues/76
9
10
  class ULID
10
11
  # 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
+ 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
13
  private_constant :UUIDV4_PATTERN
13
14
 
14
15
  # @param [String, #to_str] uuid
@@ -18,7 +19,7 @@ class ULID
18
19
  uuid = String.try_convert(uuid)
19
20
  raise ArgumentError, 'ULID.from_uuidv4 takes only strings' unless uuid
20
21
 
21
- prefix_trimmed = uuid.sub(/\Aurn:uuid:/, '')
22
+ prefix_trimmed = uuid.delete_prefix('urn:uuid:')
22
23
  unless UUIDV4_PATTERN.match?(prefix_trimmed)
23
24
  raise ParserError, "given `#{uuid}` does not match to `#{UUIDV4_PATTERN.inspect}`"
24
25
  end
data/lib/ulid/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  class ULID
5
- VERSION = '0.1.1'
5
+ VERSION = '0.1.6'
6
6
  end
data/sig/ulid.rbs CHANGED
@@ -1,5 +1,4 @@
1
- # Classes
2
- class ULID
1
+ class ULID < Object
3
2
  VERSION: String
4
3
  CROCKFORD_BASE32_ENCODING_STRING: String
5
4
  TIMESTAMP_ENCODED_LENGTH: 10
@@ -44,22 +43,57 @@ class ULID
44
43
  CROCKFORD_BASE32_CHAR_PATTERN: Regexp
45
44
  CROCKFORD_BASE32_CHAR_BY_N32_CHAR: Hash[String, String]
46
45
  N32_CHAR_PATTERN: Regexp
46
+ STANDARD_BY_VARIANT: Hash[String, String]
47
+ VARIANT_PATTERN: Regexp
47
48
 
49
+ # A private API. Should not be used in your code.
48
50
  def self.encode: (Integer integer) -> String
51
+
52
+ # A private API. Should not be used in your code.
49
53
  def self.decode: (String string) -> Integer
54
+
55
+ # A private API. Should not be used in your code.
56
+ def self.normalize: (String string) -> String
50
57
  end
51
58
 
52
59
  class MonotonicGenerator
53
- @mutex: Thread::Mutex
60
+ include MonitorMixin
61
+
62
+ # Returned value is `basically not` Thread-safety
63
+ # If you want to keep Thread-safety, keep to call {#generate} only in same {#synchronize} block
64
+ #
65
+ # ```ruby
66
+ # generator.synchronize do
67
+ # generator.prev
68
+ # generator.inspect
69
+ # generator.generate
70
+ # end
71
+ # ```
54
72
  attr_reader prev: ULID | nil
73
+
74
+ # A private API. Should not be used in your code.
55
75
  def initialize: -> void
76
+
77
+ # See [How to keep `Sortable` even if in same timestamp](https://github.com/kachick/ruby-ulid#how-to-keep-sortable-even-if-in-same-timestamp)
78
+ # The `Thread-safety` is implemented with [Monitor](https://bugs.ruby-lang.org/issues/16255)
56
79
  def generate: (?moment: moment) -> ULID
80
+
81
+ # Returned value is `basically not` Thread-safety
82
+ # If you want to keep Thread-safety, keep to call {#generate} only in same {#synchronize} block
83
+ #
84
+ # ```ruby
85
+ # generator.synchronize do
86
+ # generator.prev
87
+ # generator.inspect
88
+ # generator.generate
89
+ # end
90
+ # ```
57
91
  def inspect: -> String
58
92
  alias to_s inspect
59
93
  def freeze: -> void
60
94
  end
61
95
 
62
- interface _ULID
96
+ interface _ToULID
63
97
  def to_ulid: () -> ULID
64
98
  end
65
99
 
@@ -75,57 +109,437 @@ class ULID
75
109
  @inspect: String?
76
110
  @time: Time?
77
111
 
78
- def self.generate: (?moment: moment, ?entropy: Integer) -> self
79
- def self.at: (Time time) -> self
112
+ # Retuns a ULID
113
+ #
114
+ # They are sortable when generated in different timestamp with milliseconds precision
115
+ #
116
+ # ```ruby
117
+ # ulids = 1000.times.map do
118
+ # sleep(0.001)
119
+ # ULID.generate
120
+ # end
121
+ # ulids.uniq(&:to_time).size #=> 1000
122
+ # ulids.sort == ulids #=> true
123
+ # ```
124
+ #
125
+ # `ULID.generate` can take fixed `Time` instance.
126
+ # See also the short hand [ULID.at](https://kachick.github.io/ruby-ulid/ULID.html#at-class_method)
127
+ #
128
+ # ```ruby
129
+ # time = Time.at(946684800).utc #=> 2000-01-01 00:00:00 UTC
130
+ # ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB00N018DCPJA4H9379P)
131
+ # ULID.generate(moment: time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB006WQT3JTMN0T14EBP)
132
+ # ```
133
+ #
134
+ # The basic generator prefers `randomness`, it does not guarantee `sortable` for same milliseconds ULIDs.
135
+ #
136
+ # ```ruby
137
+ # ulids = 10000.times.map do
138
+ # ULID.generate
139
+ # end
140
+ # ulids.uniq(&:to_time).size #=> 35 (the size is not fixed, might be changed in environment)
141
+ # ulids.sort == ulids #=> false
142
+ # ```
143
+ #
144
+ # If you want to keep sortable even if in same timestamp, See also [ULID::MonotonicGenerator](https://github.com/kachick/ruby-ulid#how-to-keep-sortable-even-if-in-same-timestamp)
145
+ #
146
+ def self.generate: (?moment: moment, ?entropy: Integer) -> ULID
147
+
148
+ # Shorthand of `ULID.generate(moment: Time)`
149
+ # See also [ULID.generate](https://kachick.github.io/ruby-ulid/ULID.html#generate-class_method)
150
+ #
151
+ # ```ruby
152
+ # time = Time.at(946684800).utc #=> 2000-01-01 00:00:00 UTC
153
+ # ULID.at(time) #=> ULID(2000-01-01 00:00:00.000 UTC: 00VHNCZB002W5BGWWKN76N22H6)
154
+ #
155
+ # ulids = 1000.times.map do |n|
156
+ # ULID.at(time + n)
157
+ # end
158
+ # ulids.sort == ulids #=> true
159
+ # ```
160
+ def self.at: (Time time) -> ULID
161
+
162
+ # A private API. Should not be used in your code.
80
163
  def self.current_milliseconds: -> Integer
164
+
165
+ # A private API. Should not be used in your code.
81
166
  def self.milliseconds_from_moment: (moment moment) -> Integer
167
+
168
+ # `ULID` can be element of the `Range`. If you generated the IDs in monotonic generator, ID based filtering is easy and reliable
169
+ #
170
+ # ```ruby
171
+ # include_end = ulid1..ulid2
172
+ # exclude_end = ulid1...ulid2
173
+ #
174
+ # ulids.grep(one_of_the_above)
175
+ # ulids.grep_v(one_of_the_above)
176
+ # ```
177
+ #
178
+ # When want to filter ULIDs with `Time`, we should consider to handle the precision.
179
+ # So this gem provides `ULID.range` to generate reasonable `Range[ULID]` from `Range[Time]`
180
+ #
181
+ # ```ruby
182
+ # # Both of below, The begin of `Range[ULID]` will be the minimum in the floored milliseconds of the time1
183
+ # include_end = ULID.range(time1..time2) #=> The end of `Range[ULID]` will be the maximum in the floored milliseconds of the time2
184
+ # exclude_end = ULID.range(time1...time2) #=> The end of `Range[ULID]` will be the minimum in the floored milliseconds of the time2
185
+ #
186
+ # # Below patterns are acceptable
187
+ # pinpointing = ULID.range(time1..time1) #=> This will match only for all IDs in `time1`
188
+ # 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)
189
+ # until_the_end = ULID.range(ULID.min.to_time..time1) #=> This is same as above for Ruby 2.6
190
+ # until_the_ulid_limit = ULID.range(time1..) # This will match only for all IDs from `time1` to max value of the ULID limit
191
+ #
192
+ # # So you can use the generated range objects as below
193
+ # ulids.grep(one_of_the_above)
194
+ # ulids.grep_v(one_of_the_above)
195
+ # #=> I hope the results should be actually you want!
196
+ # ```
197
+ #
82
198
  def self.range: (period period) -> Range[ULID]
199
+
200
+ # Returns new `Time` with truncating excess precisions in ULID spec.
201
+ #
202
+ # ```ruby
203
+ # time = Time.at(946684800, Rational('123456.789')).utc
204
+ # #=> 2000-01-01 00:00:00.123456789 UTC
205
+ # ULID.floor(time) #=> 2000-01-01 00:00:00.123 UTC
206
+ # ```
83
207
  def self.floor: (Time time) -> Time
84
- def self.parse: (String string) -> self
208
+
209
+ # Get ULID instance from encoded String.
210
+ #
211
+ # ```ruby
212
+ # ulid = ULID.parse('01ARZ3NDEKTSV4RRFFQ69G5FAV')
213
+ # #=> ULID(2016-07-30 23:54:10.259 UTC: 01ARZ3NDEKTSV4RRFFQ69G5FAV)
214
+ # ```
215
+ def self.parse: (_ToStr string) -> ULID
216
+
217
+ # ```ruby
218
+ # # Currently experimental feature, so needed to load the extension.
219
+ # require 'ulid/uuid'
220
+ #
221
+ # # Basically reversible
222
+ # ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
223
+ # #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
224
+ # ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
225
+ # ```
226
+ #
227
+ # See also [Why this is experimental?](https://github.com/kachick/ruby-ulid/issues/76)
85
228
  def self.from_uuidv4: (String uuid) -> ULID
86
- def self.from_integer: (Integer integer) -> self
229
+ def self.from_integer: (Integer integer) -> ULID
230
+
231
+ # Returns termination values for ULID spec.
232
+ #
233
+ # ```ruby
234
+ # ULID.min
235
+ # #=> ULID(1970-01-01 00:00:00.000 UTC: 00000000000000000000000000)
236
+ # ```
237
+ #
238
+ # It can take `Time` instance as an optional argument.
239
+ # Then returns ULID that has minimum value of randomness part in the timestamp.
240
+ #
241
+ # ```ruby
242
+ # time = Time.at(946684800, Rational('123456.789')).utc
243
+ # #=> 2000-01-01 00:00:00.123456789 UTC
244
+ # ULID.min(time)
245
+ # #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3V0000000000000000)
246
+ # ```
247
+ #
248
+ # See also [ULID.max](https://kachick.github.io/ruby-ulid/ULID.html#max-class_method)
87
249
  def self.min: (?moment moment) -> ULID
250
+
251
+ # Returns termination values for ULID spec.
252
+ #
253
+ # ```ruby
254
+ # ULID.max
255
+ # #=> ULID(10889-08-02 05:31:50.655 UTC: 7ZZZZZZZZZZZZZZZZZZZZZZZZZ)
256
+ # ```
257
+ #
258
+ # It can take `Time` instance as an optional argument.
259
+ # Then returns ULID that has maximum value of randomness part in the timestamp.
260
+ #
261
+ # ```ruby
262
+ # time = Time.at(946684800, Rational('123456.789')).utc
263
+ # #=> 2000-01-01 00:00:00.123456789 UTC
264
+ # ULID.max(time)
265
+ # #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3VZZZZZZZZZZZZZZZZ)
266
+ # ```
267
+ #
268
+ # See also [ULID.min](https://kachick.github.io/ruby-ulid/ULID.html#min-class_method)
88
269
  def self.max: (?moment moment) -> ULID
89
- def self.sample: (?period: period) -> self
90
- | (Integer number, ?period: period) -> Array[self]
91
- def self.valid?: (untyped string) -> bool
92
- def self.scan: (String string) -> Enumerator[self, singleton(ULID)]
93
- | (String string) { (self ulid) -> void } -> singleton(ULID)
94
- def self.from_milliseconds_and_entropy: (milliseconds: Integer, entropy: Integer) -> self
95
- def self.try_convert: (_ULID) -> self
270
+
271
+ # Returns random ULIDs.
272
+ #
273
+ # Basically ignores generating time.
274
+ #
275
+ # ```ruby
276
+ # ULID.sample #=> ULID(2545-07-26 06:51:20.085 UTC: 0GGKQ45GMNMZR6N8A8GFG0ZXST)
277
+ # ULID.sample #=> ULID(5098-07-26 21:31:06.946 UTC: 2SSBNGGYA272J7BMDCG4Z6EEM5)
278
+ # ULID.sample(0) #=> []
279
+ # ULID.sample(1) #=> [ULID(2241-04-16 03:31:18.440 UTC: 07S52YWZ98AZ8T565MD9VRYMQH)]
280
+ # ULID.sample(5)
281
+ # #=>
282
+ # #[ULID(5701-04-29 12:41:19.647 UTC: 3B2YH2DV0ZYDDATGTYSKMM1CMT),
283
+ # # ULID(2816-08-01 01:21:46.612 UTC: 0R9GT6RZKMK3RG02Q2HAFVKEY2),
284
+ # # ULID(10408-10-05 17:06:27.848 UTC: 7J6CPTEEC86Y24EQ4F1Y93YYN0),
285
+ # # ULID(2741-09-02 16:24:18.803 UTC: 0P4Q4V34KKAJW46QW47WQB5463),
286
+ # # ULID(2665-03-16 14:50:22.724 UTC: 0KYFW9DWM4CEGFNTAC6YFAVVJ6)]
287
+ # ```
288
+ #
289
+ # You can specify a range object for the timestamp restriction, See also [ULID.range](https://kachick.github.io/ruby-ulid/ULID.html#range-class_method).
290
+ #
291
+ # ```ruby
292
+ # ulid1 = ULID.parse('01F4A5Y1YAQCYAYCTC7GRMJ9AA') #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
293
+ # ulid2 = ULID.parse('01F4PTVCSN9ZPFKYTY2DDJVRK4') #=> ULID(2021-05-02 15:23:48.917 UTC: 01F4PTVCSN9ZPFKYTY2DDJVRK4)
294
+ # ulids = ULID.sample(1000, period: ulid1..ulid2)
295
+ # ulids.uniq.size #=> 1000
296
+ # ulids.take(10)
297
+ # #=>
298
+ # #[ULID(2021-05-02 06:57:19.954 UTC: 01F4NXW02JNB8H0J0TK48JD39X),
299
+ # # ULID(2021-05-02 07:06:07.458 UTC: 01F4NYC372GVP7NS0YAYQGT4VZ),
300
+ # # ULID(2021-05-01 06:16:35.791 UTC: 01F4K94P6F6P68K0H64WRDSFKW),
301
+ # # ULID(2021-04-27 22:17:37.844 UTC: 01F4APHGSMFJZQTGXKZBFFBPJP),
302
+ # # ULID(2021-04-28 20:17:55.357 UTC: 01F4D231MXQJXAR8G2JZHEJNH3),
303
+ # # ULID(2021-04-30 07:18:54.307 UTC: 01F4GTA2332AS2VPHC4FMKC7R5),
304
+ # # ULID(2021-05-02 12:26:03.480 UTC: 01F4PGNXARG554Y3HYVBDW4T9S),
305
+ # # ULID(2021-04-29 09:52:15.107 UTC: 01F4EGP483ZX2747FQPWQNPPMW),
306
+ # # ULID(2021-04-29 03:18:24.152 UTC: 01F4DT4Z4RA0QV8WFQGRAG63EH),
307
+ # # ULID(2021-05-02 13:27:16.394 UTC: 01F4PM605ABF5SDVMEHBH8JJ9R)]
308
+ # ULID.sample(10, period: ulid1.to_time..ulid2.to_time)
309
+ # #=>
310
+ # # [ULID(2021-04-29 06:44:41.513 UTC: 01F4E5YPD9XQ3MYXWK8ZJKY8SW),
311
+ # # ULID(2021-05-01 00:35:06.629 UTC: 01F4JNKD85SVK1EAEYSJGF53A2),
312
+ # # ULID(2021-05-02 12:45:28.408 UTC: 01F4PHSEYRG9BWBEWMRW1XE6WW),
313
+ # # ULID(2021-05-01 03:06:09.130 UTC: 01F4JY7ZBABCBMX16XH2Q4JW4W),
314
+ # # ULID(2021-04-29 21:38:58.109 UTC: 01F4FS45DX4049JEQK4W6TER6G),
315
+ # # ULID(2021-04-29 17:14:14.116 UTC: 01F4F9ZDQ449BE8BBZFEHYQWG2),
316
+ # # ULID(2021-04-30 16:18:08.205 UTC: 01F4HS5DPD1HWDVJNJ6YKJXKSK),
317
+ # # ULID(2021-04-30 10:31:33.602 UTC: 01F4H5ATF2A1CSQF0XV5NKZ288),
318
+ # # ULID(2021-04-28 16:49:06.484 UTC: 01F4CP4PDM214Q6H3KJP7DYJRR),
319
+ # # ULID(2021-04-28 15:05:06.808 UTC: 01F4CG68ZRST94T056KRZ5K9S4)]
320
+ # ```
321
+ def self.sample: (?period: period) -> ULID
322
+ | (Integer number, ?period: period) -> Array[ULID]
323
+ def self.valid?: (untyped) -> bool
324
+
325
+ # Returns normalized string
326
+ #
327
+ # ```ruby
328
+ # ULID.normalize('-olarz3-noekisv4rrff-q6ig5fav--') #=> "01ARZ3N0EK1SV4RRFFQ61G5FAV"
329
+ # ULID.normalized?('-olarz3-noekisv4rrff-q6ig5fav--') #=> false
330
+ # ULID.normalized?('01ARZ3N0EK1SV4RRFFQ61G5FAV') #=> true
331
+ # ```
332
+ #
333
+ # See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
334
+ def self.normalize: (_ToStr string) -> String
335
+
336
+ # Returns `true` if it is normalized string
337
+ #
338
+ # ```ruby
339
+ # ULID.normalize('-olarz3-noekisv4rrff-q6ig5fav--') #=> "01ARZ3N0EK1SV4RRFFQ61G5FAV"
340
+ # ULID.normalized?('-olarz3-noekisv4rrff-q6ig5fav--') #=> false
341
+ # ULID.normalized?('01ARZ3N0EK1SV4RRFFQ61G5FAV') #=> true
342
+ # ```
343
+ #
344
+ # See also [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ulid/spec#3](https://github.com/ulid/spec/issues/3)
345
+ def self.normalized?: (untyped) -> bool
346
+
347
+ # Returns parsed ULIDs from given String for rough operations.
348
+ #
349
+ # ```ruby
350
+ # json =<<'EOD'
351
+ # {
352
+ # "id": "01F4GNAV5ZR6FJQ5SFQC7WDSY3",
353
+ # "author": {
354
+ # "id": "01F4GNBXW1AM2KWW52PVT3ZY9X",
355
+ # "name": "kachick"
356
+ # },
357
+ # "title": "My awesome blog post",
358
+ # "comments": [
359
+ # {
360
+ # "id": "01F4GNCNC3CH0BCRZBPPDEKBKS",
361
+ # "commenter": {
362
+ # "id": "01F4GNBXW1AM2KWW52PVT3ZY9X",
363
+ # "name": "kachick"
364
+ # }
365
+ # },
366
+ # {
367
+ # "id": "01F4GNCXAMXQ1SGBH5XCR6ZH0M",
368
+ # "commenter": {
369
+ # "id": "01F4GND4RYYSKNAADHQ9BNXAWJ",
370
+ # "name": "pankona"
371
+ # }
372
+ # }
373
+ # ]
374
+ # }
375
+ # EOD
376
+ #
377
+ # ULID.scan(json).to_a
378
+ # #=>
379
+ # # [ULID(2021-04-30 05:51:57.119 UTC: 01F4GNAV5ZR6FJQ5SFQC7WDSY3),
380
+ # # ULID(2021-04-30 05:52:32.641 UTC: 01F4GNBXW1AM2KWW52PVT3ZY9X),
381
+ # # ULID(2021-04-30 05:52:56.707 UTC: 01F4GNCNC3CH0BCRZBPPDEKBKS),
382
+ # # ULID(2021-04-30 05:52:32.641 UTC: 01F4GNBXW1AM2KWW52PVT3ZY9X),
383
+ # # ULID(2021-04-30 05:53:04.852 UTC: 01F4GNCXAMXQ1SGBH5XCR6ZH0M),
384
+ # # ULID(2021-04-30 05:53:12.478 UTC: 01F4GND4RYYSKNAADHQ9BNXAWJ)]
385
+ # ```
386
+ def self.scan: (_ToStr string) -> Enumerator[self, singleton(ULID)]
387
+ | (_ToStr string) { (ULID ulid) -> void } -> singleton(ULID)
388
+ def self.from_milliseconds_and_entropy: (milliseconds: Integer, entropy: Integer) -> ULID
389
+ def self.try_convert: (_ToULID) -> ULID
96
390
  | (untyped) -> nil
391
+
392
+ # ```ruby
393
+ # ulid = ULID.generate
394
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
395
+ # ulid.milliseconds #=> 1619544442826
396
+ # ```
97
397
  attr_reader milliseconds: Integer
98
398
  attr_reader entropy: Integer
99
- def initialize: (milliseconds: Integer, entropy: Integer, integer: Integer) -> void
399
+
400
+ # ```ruby
401
+ # ulid = ULID.generate
402
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
403
+ # ulid.to_s #=> "01F4A5Y1YAQCYAYCTC7GRMJ9AA"
404
+ # ```
100
405
  def to_s: -> String
406
+
407
+ # ```ruby
408
+ # ulid = ULID.generate
409
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
410
+ # ulid.to_i #=> 1957909092946624190749577070267409738
411
+ # ```
101
412
  def to_i: -> Integer
102
413
  alias hash to_i
414
+
415
+ # Basically same as String based sort.
416
+ #
417
+ # ```ruby
418
+ # ulids = ULID.sample(10000); nil
419
+ # ulids.map(&:to_s).sort == ulids.sort.map(&:to_s)
420
+ # #=> true
421
+ # ```
422
+ #
423
+ # To be precise, this sorting unaffected with `case sensitive or not` and might handle [ulid/spec#57](https://github.com/ulid/spec/pull/57) in future. So preferable than `lexicographically sortable` in actual case.
424
+ #
103
425
  def <=>: (ULID other) -> Integer
104
426
  | (untyped other) -> nil
427
+
428
+ # ```ruby
429
+ # ulid = ULID.generate
430
+ # ulid.inspect #=> "ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)"
431
+ # ```
105
432
  def inspect: -> String
433
+
434
+ # ```ruby
435
+ # ULID.parse('4NNB20D9C1ME2NGMTX51ERZJX0') == ULID.parse('4nnb20d9c1me2ngmtx51erzjx0')
436
+ # #=> true
437
+ # ```
106
438
  def eql?: (untyped other) -> bool
107
439
  alias == eql?
108
440
  def ===: (untyped other) -> bool
441
+
442
+ # ```ruby
443
+ # ulid = ULID.generate
444
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
445
+ # ulid.to_time #=> 2021-04-27 17:27:22.826 UTC
446
+ # ```
109
447
  def to_time: -> Time
448
+
449
+ # ```ruby
450
+ # ulid = ULID.generate
451
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
452
+ # ulid.timestamp #=> "01F4A5Y1YA"
453
+ # ```
110
454
  def timestamp: -> String
455
+
456
+ # ```ruby
457
+ # ulid = ULID.generate
458
+ # #=> ULID(2021-04-27 17:27:22.826 UTC: 01F4A5Y1YAQCYAYCTC7GRMJ9AA)
459
+ # ulid.randomness #=> "QCYAYCTC7GRMJ9AA"
460
+ # ```
111
461
  def randomness: -> String
462
+
463
+ # Intentionally avoiding to use `Record type` ref: https://github.com/ruby/rbs/blob/4fb4c33b2325d1a73d79ff7aaeb49f21cec1e0e5/docs/syntax.md#record-type
464
+ # Because the returning values are not fixed.
112
465
  def patterns: -> Hash[Symbol, Regexp | String]
113
466
  def octets: -> octets
114
467
  def timestamp_octets: -> timestamp_octets
115
468
  def randomness_octets: -> randomness_octets
469
+
470
+ # ```ruby
471
+ # # Currently experimental feature, so needed to load the extension.
472
+ # require 'ulid/uuid'
473
+ #
474
+ # # Basically reversible
475
+ # ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
476
+ # #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
477
+ # ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
478
+ # ```
479
+ #
480
+ # See also [Why this is experimental?](https://github.com/kachick/ruby-ulid/issues/76)
116
481
  def to_uuidv4: -> String
117
- def next: -> ULID?
118
- alias succ next
482
+
483
+ # Returns next(successor) ULID.
484
+ # Especially `ULID#succ` makes it possible `Range[ULID]#each`.
485
+ #
486
+ # NOTE: But basically `Range[ULID]#each` should not be used, incrementing 128 bits IDs are not reasonable operation in most case
487
+ #
488
+ # ```ruby
489
+ # ULID.parse('01BX5ZZKBKZZZZZZZZZZZZZZZY').next.to_s #=> "01BX5ZZKBKZZZZZZZZZZZZZZZZ"
490
+ # ULID.parse('01BX5ZZKBKZZZZZZZZZZZZZZZZ').next.to_s #=> "01BX5ZZKBM0000000000000000"
491
+ # ULID.parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').next #=> nil
492
+ # ```
493
+ #
494
+ # See also [ULID#pred](https://kachick.github.io/ruby-ulid/ULID.html#pred-instance_method)
495
+ def succ: -> ULID?
496
+ alias next succ
497
+
498
+ # Returns predecessor ULID.
499
+ #
500
+ # ```ruby
501
+ # ULID.parse('01BX5ZZKBK0000000000000001').pred.to_s #=> "01BX5ZZKBK0000000000000000"
502
+ # ULID.parse('01BX5ZZKBK0000000000000000').pred.to_s #=> "01BX5ZZKBJZZZZZZZZZZZZZZZZ"
503
+ # ULID.parse('00000000000000000000000000').pred #=> nil
504
+ # ```
505
+ #
506
+ # See also [ULID#succ](https://kachick.github.io/ruby-ulid/ULID.html#succ-instance_method)
119
507
  def pred: -> ULID?
120
508
  def freeze: -> self
509
+
510
+ # A private API. Should not be used in your code.
511
+ def marshal_dump: -> Integer
512
+
513
+ # A private API. Should not be used in your code.
514
+ def marshal_load: (Integer integer) -> void
515
+
516
+ # Returns `self`
121
517
  def to_ulid: -> self
518
+
519
+ # Returns `self`. Not a new instance.
122
520
  def dup: -> self
123
- # Same as https://github.com/ruby/rbs/blob/4fb4c33b2325d1a73d79ff7aaeb49f21cec1e0e5/core/object.rbs#L79
521
+
522
+ # Same API as [Object#clone](https://github.com/ruby/rbs/blob/4fb4c33b2325d1a73d79ff7aaeb49f21cec1e0e5/core/object.rbs#L79)
523
+ # But actually returns `self`. Not a new instance.
124
524
  def clone: (?freeze: bool) -> self
125
525
 
126
526
  private
527
+
528
+ # A private API. Should not be used in your code.
529
+ def self.new: (milliseconds: Integer, entropy: Integer, integer: Integer) -> self
530
+
531
+ # A private API. Should not be used in your code.
127
532
  def self.reasonable_entropy: -> Integer
533
+
534
+ # A private API. Should not be used in your code.
128
535
  def self.milliseconds_from_time: (Time time) -> Integer
536
+
537
+ # A private API. Should not be used in your code.
129
538
  def self.safe_get_class_name: (untyped object) -> String
539
+
540
+ # A private API. Should not be used in your code.
541
+ def initialize: (milliseconds: Integer, entropy: Integer, integer: Integer) -> void
542
+
543
+ # A private API. Should not be used in your code.
130
544
  def cache_all_instance_variables: -> void
131
545
  end
metadata CHANGED
@@ -1,69 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-ulid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.6
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-15 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: rbs
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: 1.2.0
20
- type: :development
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: 1.2.0
27
- - !ruby/object:Gem::Dependency
28
- name: benchmark-ips
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: 2.8.4
34
- - - "<"
35
- - !ruby/object:Gem::Version
36
- version: '3'
37
- type: :development
38
- prerelease: false
39
- version_requirements: !ruby/object:Gem::Requirement
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- version: 2.8.4
44
- - - "<"
45
- - !ruby/object:Gem::Version
46
- version: '3'
47
- - !ruby/object:Gem::Dependency
48
- name: yard
49
- requirement: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - ">="
52
- - !ruby/object:Gem::Version
53
- version: 0.9.26
54
- - - "<"
55
- - !ruby/object:Gem::Version
56
- version: '2'
57
- type: :development
58
- prerelease: false
59
- version_requirements: !ruby/object:Gem::Requirement
60
- requirements:
61
- - - ">="
62
- - !ruby/object:Gem::Version
63
- version: 0.9.26
64
- - - "<"
65
- - !ruby/object:Gem::Version
66
- version: '2'
11
+ date: 2021-07-05 00:00:00.000000000 Z
12
+ dependencies: []
67
13
  description: |2
68
14
  The ULID(Universally Unique Lexicographically Sortable Identifier) has useful specs for applications (e.g. `Database key`), especially possess all `uniqueness`, `randomness`, `extractable timestamps` and `sortable` features.
69
15
  This gem aims to provide the generator, monotonic generator, parser and handy manipulation features around the ULID.
@@ -74,8 +20,9 @@ executables: []
74
20
  extensions: []
75
21
  extra_rdoc_files: []
76
22
  files:
77
- - LICENSE
23
+ - LICENSE.txt
78
24
  - README.md
25
+ - lib/ruby-ulid.rb
79
26
  - lib/ulid.rb
80
27
  - lib/ulid/crockford_base32.rb
81
28
  - lib/ulid/monotonic_generator.rb
@@ -89,6 +36,7 @@ metadata:
89
36
  documentation_uri: https://kachick.github.io/ruby-ulid/
90
37
  homepage_uri: https://github.com/kachick/ruby-ulid
91
38
  source_code_uri: https://github.com/kachick/ruby-ulid
39
+ bug_tracker_uri: https://github.com/kachick/ruby-ulid/issues
92
40
  post_install_message:
93
41
  rdoc_options: []
94
42
  require_paths:
@@ -107,5 +55,5 @@ requirements: []
107
55
  rubygems_version: 3.2.15
108
56
  signing_key:
109
57
  specification_version: 4
110
- summary: A handy ULID library
58
+ summary: ULID manipulation library
111
59
  test_files: []