ruby-ulid 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|