ruby-ulid 0.0.16 → 0.0.17
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 +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
|