ruby-ulid 0.1.3 → 0.1.4
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 +31 -4
- data/lib/ulid.rb +21 -0
- data/lib/ulid/crockford_base32.rb +15 -0
- data/lib/ulid/monotonic_generator.rb +4 -2
- data/lib/ulid/version.rb +1 -1
- data/sig/ulid.rbs +50 -4
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a390455a27395fd77ef398b0613e72b321b96d44e8454c8ab9346ae15eddfdc
|
4
|
+
data.tar.gz: 1150658ae26cc591f3946898ccb356ce1c6135292de0600ef0604aceb7a00554
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ffb5c69ec1327bacbb54b4afdd6718f8cc9b5afd450f739588d8e414c645eca9c1090c77efb464293f0e8e1b960cf4df75289785d3cd7b489404e31c24ecc8a
|
7
|
+
data.tar.gz: bf6a08216e0745d0727c356a5a239126bac8518f8fbb57f295b417e14ed0878c8f0c32774b83137b1feb58d8230140492cb469bb18dc9d5df5db5aa088410ffb
|
data/README.md
CHANGED
@@ -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)
|
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.
|
52
|
+
gem 'ruby-ulid', '>= 0.1.4', '< 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 [
|
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
|
|
@@ -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.
|
@@ -418,4 +446,3 @@ The results are not something to be proud of.
|
|
418
446
|
## Note
|
419
447
|
|
420
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)
|
421
|
-
- Current parser/validator/matcher aims to cover `subset of Crockford's base32`. Suggesting it in [ulid/spec#57](https://github.com/ulid/spec/pull/57). Be that as it may, I might provide special handler or converter for the exception in [ruby-ulid#57](https://github.com/kachick/ruby-ulid/issues/57) and/or [ruby-ulid#78](https://github.com/kachick/ruby-ulid/issues/78)
|
data/lib/ulid.rb
CHANGED
@@ -259,6 +259,27 @@ class ULID
|
|
259
259
|
from_integer(CrockfordBase32.decode(string))
|
260
260
|
end
|
261
261
|
|
262
|
+
# @param [String, #to_str] string
|
263
|
+
# @return [String]
|
264
|
+
# @raise [ParserError] if the given format is not correct for ULID specs, even if ignored `orthographical variants of the format`
|
265
|
+
def self.normalize(string)
|
266
|
+
string = String.try_convert(string)
|
267
|
+
raise ArgumentError, 'ULID.normalize takes only strings' unless string
|
268
|
+
|
269
|
+
normalized_in_crockford = CrockfordBase32.normalize(string)
|
270
|
+
# Ensure the ULID correctness, because CrockfordBase32 does not always mean to satisfy ULID format
|
271
|
+
parse(normalized_in_crockford).to_s
|
272
|
+
end
|
273
|
+
|
274
|
+
# @return [Boolean]
|
275
|
+
def self.normalized?(object)
|
276
|
+
normalized = normalize(object)
|
277
|
+
rescue Exception
|
278
|
+
false
|
279
|
+
else
|
280
|
+
normalized == object
|
281
|
+
end
|
282
|
+
|
262
283
|
# @return [Boolean]
|
263
284
|
def self.valid?(object)
|
264
285
|
string = String.try_convert(object)
|
@@ -51,6 +51,14 @@ class ULID
|
|
51
51
|
CROCKFORD_BASE32_CHAR_BY_N32_CHAR = N32_CHAR_BY_CROCKFORD_BASE32_CHAR.invert.freeze
|
52
52
|
N32_CHAR_PATTERN = /[#{CROCKFORD_BASE32_CHAR_BY_N32_CHAR.keys.join}]/.freeze
|
53
53
|
|
54
|
+
VARIANT_BY_STANDARD = {
|
55
|
+
'L' => '1',
|
56
|
+
'I' => '1',
|
57
|
+
'O' => '0',
|
58
|
+
'-' => ''
|
59
|
+
}.freeze
|
60
|
+
VARIANT_PATTERN = /[#{VARIANT_BY_STANDARD.keys.join}]/.freeze
|
61
|
+
|
54
62
|
# @api private
|
55
63
|
# @param [String] string
|
56
64
|
# @return [Integer]
|
@@ -66,5 +74,12 @@ class ULID
|
|
66
74
|
n32encoded = integer.to_s(32)
|
67
75
|
n32encoded.upcase.gsub(N32_CHAR_PATTERN, CROCKFORD_BASE32_CHAR_BY_N32_CHAR).rjust(ENCODED_LENGTH, '0')
|
68
76
|
end
|
77
|
+
|
78
|
+
# @api private
|
79
|
+
# @param [String] string
|
80
|
+
# @return [String]
|
81
|
+
def self.normalize(string)
|
82
|
+
string.upcase.gsub(VARIANT_PATTERN, VARIANT_BY_STANDARD)
|
83
|
+
end
|
69
84
|
end
|
70
85
|
end
|
@@ -4,13 +4,15 @@
|
|
4
4
|
|
5
5
|
class ULID
|
6
6
|
class MonotonicGenerator
|
7
|
+
include MonitorMixin
|
8
|
+
|
7
9
|
# @return [ULID, nil]
|
8
10
|
attr_reader :prev
|
9
11
|
|
10
12
|
undef_method :instance_variable_set
|
11
13
|
|
12
14
|
def initialize
|
13
|
-
|
15
|
+
super()
|
14
16
|
@prev = nil
|
15
17
|
end
|
16
18
|
|
@@ -26,7 +28,7 @@ class ULID
|
|
26
28
|
# @raise [UnexpectedError] if the generated ULID is an invalid value in monotonicity spec.
|
27
29
|
# Basically will not happen. Just means this feature prefers error rather than invalid value.
|
28
30
|
def generate(moment: ULID.current_milliseconds)
|
29
|
-
|
31
|
+
synchronize do
|
30
32
|
unless @prev
|
31
33
|
@prev = ULID.generate(moment: moment)
|
32
34
|
return @prev
|
data/lib/ulid/version.rb
CHANGED
data/sig/ulid.rbs
CHANGED
@@ -43,27 +43,51 @@ class ULID < Object
|
|
43
43
|
CROCKFORD_BASE32_CHAR_PATTERN: Regexp
|
44
44
|
CROCKFORD_BASE32_CHAR_BY_N32_CHAR: Hash[String, String]
|
45
45
|
N32_CHAR_PATTERN: Regexp
|
46
|
+
VARIANT_BY_STANDARD: Hash[String, String]
|
47
|
+
VARIANT_PATTERN: Regexp
|
46
48
|
|
47
49
|
# A pribate API. Should not be used in your code.
|
48
50
|
def self.encode: (Integer integer) -> String
|
49
51
|
|
50
52
|
# A pribate API. Should not be used in your code.
|
51
53
|
def self.decode: (String string) -> Integer
|
54
|
+
|
55
|
+
# A pribate API. Should not be used in your code.
|
56
|
+
def self.normalize: (String string) -> String
|
52
57
|
end
|
53
58
|
|
54
59
|
class MonotonicGenerator
|
55
|
-
|
56
|
-
|
57
|
-
#
|
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
|
+
# ```
|
58
72
|
attr_reader prev: ULID | nil
|
59
73
|
|
60
74
|
# A pribate API. Should not be used in your code.
|
61
75
|
def initialize: -> void
|
62
76
|
|
63
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)
|
64
79
|
def generate: (?moment: moment) -> ULID
|
65
80
|
|
66
|
-
#
|
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
|
+
# ```
|
67
91
|
def inspect: -> String
|
68
92
|
alias to_s inspect
|
69
93
|
def freeze: -> void
|
@@ -298,6 +322,28 @@ class ULID < Object
|
|
298
322
|
| (Integer number, ?period: period) -> Array[self]
|
299
323
|
def self.valid?: (untyped) -> bool
|
300
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
|
+
|
301
347
|
# Returns parsed ULIDs from given String for rough operations.
|
302
348
|
#
|
303
349
|
# ```ruby
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-ulid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
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-
|
11
|
+
date: 2021-05-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: test-unit
|