ruby-ulid 0.1.1 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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: []