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 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