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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 15a83604732cdc37f8015ee9382884e950852f8c9c82aba107ea4b3c065c5a4d
4
- data.tar.gz: 06e7b69a5838786f4d23da19f5e1f00fb3173014e7438459165b66703637851a
3
+ metadata.gz: 29a9e3cd7ec91b93c24691976fef74ea5ab0cbc8f4fa237f449fa9322e42c6d2
4
+ data.tar.gz: 9a3c0e12e7d2fe8b6057662ebf6a392c749f7254ee41c4e77221a2e9f45ff6ac
5
5
  SHA512:
6
- metadata.gz: 6ca46525e80b1d832a703b8cb867466327de333853ee236654f722dc6abc30f46fd149ca9633c42ececcea08db2a01a6d82ab60f8eeb61ebb947f0441436dd96
7
- data.tar.gz: ccd08e78dfb047f82af02eeaf14ccd12fcb05fe882edeb2bf48c2e7ec5b9fd698feccf0b9f9e187aaff97c2e793f671a3f9d32c11e3741656c3df94f944bc808
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
- 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)]
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
- # @deprecated This method actually changes class state. Use {ULID::MonotonicGenerator} instead.
65
- # @raise [OverflowError] if the entropy part is larger than the ULID limit in same milliseconds
66
- # @return [ULID]
67
- def self.monotonic_generate
68
- warning = "`ULID.monotonic_generate` actually changes class state. Use `ULID::MonotonicGenerator` instead."
69
- if RUBY_VERSION >= '3.0'
70
- Warning.warn(warning, category: :deprecated)
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
- Warning.warn(warning)
73
- end
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
- MONOTONIC_GENERATOR.generate
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 = min
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 = max
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
- REPLACING_MAP = ENCODING_CHARS.each_with_object({}) do |encoding_char, map|
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 REPLACING_MAP.keys == crockford_base32_mappings.keys
255
- REPLACING_PATTERN = /[#{REPLACING_MAP.keys.join}]/.freeze
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(REPLACING_PATTERN, REPLACING_MAP)
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 ||= Integer::Base.string_for(to_i, ENCODING_CHARS).rjust(ENCODED_ID_LENGTH, '0').upcase.freeze
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, :REPLACING_PATTERN, :REPLACING_MAP
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
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  class ULID
5
- VERSION = '0.0.16'
5
+ VERSION = '0.0.17'
6
6
  end
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
- REPLACING_MAP: Hash[String, String]
19
- REPLACING_PATTERN: Regexp
20
- MONOTONIC_GENERATOR: MonotonicGenerator
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.16
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-05 00:00:00.000000000 Z
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