ruby-ulid 0.8.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +53 -25
- data/lib/ulid/monotonic_generator.rb +1 -1
- data/lib/ulid/utils.rb +8 -12
- data/lib/ulid/uuid/fields.rb +41 -0
- data/lib/ulid/uuid.rb +9 -25
- data/lib/ulid/version.rb +1 -1
- data/lib/ulid.rb +55 -25
- data/sig/ulid.rbs +43 -25
- metadata +5 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3b0c26b06cd815f96d9ffc78836ceb69ed4677a4f5acfac6e15c016e093e0d18
|
|
4
|
+
data.tar.gz: '09acd9841d6ee33b87687dd12a7912ef262c9cad3d85b37db6215912048b36e1'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bd7f5fe5b75a1d8f91d5a016a81a6a132debaf4247f075918ad4a52dfc96a6bef937ef433663d71610a008621eac3e5c997f35f15249c8030bbc095357882a78
|
|
7
|
+
data.tar.gz: d444cfac8c9b66ce72850236a7cd7367873a3ea8c51a6c67bc5d34bb0a549fb4192c120cbaa13ba92b4e4c49ccc45fe2cf7a3a6a01882daee287428ca29dd597
|
data/README.md
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
[](https://github.com/kachick/ruby-ulid/actions/workflows/ci.yml?query=branch%3Amain)
|
|
4
4
|
[](http://badge.fury.io/rb/ruby-ulid)
|
|
5
5
|
|
|
6
|
+
This gem is in maintenance mode, I have no plan to add new features.\
|
|
7
|
+
The reason is UUID v7 has been accepted in [IETF](https://www.rfc-editor.org/rfc/rfc9562.html) and [ruby's securerandom](https://github.com/ruby/securerandom/pull/19). See [UUID section](#uuid) for detail.
|
|
8
|
+
|
|
6
9
|
## Overview
|
|
7
10
|
|
|
8
11
|
[ulid/spec](https://github.com/ulid/spec) defines some useful features.\
|
|
@@ -38,12 +41,12 @@ Instead, herein is proposed ULID:
|
|
|
38
41
|
|
|
39
42
|
### Install
|
|
40
43
|
|
|
41
|
-
Tested only in the
|
|
44
|
+
Tested only in the Ruby 4.
|
|
42
45
|
|
|
43
46
|
Add this line to your `Gemfile`.
|
|
44
47
|
|
|
45
48
|
```ruby
|
|
46
|
-
gem('ruby-ulid', '~> 0.
|
|
49
|
+
gem('ruby-ulid', '~> 1.0.0')
|
|
47
50
|
```
|
|
48
51
|
|
|
49
52
|
And load it.
|
|
@@ -53,7 +56,18 @@ require 'ulid'
|
|
|
53
56
|
```
|
|
54
57
|
|
|
55
58
|
NOTE: This README contains information about the development version.\
|
|
56
|
-
If you would like to see released version's one. [Look at the ref](https://github.com/kachick/ruby-ulid/tree/
|
|
59
|
+
If you would like to see released version's one. [Look at the ref](https://github.com/kachick/ruby-ulid/tree/v1.0.0).
|
|
60
|
+
|
|
61
|
+
In [Nix](https://nixos.org/), you can skip the installation steps for both ruby and ruby-ulid to try.
|
|
62
|
+
|
|
63
|
+
```console
|
|
64
|
+
> nix run github:kachick/ruby-ulid#ruby -- -e 'p ULID.generate'
|
|
65
|
+
ULID(2024-03-03 18:37:06.152 UTC: 01HR2SNY789ZZ027EDJEHAGQ62)
|
|
66
|
+
|
|
67
|
+
> nix run github:kachick/ruby-ulid#irb
|
|
68
|
+
irb(main):001:0> ULID.parse('01H66XG2A9WWYRCYGPA62T4AZA')
|
|
69
|
+
=> ULID(2023-07-25 16:18:12.937 UTC: 01H66XG2A9WWYRCYGPA62T4AZA)
|
|
70
|
+
```
|
|
57
71
|
|
|
58
72
|
### Generator and Parser
|
|
59
73
|
|
|
@@ -269,7 +283,7 @@ ULID.min(time) #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3V0000000000000000)
|
|
|
269
283
|
ULID.max(time) #=> ULID(2000-01-01 00:00:00.123 UTC: 00VHNCZB3VZZZZZZZZZZZZZZZZ)
|
|
270
284
|
```
|
|
271
285
|
|
|
272
|
-
#### As element in Enumerable
|
|
286
|
+
#### As an element in Enumerable and Range
|
|
273
287
|
|
|
274
288
|
`ULID#next` and `ULID#succ` returns next(successor) ULID.\
|
|
275
289
|
Especially `ULID#succ` makes it possible `Range[ULID]#each`.
|
|
@@ -290,6 +304,17 @@ ULID.parse('01BX5ZZKBK0000000000000000').pred.to_s #=> "01BX5ZZKBJZZZZZZZZZZZZZZ
|
|
|
290
304
|
ULID.parse('00000000000000000000000000').pred #=> nil
|
|
291
305
|
```
|
|
292
306
|
|
|
307
|
+
`ULID#+` is also provided to realize `Range#step` since [ruby-3.4.0 spec changes](https://bugs.ruby-lang.org/issues/18368).
|
|
308
|
+
|
|
309
|
+
```ruby
|
|
310
|
+
# This code works only in ruby-3.4.0dev or later
|
|
311
|
+
(ULID.min...).step(42).take(3)
|
|
312
|
+
# =>
|
|
313
|
+
[ULID(1970-01-01 00:00:00.000 UTC: 00000000000000000000000000),
|
|
314
|
+
ULID(1970-01-01 00:00:00.000 UTC: 0000000000000000000000001A),
|
|
315
|
+
ULID(1970-01-01 00:00:00.000 UTC: 0000000000000000000000002M)]
|
|
316
|
+
```
|
|
317
|
+
|
|
293
318
|
#### Test helpers
|
|
294
319
|
|
|
295
320
|
`ULID.sample` returns random ULIDs.
|
|
@@ -356,38 +381,47 @@ ULID.parse_variant_format('01G70Y0Y7G-ZLXWDIREXERGSDoD') #=> ULID(2022-07-03 02:
|
|
|
356
381
|
|
|
357
382
|
#### UUID
|
|
358
383
|
|
|
359
|
-
Both ULID and UUID are 128-bit IDs. But with different specs. Especially UUID has some versions
|
|
384
|
+
Both ULID and UUID are 128-bit IDs. But with different specs. Especially, UUID has some versions, for example, UUIDv4 and UUIDv7.
|
|
360
385
|
|
|
361
|
-
All
|
|
362
|
-
Most ULIDs cannot be converted to
|
|
386
|
+
All UUIDs can be converted to ULID, but only [new versions](https://datatracker.ietf.org/doc/rfc9562/) have a correct "timestamp".\
|
|
387
|
+
Most ULIDs cannot be converted to UUID while maintaining reversibility, because UUID requires version and variants in the fields.
|
|
363
388
|
|
|
364
389
|
See also [ulid/spec#64](https://github.com/ulid/spec/issues/64) for further detail.
|
|
365
390
|
|
|
366
|
-
For now, this gem provides
|
|
391
|
+
For now, this gem provides some methods for UUIDs.
|
|
367
392
|
|
|
368
393
|
- Reversibility is preferred: `ULID.from_uuidish`, `ULID.to_uuidish`
|
|
369
|
-
- Prefer
|
|
394
|
+
- Prefer variants specification: `ULID.from_uuid_v4`, `ULID.from_uuid_v7`, `ULID.to_uuid_v4`, `ULID.to_uuid_v7`
|
|
370
395
|
|
|
371
396
|
```ruby
|
|
372
|
-
# All UUIDv4 IDs can be reversible even if converted to ULID
|
|
373
|
-
|
|
374
|
-
ULID.from_uuidish(
|
|
375
|
-
ULID.from_uuidish(
|
|
397
|
+
# All UUIDv4 and UUIDv7 IDs can be reversible even if converted to ULID
|
|
398
|
+
uuid_v4 = SecureRandom.uuid_v4
|
|
399
|
+
ULID.from_uuidish(uuid_v4) == ULID.from_uuid_v4(uuid_v4) #=> true
|
|
400
|
+
ULID.from_uuidish(uuid_v4).to_uuidish == ULID.from_uuid_v4(uuid_v4).to_uuid_v4 #=> true
|
|
401
|
+
|
|
402
|
+
# v4 does not have timestamp, v7 has it.
|
|
403
|
+
|
|
404
|
+
ULID.from_uuid_v4(SecureRandom.uuid_v4).to_time
|
|
405
|
+
# 'f80b3f53-043a-4298-a674-cd83a7fd5d22' => 10612-05-19 16:58:53.882 UTC
|
|
376
406
|
|
|
377
|
-
|
|
407
|
+
ULID.from_uuid_v7(SecureRandom.uuid_v7).to_time
|
|
408
|
+
# '01946f9e-bf58-7be3-8fd4-4606606b05aa' => 2025-01-16 14:57:42.232 UTC
|
|
409
|
+
# ULID is officially defined milliseconds precision for the spec. So omit the nanoseconds precisions even if the UUID v7 ID was generated with extra_timestamp_bits >= 1.
|
|
410
|
+
|
|
411
|
+
# However most ULIDs cannot be converted to versioned UUID
|
|
378
412
|
ulid = ULID.parse('01F4A5Y1YAQCYAYCTC7GRMJ9AA')
|
|
379
|
-
ulid.
|
|
413
|
+
ulid.to_uuid_v4 #=> ULID::IrreversibleUUIDError
|
|
380
414
|
# So 2 ways to get substitute strings that might satisfy the use case
|
|
381
|
-
ulid.
|
|
382
|
-
ulid == ULID.
|
|
415
|
+
ulid.to_uuid_v4(force: true) #=> "0179145f-07ca-4b3c-af33-4c3c3149254a" this cannot be reverse to source ULID
|
|
416
|
+
ulid == ULID.from_uuid_v4(ulid.to_uuid_v4(force: true)) #=> false
|
|
383
417
|
ulid.to_uuidish #=> "0179145f-07ca-bb3c-af33-4c3c3149254a" does not satisfy UUIDv4 spec
|
|
384
418
|
ulid == ULID.from_uuidish(ulid.to_uuidish) #=> true
|
|
385
419
|
|
|
386
420
|
# Seeing boundary IDs makes it easier to understand
|
|
387
421
|
ULID.min.to_uuidish #=> "00000000-0000-0000-0000-000000000000"
|
|
388
|
-
ULID.min.
|
|
422
|
+
ULID.min.to_uuid_v4(force: true) #=> "00000000-0000-4000-8000-000000000000"
|
|
389
423
|
ULID.max.to_uuidish #=> "ffffffff-ffff-ffff-ffff-ffffffffffff"
|
|
390
|
-
ULID.max.
|
|
424
|
+
ULID.max.to_uuid_v4(force: true) #=> "ffffffff-ffff-4fff-bfff-ffffffffffff"
|
|
391
425
|
```
|
|
392
426
|
|
|
393
427
|
## Migration from other gems
|
|
@@ -404,9 +438,3 @@ See [wiki page for gem migration](https://github.com/kachick/ruby-ulid/wiki/Gem-
|
|
|
404
438
|
- [Repository](https://github.com/kachick/ruby-ulid)
|
|
405
439
|
- [API documents](https://kachick.github.io/ruby-ulid/)
|
|
406
440
|
- [ulid/spec](https://github.com/ulid/spec)
|
|
407
|
-
|
|
408
|
-
## Note
|
|
409
|
-
|
|
410
|
-
- [UUIDv6, UUIDv7, UUIDv8](https://www.ietf.org/archive/id/draft-ietf-uuidrev-rfc4122bis-02.html) is another choice for sortable and randomness ID.
|
|
411
|
-
\
|
|
412
|
-
However they remain in draft state. Our tracker is: [ruby-ulid#37](https://github.com/kachick/ruby-ulid/issues/37)
|
|
@@ -10,7 +10,7 @@ require_relative('utils')
|
|
|
10
10
|
class ULID
|
|
11
11
|
class MonotonicGenerator
|
|
12
12
|
# @note When use https://github.com/ko1/ractor-tvar might realize Ractor based thread safe monotonic generator.
|
|
13
|
-
# However it is a C
|
|
13
|
+
# However it is a C extension, I'm pending to use it for now.
|
|
14
14
|
include(MonitorMixin)
|
|
15
15
|
|
|
16
16
|
# @return [ULID, nil]
|
data/lib/ulid/utils.rb
CHANGED
|
@@ -7,20 +7,16 @@
|
|
|
7
7
|
require('securerandom')
|
|
8
8
|
|
|
9
9
|
class ULID
|
|
10
|
-
# @note I don't have confidence for the naming of `Utils`. However some standard libraries have same name.
|
|
11
|
-
# https://github.com/ruby/webrick/blob/14612a7540fdd7373344461851c4bfff64985b3e/lib/webrick/utils.rb#L17
|
|
12
|
-
# https://docs.ruby-lang.org/ja/latest/class/ERB=3a=3aUtil.html
|
|
13
|
-
# https://github.com/ruby/rss/blob/af1c3c9c9630ec0a48abec48ed1ef348ba82aa13/lib/rss/utils.rb#L9
|
|
14
10
|
module Utils
|
|
15
11
|
# @return [Integer]
|
|
16
12
|
def self.current_milliseconds
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
(
|
|
13
|
+
# There are different recommendations for this featrure with the accuracy and other context
|
|
14
|
+
# At here, I prefer to adjust with Ruby UUID v7 imeplementation and respect monotonicity use-case
|
|
15
|
+
# https://github.com/ruby/securerandom/pull/19/files#diff-cad52e37612706fe31d85599bb8bc789e90fd382f091ed31fdd036119af3e5cdR252
|
|
16
|
+
# Other resources
|
|
17
|
+
# - https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/
|
|
18
|
+
# - https://github.com/ruby/ruby/blob/5df20ab0b49b55c9cf858879f3e6e30cc3dcd803/process.c#L8131
|
|
19
|
+
Process.clock_gettime(Process::CLOCK_REALTIME, :millisecond)
|
|
24
20
|
end
|
|
25
21
|
|
|
26
22
|
# @param [Time, Integer] moment
|
|
@@ -30,7 +26,7 @@ class ULID
|
|
|
30
26
|
when Integer
|
|
31
27
|
moment
|
|
32
28
|
when Time
|
|
33
|
-
|
|
29
|
+
(moment.to_r * 1000).to_i
|
|
34
30
|
else
|
|
35
31
|
raise(ArgumentError, '`moment` should be a `Time` or `Integer as milliseconds`')
|
|
36
32
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# coding: us-ascii
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
# shareable_constant_value: literal
|
|
4
|
+
|
|
5
|
+
# Copyright (C) 2021 Kenichi Kamiya
|
|
6
|
+
|
|
7
|
+
class ULID
|
|
8
|
+
module UUID
|
|
9
|
+
# @see https://www.rfc-editor.org/rfc/rfc4122#section-4.1.2
|
|
10
|
+
# @note
|
|
11
|
+
# - Using `Fields = Data.define do; end` syntax made https://github.com/kachick/ruby-ulid/issues/233 again. So use class syntax instead
|
|
12
|
+
# - This file is extracted to avoid YARD warnings "Ruby::ClassHandler: Undocumentable superclass" https://github.com/lsegal/yard/issues/737
|
|
13
|
+
# Partially avoiding is hard in YARD, so extracting the code and using exclude filter in yardopts...
|
|
14
|
+
class Fields < Data.define(:time_low, :time_mid, :time_hi_and_version, :clock_seq_hi_and_res, :clk_seq_low, :node)
|
|
15
|
+
def self.raw_from_octets(octets)
|
|
16
|
+
case octets.pack('C*').unpack('NnnnnN')
|
|
17
|
+
in [Integer => time_low, Integer => time_mid, Integer => time_hi_and_version, Integer => clock_seq_hi_and_res, Integer => clk_seq_low, Integer => node]
|
|
18
|
+
new(time_low:, time_mid:, time_hi_and_version:, clock_seq_hi_and_res:, clk_seq_low:, node:).freeze
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.forced_version_from_octets(octets, mask:)
|
|
23
|
+
case octets.pack('C*').unpack('NnnnnN')
|
|
24
|
+
in [Integer => time_low, Integer => time_mid, Integer => time_hi_and_version, Integer => clock_seq_hi_and_res, Integer => clk_seq_low, Integer => node]
|
|
25
|
+
new(
|
|
26
|
+
time_low:,
|
|
27
|
+
time_mid:,
|
|
28
|
+
time_hi_and_version: (time_hi_and_version & 0x0fff) | mask,
|
|
29
|
+
clock_seq_hi_and_res: (clock_seq_hi_and_res & 0x3fff) | 0x8000,
|
|
30
|
+
clk_seq_low:,
|
|
31
|
+
node:
|
|
32
|
+
).freeze
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def to_s
|
|
37
|
+
'%08x-%04x-%04x-%04x-%04x%08x' % deconstruct
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/ulid/uuid.rb
CHANGED
|
@@ -6,12 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
require_relative('errors')
|
|
8
8
|
require_relative('utils')
|
|
9
|
+
require_relative('uuid/fields')
|
|
9
10
|
|
|
10
11
|
class ULID
|
|
11
12
|
module UUID
|
|
12
13
|
BASE_PATTERN = /\A[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}\z/i
|
|
13
14
|
# Imported from https://stackoverflow.com/a/38191104/1212807, thank you!
|
|
14
15
|
V4_PATTERN = /\A[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i
|
|
16
|
+
V7_PATTERN = /\A[0-9A-F]{8}-[0-9A-F]{4}-7[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i
|
|
15
17
|
|
|
16
18
|
def self.parse_any_to_int(uuidish)
|
|
17
19
|
encoded = String.try_convert(uuidish)
|
|
@@ -38,34 +40,16 @@ class ULID
|
|
|
38
40
|
parse_any_to_int(encoded)
|
|
39
41
|
end
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class Fields < Struct.new(:time_low, :time_mid, :time_hi_and_version, :clock_seq_hi_and_res, :clk_seq_low, :node, keyword_init: true)
|
|
45
|
-
def self.raw_from_octets(octets)
|
|
46
|
-
case octets.pack('C*').unpack('NnnnnN')
|
|
47
|
-
in [Integer => time_low, Integer => time_mid, Integer => time_hi_and_version, Integer => clock_seq_hi_and_res, Integer => clk_seq_low, Integer => node]
|
|
48
|
-
new(time_low:, time_mid:, time_hi_and_version:, clock_seq_hi_and_res:, clk_seq_low:, node:).freeze
|
|
49
|
-
end
|
|
50
|
-
end
|
|
43
|
+
def self.parse_v7_to_int(uuid)
|
|
44
|
+
encoded = String.try_convert(uuid)
|
|
45
|
+
raise(ArgumentError, 'should pass a string for UUID parser') unless encoded
|
|
51
46
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
new(
|
|
56
|
-
time_low:,
|
|
57
|
-
time_mid:,
|
|
58
|
-
time_hi_and_version: (time_hi_and_version & 0x0fff) | 0x4000,
|
|
59
|
-
clock_seq_hi_and_res: (clock_seq_hi_and_res & 0x3fff) | 0x8000,
|
|
60
|
-
clk_seq_low:,
|
|
61
|
-
node:
|
|
62
|
-
).freeze
|
|
63
|
-
end
|
|
47
|
+
prefix_trimmed = encoded.delete_prefix('urn:uuid:')
|
|
48
|
+
unless V7_PATTERN.match?(prefix_trimmed)
|
|
49
|
+
raise(ParserError, "given `#{encoded}` does not match to `#{V7_PATTERN.inspect}`")
|
|
64
50
|
end
|
|
65
51
|
|
|
66
|
-
|
|
67
|
-
'%08x-%04x-%04x-%04x-%04x%08x' % values
|
|
68
|
-
end
|
|
52
|
+
parse_any_to_int(encoded)
|
|
69
53
|
end
|
|
70
54
|
end
|
|
71
55
|
|
data/lib/ulid/version.rb
CHANGED
data/lib/ulid.rb
CHANGED
|
@@ -40,7 +40,7 @@ class ULID
|
|
|
40
40
|
SCANNING_PATTERN = /\b[0-7][#{CrockfordBase32::ENCODING_STRING}]{#{TIMESTAMP_ENCODED_LENGTH - 1}}[#{CrockfordBase32::ENCODING_STRING}]{#{RANDOMNESS_ENCODED_LENGTH}}\b/i
|
|
41
41
|
|
|
42
42
|
# Similar as Time#inspect since Ruby 2.7, however it is NOT same.
|
|
43
|
-
# Time#inspect
|
|
43
|
+
# Time#inspect truncates needless digits. Keeping full milliseconds with "%3N" will fit for ULID.
|
|
44
44
|
# @see https://bugs.ruby-lang.org/issues/15958
|
|
45
45
|
# @see https://github.com/ruby/ruby/blob/744d17ff6c33b09334508e8110007ea2a82252f5/time.c#L4026-L4078
|
|
46
46
|
TIME_FORMAT_IN_INSPECT = '%Y-%m-%d %H:%M:%S.%3N %Z'
|
|
@@ -351,10 +351,17 @@ class ULID
|
|
|
351
351
|
# @param [String, #to_str] uuid
|
|
352
352
|
# @return [ULID]
|
|
353
353
|
# @raise [ParserError] if the given format is not correct for UUIDv4 specs
|
|
354
|
-
def self.
|
|
354
|
+
def self.from_uuid_v4(uuid)
|
|
355
355
|
from_integer(UUID.parse_v4_to_int(uuid))
|
|
356
356
|
end
|
|
357
357
|
|
|
358
|
+
# @param [String, #to_str] uuid
|
|
359
|
+
# @return [ULID]
|
|
360
|
+
# @raise [ParserError] if the given format is not correct for UUIDv4 specs
|
|
361
|
+
def self.from_uuid_v7(uuid)
|
|
362
|
+
from_integer(UUID.parse_v7_to_int(uuid))
|
|
363
|
+
end
|
|
364
|
+
|
|
358
365
|
attr_reader(:milliseconds, :entropy, :encoded)
|
|
359
366
|
protected(:encoded)
|
|
360
367
|
|
|
@@ -449,33 +456,43 @@ class ULID
|
|
|
449
456
|
@encoded.slice(TIMESTAMP_ENCODED_LENGTH, RANDOMNESS_ENCODED_LENGTH) || raise(UnexpectedError)
|
|
450
457
|
end
|
|
451
458
|
|
|
452
|
-
# @
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
+
# @param [Integer] other
|
|
460
|
+
# @return [ULID, nil] when returning URID might be greater than `7ZZZZZZZZZZZZZZZZZZZZZZZZZ`, returns `nil` instead of ULID
|
|
461
|
+
def +(other)
|
|
462
|
+
raise(ArgumentError, 'ULID#+ takes only integers') unless Integer === other
|
|
463
|
+
|
|
464
|
+
new_int = @integer + other
|
|
465
|
+
case new_int
|
|
466
|
+
when MAX_INTEGER
|
|
467
|
+
MAX
|
|
468
|
+
when 0
|
|
469
|
+
MIN
|
|
470
|
+
else
|
|
471
|
+
if new_int > MAX_INTEGER || new_int < 0
|
|
459
472
|
nil
|
|
473
|
+
else
|
|
474
|
+
ULID.from_integer(new_int)
|
|
460
475
|
end
|
|
461
|
-
else
|
|
462
|
-
ULID.from_integer(succ_int)
|
|
463
476
|
end
|
|
464
477
|
end
|
|
478
|
+
|
|
479
|
+
# @param [Integer] other
|
|
480
|
+
# @return [ULID, nil] when returning URID might be less than `00000000000000000000000000`, returns `nil` instead of ULID
|
|
481
|
+
def -(other)
|
|
482
|
+
raise(ArgumentError, 'ULID#- takes only integers') unless Integer === other
|
|
483
|
+
|
|
484
|
+
self + -other
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
# @return [ULID, nil] when called on ULID as `7ZZZZZZZZZZZZZZZZZZZZZZZZZ`, returns `nil` instead of ULID
|
|
488
|
+
def succ
|
|
489
|
+
self + 1
|
|
490
|
+
end
|
|
465
491
|
alias_method(:next, :succ)
|
|
466
492
|
|
|
467
493
|
# @return [ULID, nil] when called on ULID as `00000000000000000000000000`, returns `nil` instead of ULID
|
|
468
494
|
def pred
|
|
469
|
-
|
|
470
|
-
if pred_int <= 0
|
|
471
|
-
if pred_int == 0
|
|
472
|
-
MIN
|
|
473
|
-
else
|
|
474
|
-
nil
|
|
475
|
-
end
|
|
476
|
-
else
|
|
477
|
-
ULID.from_integer(pred_int)
|
|
478
|
-
end
|
|
495
|
+
self - 1
|
|
479
496
|
end
|
|
480
497
|
|
|
481
498
|
# @return [Integer]
|
|
@@ -500,8 +517,8 @@ class ULID
|
|
|
500
517
|
self
|
|
501
518
|
end
|
|
502
519
|
|
|
503
|
-
# Generate a UUID-like string that does not
|
|
504
|
-
# It means wrong in
|
|
520
|
+
# Generate a UUID-like string that does not touch the version and variants field.
|
|
521
|
+
# It means basically wrong in UUID specs, but reversible
|
|
505
522
|
#
|
|
506
523
|
# @return [String]
|
|
507
524
|
def to_uuidish
|
|
@@ -516,8 +533,8 @@ class ULID
|
|
|
516
533
|
# @see https://github.com/kachick/ruby-ulid/issues/76
|
|
517
534
|
# @param [bool] force
|
|
518
535
|
# @return [String]
|
|
519
|
-
def
|
|
520
|
-
v4 = UUID::Fields.
|
|
536
|
+
def to_uuid_v4(force: false)
|
|
537
|
+
v4 = UUID::Fields.forced_version_from_octets(octets, mask: 0x4000)
|
|
521
538
|
unless force
|
|
522
539
|
uuidish = UUID::Fields.raw_from_octets(octets)
|
|
523
540
|
raise(IrreversibleUUIDError) unless uuidish == v4
|
|
@@ -526,6 +543,19 @@ class ULID
|
|
|
526
543
|
v4.to_s.freeze
|
|
527
544
|
end
|
|
528
545
|
|
|
546
|
+
# @see [#to_uuid_v4] and https://datatracker.ietf.org/doc/rfc9562/
|
|
547
|
+
# @param [bool] force
|
|
548
|
+
# @return [String]
|
|
549
|
+
def to_uuid_v7(force: false)
|
|
550
|
+
v7 = UUID::Fields.forced_version_from_octets(octets, mask: 0x7000)
|
|
551
|
+
unless force
|
|
552
|
+
uuidish = UUID::Fields.raw_from_octets(octets)
|
|
553
|
+
raise(IrreversibleUUIDError) unless uuidish == v7
|
|
554
|
+
end
|
|
555
|
+
|
|
556
|
+
v7.to_s.freeze
|
|
557
|
+
end
|
|
558
|
+
|
|
529
559
|
# @return [ULID]
|
|
530
560
|
def dup
|
|
531
561
|
super.freeze
|
data/sig/ulid.rbs
CHANGED
|
@@ -37,21 +37,23 @@ class ULID < Object
|
|
|
37
37
|
module UUID
|
|
38
38
|
BASE_PATTERN: Regexp
|
|
39
39
|
V4_PATTERN: Regexp
|
|
40
|
+
V7_PATTERN: Regexp
|
|
40
41
|
|
|
41
42
|
def self.parse_any_to_int: (String) -> Integer
|
|
42
43
|
def self.parse_v4_to_int: (String) -> Integer
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
def self.parse_v7_to_int: (String) -> Integer
|
|
45
|
+
|
|
46
|
+
class Fields
|
|
47
|
+
attr_reader time_low: Integer
|
|
48
|
+
attr_reader time_mid: Integer
|
|
49
|
+
attr_reader time_hi_and_version: Integer
|
|
50
|
+
attr_reader clock_seq_hi_and_res: Integer
|
|
51
|
+
attr_reader clk_seq_low: Integer
|
|
52
|
+
attr_reader node: Integer
|
|
51
53
|
def self.raw_from_octets: (octets) -> Fields
|
|
52
|
-
def self.
|
|
54
|
+
def self.forced_version_from_octets: (octets, mask: Integer) -> Fields
|
|
53
55
|
|
|
54
|
-
def
|
|
56
|
+
def deconstruct: -> Array[Integer]
|
|
55
57
|
|
|
56
58
|
private
|
|
57
59
|
|
|
@@ -69,8 +71,6 @@ class ULID < Object
|
|
|
69
71
|
|
|
70
72
|
def self.reasonable_entropy: -> Integer
|
|
71
73
|
|
|
72
|
-
def self.milliseconds_from_time: (Time time) -> milliseconds
|
|
73
|
-
|
|
74
74
|
def self.safe_get_class_name: (untyped object) -> String
|
|
75
75
|
|
|
76
76
|
def self.make_sharable_constants: (Module) -> void
|
|
@@ -147,7 +147,7 @@ class ULID < Object
|
|
|
147
147
|
@integer: Integer
|
|
148
148
|
@encoded: String
|
|
149
149
|
|
|
150
|
-
#
|
|
150
|
+
# Returns a ULID
|
|
151
151
|
#
|
|
152
152
|
# They are sortable when generated in different timestamp with milliseconds precision
|
|
153
153
|
#
|
|
@@ -183,7 +183,7 @@ class ULID < Object
|
|
|
183
183
|
#
|
|
184
184
|
def self.generate: (?moment: moment, ?entropy: Integer) -> ULID
|
|
185
185
|
|
|
186
|
-
#
|
|
186
|
+
# Returns encoded and normalzied String.\
|
|
187
187
|
# It has same arguments signatures as `.generate`\
|
|
188
188
|
# So can be used for just ID creation usecases without needless object creation.
|
|
189
189
|
#
|
|
@@ -287,20 +287,23 @@ class ULID < Object
|
|
|
287
287
|
# #=> ULID(2605-08-20 10:28:29.979 UTC: 0J7S2PFT4V2B9T8NJ2CRA1EG00)
|
|
288
288
|
# ```
|
|
289
289
|
#
|
|
290
|
-
# See also [ULID.
|
|
290
|
+
# See also [ULID.from_uuid_v4]
|
|
291
291
|
def self.from_uuidish: (String uuidish) -> ULID
|
|
292
292
|
|
|
293
293
|
# Load a UUIDv4 string with checking version and variants.
|
|
294
294
|
#
|
|
295
295
|
# ```ruby
|
|
296
|
-
# ULID.
|
|
296
|
+
# ULID.from_uuid_v4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
|
|
297
297
|
# #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
|
|
298
|
-
# ULID.
|
|
298
|
+
# ULID.from_uuid_v4('123e4567-e89b-12d3-a456-426614174000')
|
|
299
299
|
# #=> ULID::ParserError
|
|
300
300
|
# ```
|
|
301
301
|
#
|
|
302
302
|
# See also [ULID.from_uuidish]
|
|
303
|
-
def self.
|
|
303
|
+
def self.from_uuid_v4: (String uuid) -> ULID
|
|
304
|
+
|
|
305
|
+
# See also [ULID.from_uuid_v4]
|
|
306
|
+
def self.from_uuid_v7: (String uuid) -> ULID
|
|
304
307
|
|
|
305
308
|
# Load integer as ULID
|
|
306
309
|
#
|
|
@@ -594,6 +597,18 @@ class ULID < Object
|
|
|
594
597
|
# ```
|
|
595
598
|
def octets: -> octets
|
|
596
599
|
|
|
600
|
+
# Returns incremented ULID.\
|
|
601
|
+
# Especially providing for Range#step since ruby-3.4.0 spec changes
|
|
602
|
+
#
|
|
603
|
+
# See also [ruby-lang#18368](https://bugs.ruby-lang.org/issues/18368)
|
|
604
|
+
# ```
|
|
605
|
+
def +: (Integer other) -> ULID?
|
|
606
|
+
|
|
607
|
+
# Returns decremented ULID.\
|
|
608
|
+
# Providing for realizing natural API convention with the `ULID#+`
|
|
609
|
+
# ```
|
|
610
|
+
def -: (Integer other) -> ULID?
|
|
611
|
+
|
|
597
612
|
# Returns next(successor) ULID.\
|
|
598
613
|
# Especially `ULID#succ` makes it possible `Range[ULID]#each`.
|
|
599
614
|
#
|
|
@@ -636,7 +651,7 @@ class ULID < Object
|
|
|
636
651
|
# ULID.from_uuidish(ulid.to_uuidish) #=> ULID(2023-03-07 11:48:07.469 UTC: 01GTXYCWNDKRYH14DBZ77TRSD7)
|
|
637
652
|
# ```
|
|
638
653
|
#
|
|
639
|
-
# See also [ULID.from_uuidish], [ULID#
|
|
654
|
+
# See also [ULID.from_uuidish], [ULID#to_uuid_v4], [ulid/spec#64](https://github.com/ulid/spec/issues/64)
|
|
640
655
|
def to_uuidish: -> String
|
|
641
656
|
|
|
642
657
|
# Generate a UUIDv4-like string that sets the version and variants field.\
|
|
@@ -645,18 +660,21 @@ class ULID < Object
|
|
|
645
660
|
#
|
|
646
661
|
# ```ruby
|
|
647
662
|
# uuid = '0983d0a2-ff15-4d83-8f37-7dd945b5aa39'
|
|
648
|
-
# ulid = ULID.
|
|
649
|
-
# ulid.
|
|
663
|
+
# ulid = ULID.from_uuid_v4(uuid)
|
|
664
|
+
# ulid.to_uuid_v4 #=> 0983d0a2-ff15-4d83-8f37-7dd945b5aa39
|
|
650
665
|
# ```
|
|
651
666
|
#
|
|
652
667
|
# ```ruby
|
|
653
668
|
# ulid = ULID.from_uuidish('0186bbe6-72ad-9e3d-1091-abf9cfac65a7')
|
|
654
|
-
# ulid.
|
|
655
|
-
# ulid.
|
|
669
|
+
# ulid.to_uuid_v4 #=> ULID::IrreversibleUUIDError
|
|
670
|
+
# ulid.to_uuid_v4(force: true) #=> '0186bbe6-72ad-4e3d-9091-abf9cfac65a7'
|
|
656
671
|
# ```
|
|
657
672
|
#
|
|
658
|
-
# See also [ULID.
|
|
659
|
-
def
|
|
673
|
+
# See also [ULID.from_uuid_v4], [ULID#to_uuidish], [ulid/spec#64](https://github.com/ulid/spec/issues/64)
|
|
674
|
+
def to_uuid_v4: (?force: boolish) -> String
|
|
675
|
+
|
|
676
|
+
# See also [ULID.from_uuid_v7], [ULID#to_uuidish]
|
|
677
|
+
def to_uuid_v7: (?force: boolish) -> String
|
|
660
678
|
|
|
661
679
|
# Returns same ID with different Ruby object.
|
|
662
680
|
def dup: -> ULID
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ruby-ulid
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kenichi Kamiya
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
12
|
description: " generator, optional monotonicity, parser and tools for ULID (RBS
|
|
14
13
|
included)\n"
|
|
@@ -27,6 +26,7 @@ files:
|
|
|
27
26
|
- lib/ulid/monotonic_generator.rb
|
|
28
27
|
- lib/ulid/utils.rb
|
|
29
28
|
- lib/ulid/uuid.rb
|
|
29
|
+
- lib/ulid/uuid/fields.rb
|
|
30
30
|
- lib/ulid/version.rb
|
|
31
31
|
- sig/ulid.rbs
|
|
32
32
|
homepage: https://github.com/kachick/ruby-ulid
|
|
@@ -34,11 +34,9 @@ licenses:
|
|
|
34
34
|
- MIT
|
|
35
35
|
metadata:
|
|
36
36
|
documentation_uri: https://kachick.github.io/ruby-ulid/
|
|
37
|
-
homepage_uri: https://github.com/kachick/ruby-ulid
|
|
38
37
|
source_code_uri: https://github.com/kachick/ruby-ulid
|
|
39
38
|
bug_tracker_uri: https://github.com/kachick/ruby-ulid/issues
|
|
40
39
|
rubygems_mfa_required: 'true'
|
|
41
|
-
post_install_message:
|
|
42
40
|
rdoc_options: []
|
|
43
41
|
require_paths:
|
|
44
42
|
- lib
|
|
@@ -46,15 +44,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
46
44
|
requirements:
|
|
47
45
|
- - ">="
|
|
48
46
|
- !ruby/object:Gem::Version
|
|
49
|
-
version: '
|
|
47
|
+
version: '4.0'
|
|
50
48
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
51
49
|
requirements:
|
|
52
50
|
- - ">="
|
|
53
51
|
- !ruby/object:Gem::Version
|
|
54
52
|
version: '0'
|
|
55
53
|
requirements: []
|
|
56
|
-
rubygems_version:
|
|
57
|
-
signing_key:
|
|
54
|
+
rubygems_version: 4.0.3
|
|
58
55
|
specification_version: 4
|
|
59
56
|
summary: ULID manipulation library
|
|
60
57
|
test_files: []
|