ruby-ulid 0.0.14 → 0.0.15

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: fa89ecbb2a09940666b57e690d43a985872bb85f40b2b1175c79a762d79c8e45
4
- data.tar.gz: 2404fe5e1efa77899262fbf8979529fc474e44ffa0a8572ee3cf71eb1caf941a
3
+ metadata.gz: 6a620e80f82e91c778329ccf7da8cca96bda4b12e047610b9543768c5ec85d22
4
+ data.tar.gz: ea28469d63348758f52e1460b5330ce177ad2281e63060800a0669054758f3ee
5
5
  SHA512:
6
- metadata.gz: 1a86224846b27985d641bf485f952583c73dafc87a0ca8f65744cfdfa35371a6f5cfedb4246e7839da6e51fdcfd53632f20057c399038f3399f2986a9bf20085
7
- data.tar.gz: 24daff089a2b0564a2e7a93c34fef4d44420d1e0bbc6a054c5bcdfac02986c66204515bc085a7dc6499257692682217a888d8a04e04a7ae8de36e884cc182965
6
+ metadata.gz: acb07ae797c3a06a6626d34e15dc1056a30586e8bc151a3773770259d2f4047d0708593e702fbd67a7609758165f20a15f208fede068717a5555733535ed4bde
7
+ data.tar.gz: 57c9819d86f2a0f002cf3b9a7b76b3a36bc412fa874d50136ec296f1217fb905fa19feb52de2c9d5250547da384580cf882121f0e331054cd665bd113d1f10e3
data/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  A handy `ULID` library
4
4
 
