ruby-ulid 0.0.16 → 0.0.17
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +22 -6
- data/lib/ulid.rb +47 -43
- data/lib/ulid/version.rb +1 -1
- data/sig/ulid.rbs +9 -5
- metadata +2 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29a9e3cd7ec91b93c24691976fef74ea5ab0cbc8f4fa237f449fa9322e42c6d2
|
4
|
+
data.tar.gz: 9a3c0e12e7d2fe8b6057662ebf6a392c749f7254ee41c4e77221a2e9f45ff6ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 53a12c330c32f76f13e651cef96ce29a6a442cf3dc7742faf2409a0ee17c7dfdcee1031c17696cd51d7bd3cf5b22b2df34b8be5a203cd1d4fd5df079eb357b82
|
7
|
+
data.tar.gz: f6c8f8850272353bbd32cc15d437d0b021c13d9bc93c5c988255d6c50fe21ff41aef5b719261ca95b00c129024fe325e2d3c8683c70f9b55e89a09a261fff31f
|
data/README.md
CHANGED
@@ -207,12 +207,12 @@ EOD
|
|
207
207
|
|
208
208
|
ULID.scan(json).to_a
|
209
209
|
#=>
|
210
|
-
[ULID(2021-04-30 05:51:57.119 UTC: 01F4GNAV5ZR6FJQ5SFQC7WDSY3),
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
210
|
+
# [ULID(2021-04-30 05:51:57.119 UTC: 01F4GNAV5ZR6FJQ5SFQC7WDSY3),
|
211
|
+
# ULID(2021-04-30 05:52:32.641 UTC: 01F4GNBXW1AM2KWW52PVT3ZY9X),
|
212
|
+
# ULID(2021-04-30 05:52:56.707 UTC: 01F4GNCNC3CH0BCRZBPPDEKBKS),
|
213
|
+
# ULID(2021-04-30 05:52:32.641 UTC: 01F4GNBXW1AM2KWW52PVT3ZY9X),
|
214
|
+
# ULID(2021-04-30 05:53:04.852 UTC: 01F4GNCXAMXQ1SGBH5XCR6ZH0M),
|
215
|
+
# ULID(2021-04-30 05:53:12.478 UTC: 01F4GND4RYYSKNAADHQ9BNXAWJ)]
|
216
216
|
```
|
217
217
|
|
218
218
|
### Some methods to help manipulations
|
@@ -247,6 +247,22 @@ ULID.parse('01BX5ZZKBK0000000000000000').pred.to_s #=> "01BX5ZZKBJZZZZZZZZZZZZZZ
|
|
247
247
|
ULID.parse('00000000000000000000000000').pred #=> nil
|
248
248
|
```
|
249
249
|
|
250
|
+
`ULID.sample` returns random ULIDs ignoring the generating time
|
251
|
+
|
252
|
+
```ruby
|
253
|
+
ULID.sample #=> ULID(2545-07-26 06:51:20.085 UTC: 0GGKQ45GMNMZR6N8A8GFG0ZXST)
|
254
|
+
ULID.sample #=> ULID(5098-07-26 21:31:06.946 UTC: 2SSBNGGYA272J7BMDCG4Z6EEM5)
|
255
|
+
ULID.sample(0) #=> []
|
256
|
+
ULID.sample(1) #=> [ULID(2241-04-16 03:31:18.440 UTC: 07S52YWZ98AZ8T565MD9VRYMQH)]
|
257
|
+
ULID.sample(5)
|
258
|
+
#=>
|
259
|
+
#[ULID(5701-04-29 12:41:19.647 UTC: 3B2YH2DV0ZYDDATGTYSKMM1CMT),
|
260
|
+
# ULID(2816-08-01 01:21:46.612 UTC: 0R9GT6RZKMK3RG02Q2HAFVKEY2),
|
261
|
+
# ULID(10408-10-05 17:06:27.848 UTC: 7J6CPTEEC86Y24EQ4F1Y93YYN0),
|
262
|
+
# ULID(2741-09-02 16:24:18.803 UTC: 0P4Q4V34KKAJW46QW47WQB5463),
|
263
|
+
# ULID(2665-03-16 14:50:22.724 UTC: 0KYFW9DWM4CEGFNTAC6YFAVVJ6)]
|
264
|
+
```
|
265
|
+
|
250
266
|
### UUIDv4 converter for migration use-cases
|
251
267
|
|
252
268
|
`ULID.from_uuidv4` and `ULID#to_uuidv4` is the converter.
|
data/lib/ulid.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
# Copyright (C) 2021 Kenichi Kamiya
|
4
4
|
|
5
5
|
require 'securerandom'
|
6
|
-
require 'integer/base'
|
7
6
|
|
8
7
|
# @see https://github.com/ulid/spec
|
9
8
|
# @!attribute [r] milliseconds
|
@@ -42,6 +41,9 @@ class ULID
|
|
42
41
|
# @see https://bugs.ruby-lang.org/issues/15958
|
43
42
|
TIME_FORMAT_IN_INSPECT = '%Y-%m-%d %H:%M:%S.%3N %Z'
|
44
43
|
|
44
|
+
UNDEFINED = BasicObject.new
|
45
|
+
Kernel.instance_method(:freeze).bind(UNDEFINED).call
|
46
|
+
|
45
47
|
# @param [Integer, Time] moment
|
46
48
|
# @param [Integer] entropy
|
47
49
|
# @return [ULID]
|
@@ -61,18 +63,29 @@ class ULID
|
|
61
63
|
MAX_MILLISECONDS.equal?(moment) ? MAX : generate(moment: moment, entropy: MAX_ENTROPY)
|
62
64
|
end
|
63
65
|
|
64
|
-
# @
|
65
|
-
# @
|
66
|
-
# @
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
66
|
+
# @param [Integer] number
|
67
|
+
# @return [ULID, Array<ULID>]
|
68
|
+
# @raise [ArgumentError] if the given number is lager than ULID spec limits or given negative number
|
69
|
+
# @note Major difference of `Array#sample` interface is below
|
70
|
+
# * Do not ensure the uniqueness
|
71
|
+
# * Do not take random generator for the arguments
|
72
|
+
# * Raising error instead of truncating elements for the given number
|
73
|
+
def self.sample(number=UNDEFINED)
|
74
|
+
if UNDEFINED.equal?(number)
|
75
|
+
from_integer(SecureRandom.random_number(MAX_INTEGER))
|
71
76
|
else
|
72
|
-
|
73
|
-
|
77
|
+
begin
|
78
|
+
int = number.to_int
|
79
|
+
rescue
|
80
|
+
# Can not use `number.to_s` and `number.inspect` for considering BasicObject here
|
81
|
+
raise TypeError, 'accepts no argument or integer only'
|
82
|
+
end
|
74
83
|
|
75
|
-
|
84
|
+
if int > MAX_INTEGER || int.negative?
|
85
|
+
raise ArgumentError, "given number is larger than ULID limit #{MAX_INTEGER} or negative: #{number.inspect}"
|
86
|
+
end
|
87
|
+
int.times.map { from_integer(SecureRandom.random_number(MAX_INTEGER)) }
|
88
|
+
end
|
76
89
|
end
|
77
90
|
|
78
91
|
# @param [String, #to_str] string
|
@@ -133,7 +146,7 @@ class ULID
|
|
133
146
|
when Time
|
134
147
|
begin_ulid = min(moment: begin_time)
|
135
148
|
when nil
|
136
|
-
begin_ulid =
|
149
|
+
begin_ulid = MIN
|
137
150
|
else
|
138
151
|
raise argument_error_for_range_building(time_range)
|
139
152
|
end
|
@@ -147,7 +160,7 @@ class ULID
|
|
147
160
|
end
|
148
161
|
when nil
|
149
162
|
# The end should be max and include end, because nil end means to cover endless ULIDs until the limit
|
150
|
-
end_ulid =
|
163
|
+
end_ulid = MAX
|
151
164
|
exclude_end = false
|
152
165
|
else
|
153
166
|
raise argument_error_for_range_building(time_range)
|
@@ -169,51 +182,32 @@ class ULID
|
|
169
182
|
end
|
170
183
|
end
|
171
184
|
|
185
|
+
# @api private
|
172
186
|
# @return [Integer]
|
173
187
|
def self.current_milliseconds
|
174
188
|
milliseconds_from_time(Time.now)
|
175
189
|
end
|
176
190
|
|
191
|
+
# @api private
|
177
192
|
# @param [Time] time
|
178
193
|
# @return [Integer]
|
179
194
|
def self.milliseconds_from_time(time)
|
180
195
|
(time.to_r * 1000).to_i
|
181
196
|
end
|
182
197
|
|
198
|
+
# @api private
|
183
199
|
# @param [Time, Integer] moment
|
184
200
|
# @return [Integer]
|
185
201
|
def self.milliseconds_from_moment(moment)
|
186
202
|
moment.kind_of?(Time) ? milliseconds_from_time(moment) : moment.to_int
|
187
203
|
end
|
188
204
|
|
205
|
+
# @api private
|
189
206
|
# @return [Integer]
|
190
207
|
def self.reasonable_entropy
|
191
208
|
SecureRandom.random_number(MAX_ENTROPY)
|
192
209
|
end
|
193
210
|
|
194
|
-
# @api private
|
195
|
-
# @deprecated Just exists to compare performance with old implementation. ref: https://github.com/kachick/ruby-ulid/issues/7
|
196
|
-
# @param [String, #to_str] string
|
197
|
-
# @return [ULID]
|
198
|
-
# @raise [ParserError] if the given format is not correct for ULID specs
|
199
|
-
# @raise [OverflowError] if the given value is larger than the ULID limit
|
200
|
-
def self.parse_with_integer_base(string)
|
201
|
-
begin
|
202
|
-
string = string.to_str
|
203
|
-
unless string.size == ENCODED_ID_LENGTH
|
204
|
-
raise "parsable string must be #{ENCODED_ID_LENGTH} characters, but actually given #{string.size} characters"
|
205
|
-
end
|
206
|
-
timestamp = string.slice(0, TIMESTAMP_PART_LENGTH)
|
207
|
-
randomness = string.slice(TIMESTAMP_PART_LENGTH, RANDOMNESS_PART_LENGTH)
|
208
|
-
milliseconds = Integer::Base.parse(timestamp, ENCODING_CHARS)
|
209
|
-
entropy = Integer::Base.parse(randomness, ENCODING_CHARS)
|
210
|
-
rescue => err
|
211
|
-
raise ParserError, "parsing failure as #{err.inspect} for given #{string.inspect}"
|
212
|
-
end
|
213
|
-
|
214
|
-
new milliseconds: milliseconds, entropy: entropy
|
215
|
-
end
|
216
|
-
|
217
211
|
n32_chars = [*'0'..'9', *'A'..'V'].map(&:freeze).freeze
|
218
212
|
raise SetupError, 'obvious bug exists in the mapping algorithm' unless n32_chars.size == 32
|
219
213
|
|
@@ -245,15 +239,21 @@ class ULID
|
|
245
239
|
'Z' => 31
|
246
240
|
}.freeze
|
247
241
|
|
248
|
-
|
242
|
+
N32_CHAR_BY_CROCKFORD_BASE32_CHAR = ENCODING_CHARS.each_with_object({}) do |encoding_char, map|
|
249
243
|
if n = crockford_base32_mappings[encoding_char]
|
250
244
|
char_32 = n32_char_by_number.fetch(n)
|
251
245
|
map[encoding_char] = char_32
|
252
246
|
end
|
253
247
|
end.freeze
|
254
|
-
raise SetupError, 'obvious bug exists in the mapping algorithm' unless
|
255
|
-
|
248
|
+
raise SetupError, 'obvious bug exists in the mapping algorithm' unless N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys == crockford_base32_mappings.keys
|
249
|
+
CROCKFORD_BASE32_CHAR_PATTERN = /[#{N32_CHAR_BY_CROCKFORD_BASE32_CHAR.keys.join}]/.freeze
|
250
|
+
|
251
|
+
CROCKFORD_BASE32_CHAR_BY_N32_CHAR = N32_CHAR_BY_CROCKFORD_BASE32_CHAR.invert.freeze
|
252
|
+
N32_CHAR_PATTERN = /[#{CROCKFORD_BASE32_CHAR_BY_N32_CHAR.keys.join}]/.freeze
|
256
253
|
|
254
|
+
# @param [String, #to_str] string
|
255
|
+
# @return [ULID]
|
256
|
+
# @raise [ParserError] if the given format is not correct for ULID specs
|
257
257
|
def self.parse(string)
|
258
258
|
begin
|
259
259
|
string = string.to_str
|
@@ -272,7 +272,7 @@ class ULID
|
|
272
272
|
|
273
273
|
# @api private
|
274
274
|
private_class_method def self.convert_crockford_base32_to_n32(string)
|
275
|
-
string.gsub(
|
275
|
+
string.gsub(CROCKFORD_BASE32_CHAR_PATTERN, N32_CHAR_BY_CROCKFORD_BASE32_CHAR)
|
276
276
|
end
|
277
277
|
|
278
278
|
# @return [Boolean]
|
@@ -335,7 +335,7 @@ class ULID
|
|
335
335
|
|
336
336
|
# @return [String]
|
337
337
|
def to_s
|
338
|
-
@string ||=
|
338
|
+
@string ||= convert_n32_to_crockford_base32(to_i.to_s(32).rjust(ENCODED_ID_LENGTH, '0').upcase).freeze
|
339
339
|
end
|
340
340
|
|
341
341
|
# @return [Integer]
|
@@ -459,6 +459,11 @@ class ULID
|
|
459
459
|
|
460
460
|
private
|
461
461
|
|
462
|
+
# @api private
|
463
|
+
def convert_n32_to_crockford_base32(string)
|
464
|
+
string.gsub(N32_CHAR_PATTERN, CROCKFORD_BASE32_CHAR_BY_N32_CHAR)
|
465
|
+
end
|
466
|
+
|
462
467
|
# @return [MatchData]
|
463
468
|
def matchdata
|
464
469
|
@matchdata ||= STRICT_PATTERN.match(to_s).freeze
|
@@ -482,7 +487,6 @@ require_relative 'ulid/monotonic_generator'
|
|
482
487
|
class ULID
|
483
488
|
MIN = parse('00000000000000000000000000').freeze
|
484
489
|
MAX = parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').freeze
|
485
|
-
MONOTONIC_GENERATOR = MonotonicGenerator.new
|
486
490
|
|
487
|
-
private_constant :ENCODING_CHARS, :TIME_FORMAT_IN_INSPECT, :UUIDV4_PATTERN, :MIN, :MAX, :
|
491
|
+
private_constant :ENCODING_CHARS, :TIME_FORMAT_IN_INSPECT, :UUIDV4_PATTERN, :MIN, :MAX, :CROCKFORD_BASE32_CHAR_PATTERN, :N32_CHAR_BY_CROCKFORD_BASE32_CHAR, :CROCKFORD_BASE32_CHAR_BY_N32_CHAR, :N32_CHAR_PATTERN, :UNDEFINED
|
488
492
|
end
|
data/lib/ulid/version.rb
CHANGED
data/sig/ulid.rbs
CHANGED
@@ -15,11 +15,13 @@ class ULID
|
|
15
15
|
PATTERN: Regexp
|
16
16
|
STRICT_PATTERN: Regexp
|
17
17
|
UUIDV4_PATTERN: Regexp
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
N32_CHAR_BY_CROCKFORD_BASE32_CHAR: Hash[String, String]
|
19
|
+
CROCKFORD_BASE32_CHAR_PATTERN: Regexp
|
20
|
+
CROCKFORD_BASE32_CHAR_BY_N32_CHAR: Hash[String, String]
|
21
|
+
N32_CHAR_PATTERN: Regexp
|
21
22
|
MIN: ULID
|
22
23
|
MAX: ULID
|
24
|
+
UNDEFINED: BasicObject
|
23
25
|
include Comparable
|
24
26
|
|
25
27
|
# The `moment` is a `Time` or `Intger of the milliseconds`
|
@@ -68,7 +70,6 @@ class ULID
|
|
68
70
|
@matchdata: MatchData?
|
69
71
|
|
70
72
|
def self.generate: (?moment: moment, ?entropy: Integer) -> ULID
|
71
|
-
def self.monotonic_generate: -> ULID
|
72
73
|
def self.current_milliseconds: -> Integer
|
73
74
|
def self.milliseconds_from_time: (Time time) -> Integer
|
74
75
|
def self.milliseconds_from_moment: (moment moment) -> Integer
|
@@ -80,12 +81,13 @@ class ULID
|
|
80
81
|
def self.from_integer: (Integer integer) -> ULID
|
81
82
|
def self.min: (?moment: moment) -> ULID
|
82
83
|
def self.max: (?moment: moment) -> ULID
|
84
|
+
def self.sample: -> ULID
|
85
|
+
| (Integer number) -> Array[ULID]
|
83
86
|
def self.valid?: (untyped string) -> bool
|
84
87
|
def self.scan: (String string) -> Enumerator[ULID, singleton(ULID)]
|
85
88
|
| (String string) { (ULID ulid) -> void } -> singleton(ULID)
|
86
89
|
def self.octets_from_integer: (Integer integer, length: Integer) -> Array[Integer]
|
87
90
|
def self.inverse_of_digits: (Array[Integer] reversed_digits) -> Integer
|
88
|
-
def self.convert_crockford_base32_to_n32: (String) -> String
|
89
91
|
attr_reader milliseconds: Integer
|
90
92
|
attr_reader entropy: Integer
|
91
93
|
def initialize: (milliseconds: Integer, entropy: Integer) -> void
|
@@ -113,7 +115,9 @@ class ULID
|
|
113
115
|
def freeze: -> self
|
114
116
|
|
115
117
|
private
|
118
|
+
def self.convert_crockford_base32_to_n32: (String) -> String
|
116
119
|
def self.argument_error_for_range_building: (untyped argument) -> ArgumentError
|
120
|
+
def convert_n32_to_crockford_base32: (String) -> String
|
117
121
|
def matchdata: -> MatchData
|
118
122
|
def cache_all_instance_variables: -> void
|
119
123
|
end
|
metadata
CHANGED
@@ -1,35 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-ulid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.17
|
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-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: integer-base
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 0.1.2
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: 0.2.0
|
23
|
-
type: :runtime
|
24
|
-
prerelease: false
|
25
|
-
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
requirements:
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 0.1.2
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: 0.2.0
|
33
13
|
- !ruby/object:Gem::Dependency
|
34
14
|
name: rbs
|
35
15
|
requirement: !ruby/object:Gem::Requirement
|