5
- The `ULID` spec is defined on [ulid/spec](https://github.com/ulid/spec).
5
+ The `ULID` spec is defined on [ulid/spec](https://github.com/ulid/spec). It has useful specs for applications (e.g. `Database key`), especially possess all `uniqueness`, `randomness`, `extractable timestamps` and `sortable` features.
6
6
  This gem aims to provide the generator, monotonic generator, parser and handy manipulation features around the ULID.
7
- Also providing rbs signature files.
7
+ Also providing [ruby/rbs](https://github.com/ruby/rbs) signature files.
8
8
 
9
9
  ---
10
10
 
@@ -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 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) # See also exists issues in [Note](#note)
32
32
  - Case insensitive
33
33
  - No special characters (URL safe)
34
34
  - Monotonic sort order (correctly detects and handles the same millisecond)
@@ -98,19 +98,19 @@ ulids.uniq(&:to_time).size #=> 35 (the size is not fixed, might be changed in en
98
98
  ulids.sort == ulids #=> false
99
99
  ```
100
100
 
101
- If you want to prefer `sortable` rather than the `randomness`, Use `MonotonicGenerator` instead. It is called as [Monotonicity](https://github.com/ulid/spec/tree/d0c7170df4517939e70129b4d6462cc162f2d5bf#monotonicity) on the spec.
101
+ If you want to ensure `sortable`, Use `MonotonicGenerator` instead. It is called as [Monotonicity](https://github.com/ulid/spec/tree/d0c7170df4517939e70129b4d6462cc162f2d5bf#monotonicity) on the spec.
102
102
  (Though it starts with new random value when changed the timestamp)
103
103
 
104
104
  ```ruby
105
105
  monotonic_generator = ULID::MonotonicGenerator.new
106
- monotonic_ulids = 10000.times.map do
106
+ ulids = 10000.times.map do
107
107
  monotonic_generator.generate
108
108
  end
109
- sample_ulids_by_the_time = monotonic_ulids.uniq(&:to_time)
109
+ sample_ulids_by_the_time = ulids.uniq(&:to_time)
110
110
  sample_ulids_by_the_time.size #=> 32 (the size is not fixed, might be changed in environment)
111
111
 
112
112
  # In same milliseconds creation, it just increments the end of randomness part
113
- monotonic_ulids.take(5) #=>
113
+ ulids.take(5) #=>
114
114
  # [ULID(2021-05-02 15:23:48.917 UTC: 01F4PTVCSN9ZPFKYTY2DDJVRK4),
115
115
  # ULID(2021-05-02 15:23:48.917 UTC: 01F4PTVCSN9ZPFKYTY2DDJVRK5),
116
116
  # ULID(2021-05-02 15:23:48.917 UTC: 01F4PTVCSN9ZPFKYTY2DDJVRK6),
@@ -125,7 +125,7 @@ sample_ulids_by_the_time.take(5) #=>
125
125
  # ULID(2021-05-02 15:23:48.920 UTC: 01F4PTVCSRBXN2H4P1EYWZ27AK),
126
126
  # ULID(2021-05-02 15:23:48.921 UTC: 01F4PTVCSSK0ASBBZARV7013F8)]
127
127
 
128
- monotonic_ulids.sort == monotonic_ulids #=> true
128
+ ulids.sort == ulids #=> true
129
129
  ```
130
130
 
131
131
  When filtering ULIDs by `Time`, we should consider to handle the precision.
@@ -209,11 +209,34 @@ ULID.parse('01BX5ZZKBK0000000000000000').pred.to_s #=> "01BX5ZZKBJZZZZZZZZZZZZZZ
209
209
  ULID.parse('00000000000000000000000000').pred #=> nil
210
210
  ```
211
211
 
212
- UUIDv4 converter for migration use-cases. (Of course the timestamp will be useless one. Sortable benefit is lost.)
212
+ ### UUIDv4 converter for migration use-cases
213
+
214
+ `ULID.from_uuidv4` and `ULID#to_uuidv4` is the converter.
215
+ The imported timestamp is meaningless. So ULID's benefit will lost
213
216
 
214
217
  ```ruby
215
- ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
216
- #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
218
+ # Basically reversible
219
+ ulid = ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39') #=> ULID(2301-07-10 00:28:28.821 UTC: 09GF8A5ZRN9P1RYDVXV52VBAHS)
220
+ ulid.to_uuidv4 #=> "0983d0a2-ff15-4d83-8f37-7dd945b5aa39"
221
+
222
+ uuid_v4s = 10000.times.map { SecureRandom.uuid }
223
+ uuid_v4s.uniq.size == 10000 #=> Probably `true`
224
+
225
+ ulids = uuid_v4s.map { |uuid_v4| ULID.from_uuidv4(uuid_v4) }
226
+ ulids.map(&:to_uuidv4) == uuid_v4s #=> **Probably** `true` except below examples.
227
+
228
+ # NOTE: Some boundary values are not reversible. See below.
229
+
230
+ ULID.min.to_uuidv4 #=> "00000000-0000-4000-8000-000000000000"
231
+ ULID.max.to_uuidv4 #=> "ffffffff-ffff-4fff-bfff-ffffffffffff"
232
+
233
+ # These importing results are same as https://github.com/ahawker/ulid/tree/96bdb1daad7ce96f6db8c91ac0410b66d2e1c4c1 on CPython 3.9.4
234
+ reversed_min = ULID.from_uuidv4('00000000-0000-4000-8000-000000000000') #=> ULID(1970-01-01 00:00:00.000 UTC: 00000000008008000000000000)
235
+ reversed_max = ULID.from_uuidv4('ffffffff-ffff-4fff-bfff-ffffffffffff') #=> ULID(10889-08-02 05:31:50.655 UTC: 7ZZZZZZZZZ9ZZVZZZZZZZZZZZZ)
236
+
237
+ # But they are not reversible! Need to consider this issue in https://github.com/kachick/ruby-ulid/issues/76
238
+ ULID.min == reversed_min #=> false
239
+ ULID.max == reversed_max #=> false
217
240
  ```
218
241
 
219
242
  ## References
@@ -221,5 +244,8 @@ ULID.from_uuidv4('0983d0a2-ff15-4d83-8f37-7dd945b5aa39')
221
244
  - [Repository](https://github.com/kachick/ruby-ulid)
222
245
  - [API documents](https://kachick.github.io/ruby-ulid/)
223
246
  - [ulid/spec](https://github.com/ulid/spec)
224
- - [Another choices are UUIDv6, UUIDv7, UUIDv8. 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)
225
- - Current parser/validator/matcher implementation aims `strict`, It might be changed in [ulid/spec#57](https://github.com/ulid/spec/pull/57) and [ruby-ulid#57](https://github.com/kachick/ruby-ulid/issues/57).
247
+
248
+ ## Note
249
+
250
+ - 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)
251
+ - 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
@@ -35,7 +35,7 @@ class ULID
35
35
  STRICT_PATTERN = /\A#{PATTERN.source}\z/i.freeze
36
36
 
37
37
  # Imported from https://stackoverflow.com/a/38191104/1212807, thank you!
38
- UUIDV4_PATTERN = /\A[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i
38
+ UUIDV4_PATTERN = /\A[0-9A-F]{8}-[0-9A-F]{4}-[4][0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}\z/i.freeze
39
39
 
40
40
  # Same as Time#inspect since Ruby 2.7, just to keep backward compatibility
41
41
  # @see https://bugs.ruby-lang.org/issues/15958
@@ -51,13 +51,13 @@ class ULID
51
51
  # @param [Integer, Time] moment
52
52
  # @return [ULID]
53
53
  def self.min(moment: 0)
54
- generate(moment: moment, entropy: 0)
54
+ 0.equal?(moment) ? MIN : generate(moment: moment, entropy: 0)
55
55
  end
56
56
 
57
57
  # @param [Integer, Time] moment
58
58
  # @return [ULID]
59
59
  def self.max(moment: MAX_MILLISECONDS)
60
- generate(moment: moment, entropy: MAX_ENTROPY)
60
+ MAX_MILLISECONDS.equal?(moment) ? MAX : generate(moment: moment, entropy: MAX_ENTROPY)
61
61
  end
62
62
 
63
63
  # @deprecated This method actually changes class state. Use {ULID::MonotonicGenerator} instead.
@@ -121,10 +121,11 @@ class ULID
121
121
  new milliseconds: milliseconds, entropy: entropy
122
122
  end
123
123
 
124
- # @param [Range<Time>] time_range
124
+ # @param [Range<Time>, Range<nil>] time_range
125
125
  # @return [Range<ULID>]
126
+ # @raise [ArgumentError] if the given time_range is not a `Range[Time]` or `Range[nil]`
126
127
  def self.range(time_range)
127
- raise ArgumentError, 'ULID.range takes only Range[Time]' unless time_range.kind_of?(Range)
128
+ raise argument_error_for_range_building(time_range) unless time_range.kind_of?(Range)
128
129
  begin_time, end_time, exclude_end = time_range.begin, time_range.end, time_range.exclude_end?
129
130
 
130
131
  case begin_time
@@ -133,7 +134,7 @@ class ULID
133
134
  when nil
134
135
  begin_ulid = min
135
136
  else
136
- raise ArgumentError, 'ULID.range takes only Range[Time]'
137
+ raise argument_error_for_range_building(time_range)
137
138
  end
138
139
 
139
140
  case end_time
@@ -148,9 +149,12 @@ class ULID
148
149
  end_ulid = max
149
150
  exclude_end = false
150
151
  else
151
- raise ArgumentError, 'ULID.range takes only Range[Time]'
152
+ raise argument_error_for_range_building(time_range)
152
153
  end
153
154
 
155
+ begin_ulid.freeze
156
+ end_ulid.freeze
157
+
154
158
  Range.new(begin_ulid, end_ulid, exclude_end)
155
159
  end
156
160
 
@@ -241,6 +245,11 @@ class ULID
241
245
  num
242
246
  end
243
247
 
248
+ # @return [ArgumentError]
249
+ private_class_method def self.argument_error_for_range_building(argument)
250
+ ArgumentError.new "ULID.range takes only `Range[Time]` or `Range[nil]`, given: #{argument.inspect}"
251
+ end
252
+
244
253
  attr_reader :milliseconds, :entropy
245
254
 
246
255
  # @api private
@@ -364,15 +373,21 @@ class ULID
364
373
  @pred ||= self.class.from_integer(pre_int)
365
374
  end
366
375
 
376
+ # @return [String]
377
+ def to_uuidv4
378
+ @uuidv4 ||= begin
379
+ # This code referenced https://github.com/ruby/ruby/blob/121fa24a3451b45c41ac0a661b64e9fc8600e589/lib/securerandom.rb#L221-L241
380
+ array = octets.pack('C*').unpack('NnnnnN')
381
+ array[2] = (array[2] & 0x0fff) | 0x4000
382
+ array[3] = (array[3] & 0x3fff) | 0x8000
383
+ ('%08x-%04x-%04x-%04x-%04x%08x' % array).freeze
384
+ end
385
+ end
386
+
367
387
  # @return [self]
368
388
  def freeze
369
- # Evaluate all caching
370
- inspect
371
- octets
372
- to_i
373
- succ
374
- pred
375
- strict_pattern
389
+ # Need to cache before freezing, because frozen objects can't assign instance variables
390
+ cache_all_instance_variables
376
391
  super
377
392
  end
378
393
 
@@ -382,13 +397,26 @@ class ULID
382
397
  def matchdata
383
398
  @matchdata ||= STRICT_PATTERN.match(to_s).freeze
384
399
  end
400
+
401
+ # @return [void]
402
+ def cache_all_instance_variables
403
+ inspect
404
+ octets
405
+ to_i
406
+ succ
407
+ pred
408
+ strict_pattern
409
+ to_uuidv4
410
+ end
385
411
  end
386
412
 
387
413
  require_relative 'ulid/version'
388
414
  require_relative 'ulid/monotonic_generator'
389
415
 
390
416
  class ULID
417
+ MIN = parse('00000000000000000000000000').freeze
418
+ MAX = parse('7ZZZZZZZZZZZZZZZZZZZZZZZZZ').freeze
391
419
  MONOTONIC_GENERATOR = MonotonicGenerator.new
392
420
 
393
- private_constant :ENCODING_CHARS, :TIME_FORMAT_IN_INSPECT, :UUIDV4_PATTERN
421
+ private_constant :ENCODING_CHARS, :TIME_FORMAT_IN_INSPECT, :UUIDV4_PATTERN, :MIN, :MAX
394
422
  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.14'
5
+ VERSION = '0.0.15'
6
6
  end
data/sig/ulid.rbs CHANGED
@@ -16,8 +16,11 @@ class ULID
16
16
  STRICT_PATTERN: Regexp
17
17
  UUIDV4_PATTERN: Regexp
18
18
  MONOTONIC_GENERATOR: MonotonicGenerator
19
+ MIN: ULID
20
+ MAX: ULID
19
21
  include Comparable
20
22
 
23
+ # The `moment` is a `Time` or `Intger of the milliseconds`
21
24
  type moment = Time | Integer
22
25
 
23
26
  class Error < StandardError
@@ -56,6 +59,7 @@ class ULID
56
59
  @next: ULID?
57
60
  @pattern: Regexp?
58
61
  @strict_pattern: Regexp?
62
+ @uuidv4: String?
59
63
  @matchdata: MatchData?
60
64
 
61
65
  def self.generate: (?moment: moment, ?entropy: Integer) -> ULID
@@ -63,7 +67,7 @@ class ULID
63
67
  def self.current_milliseconds: -> Integer
64
68
  def self.milliseconds_from_time: (Time time) -> Integer
65
69
  def self.milliseconds_from_moment: (moment moment) -> Integer
66
- def self.range: (Range[Time] time_range) -> Range[ULID]
70
+ def self.range: (Range[Time] | Range[nil] time_range) -> Range[ULID]
67
71
  def self.floor: (Time time) -> Time
68
72
  def self.reasonable_entropy: -> Integer
69
73
  def self.parse: (String string) -> ULID
@@ -96,11 +100,14 @@ class ULID
96
100
  def octets: -> octets
97
101
  def timestamp_octets: -> timestamp_octets
98
102
  def randomness_octets: -> randomness_octets
103
+ def to_uuidv4: -> String
99
104
  def next: -> ULID?
100
105
  alias succ next
101
106
  def pred: -> ULID?
102
107
  def freeze: -> self
103
108
 
104
109
  private
110
+ def self.argument_error_for_range_building: (untyped argument) -> ArgumentError
105
111
  def matchdata: -> MatchData
112
+ def cache_all_instance_variables: -> void
106
113
  end
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.0.14
4
+ version: 0.0.15
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-03 00:00:00.000000000 Z
11
+ date: 2021-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: integer-base
@@ -96,7 +96,6 @@ extra_rdoc_files: []
96
96
  files:
97
97
  - LICENSE
98
98
  - README.md
99
- - Steepfile
100
99
  - lib/ulid.rb
101
100
  - lib/ulid/monotonic_generator.rb
102
101
  - lib/ulid/version.rb
@@ -123,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
122
  - !ruby/object:Gem::Version
124
123
  version: '0'
125
124
  requirements: []
126
- rubygems_version: 3.1.4
125
+ rubygems_version: 3.2.15
127
126
  signing_key:
128
127
  specification_version: 4
129
128
  summary: A handy ULID library
data/Steepfile DELETED
@@ -1,7 +0,0 @@
1
- target :lib do
2
- signature 'sig'
3
-
4
- check 'lib'
5
-
6
- library 'securerandom'
7
- end