make_id 0.1.1 → 0.1.3

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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +28 -24
  3. data/lib/make_id/version.rb +1 -1
  4. data/lib/make_id.rb +97 -59
  5. metadata +17 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 30fbadcaf247ed032060da86a3634f1124cc550b6061fd8599dd63f8f07ce473
4
- data.tar.gz: 1fed2e5cd4c0ace07c5457a7b0f3839ea0756d737a842f2876390121cf88374e
3
+ metadata.gz: 7f93b4ee4fa18932ab4bc7769214bee5a4297555d776ab4b38a31a56e3c98fe6
4
+ data.tar.gz: ea62bc4fa6bab34e649d9c3983146b55c81dbe469d16142c9847c00c8c291f49
5
5
  SHA512:
6
- metadata.gz: 234e0c71427fd3ec7f522862c42698afb475702baf6e70bf9e524d98ded74c26f9652b404e4afea82bbb0b0e2cd8daf181c505bb2a44c25ac12d5611418feb43
7
- data.tar.gz: 2f682f097b4d791c956b381c9007c14a4748dc2e3e97c21f5e8a5942f54e1369c5cc361033a41aa88e65f2ce9f1fada51fac98b4f22c1684e36d3fbf6a46bc05
6
+ metadata.gz: 183ea9a2f7e14c6a70d5f0addec62718ce467face83be99128163df22eae71cb5a6cdb813fe413cac2bb0daa80e1bda17267400995455fa11b916ba37bb20662
7
+ data.tar.gz: c2001d8acbc1427e88007146a05a32bbf47e725cce418ac42fd6242d94a23757819afefbaca268558943881012ba3ddcf5b133bc580b2fea44b6f4af092041bd
data/README.md CHANGED
@@ -61,8 +61,14 @@ MakeId can return a random (8-byte by default) integer. You can request it retur
61
61
  and with an optional check_digit.
62
62
  Usually, you would use the integer returned, and call `int_to_base` to format for a URL or code.
63
63
 
64
- MakeId.random_id() #=> 15379918763975837985ZZ
65
- MakeId.random_id(base: 62, check_digit: true) #=> "2984biEwRT1"
64
+ MakeId.id() #=> 15379918763975837985ZZ
65
+ MakeId.id(base: 62, check_digit: true) #=> "2984biEwRT1"
66
+
67
+ Nano Id's are shorter unique strings generated from random characters, usually as a friendlier alternative
68
+ to UUID's. These are 8-byte numeric identifiers in extended bases, such as 36 or 62.
69
+
70
+ MakeId.nano_id() #=> "iZnLn96FVcjivEJA" (Base-62 be default)
71
+ MakeId.nano_id(base: 36) #=> "sf8kqb8ekn7k98rq"
66
72
 
67
73
  ### UUID
68
74
 
@@ -76,16 +82,28 @@ can be used to transform a long UUID into a possibly more palettable base repres
76
82
  MakeId.uuid_to_base(u, 62) #=> "fWJtuXEQJnkjxroWjkmei" (21 characters)
77
83
 
78
84
  Note that some databases support a UUID type which makes storing UUID's easier, and since they are stored as a binary
79
- field, consume less space.ZZ
85
+ field, consume less space.
80
86
 
81
- ### Nano Id
87
+ ### Tokens
82
88
 
83
- Nano Id's are shorter unique strings generated from random characters, usually as a friendlier alternative
84
- to UUID's. They also can be of any size, depending on the key range you require. Pay attention to the keyspace,
85
- ensuring you have enough characters to avoid predictable collisions in the future.
89
+ Tokens are randomly-generated strings of the character set of the requested base.
90
+ The default size is 16 characters of the Base-62 character set.
91
+
92
+ MakeId.token() #=> "Na4VX61PBFVZWL6Y"
93
+ MakeId.token(8, base:36) #=> "BK0ZTL9H"
94
+
95
+ ### Codes
86
96
 
87
- MakeId.nano_id(size: 16) #=> "iZnLn96FVcjivEJA"
88
- MakeId.nano_id(size: 16, base: 32) #=> "sf8kqb8ekn7k98rq"
97
+ Codes are string tokens to send to users for input. They have no ambiguous characters to avoid confusion.
98
+ This is useful for verifications such as two-factor authentication codes, or license numbers.
99
+ This returns an 8-character string by default. Specify a group (size) and delimiter (default is a hyphen)
100
+ to make long codes readable.
101
+
102
+ MakeId.code #=> "22E0D18F"
103
+ MakeId.code(20) #=> "Y41Q24AG7DYZYTAZWQZX"
104
+ MakeId.code(20, group: 4, delimiter: "-") #=> "9975-V5VM-KKSR-4PQ6-7F4G"
105
+
106
+ ### Temporal Identifiers
89
107
 
90
108
  A `request_id` is a nano_id that can be used to track requests and jobs. It is a 16-byte string, the same
91
109
  storage as a UUID, but with columnar values. The substring of 3 for 8 is a short (8 character) version that
@@ -96,9 +114,7 @@ can be used as well, is easier to read, sortable within a day, and unique enough
96
114
  id[3,8] #=> "f1272t01"
97
115
  #-------------------------->Hsssuuqq
98
116
 
99
- ### Snowflake Id
100
-
101
- Snowflakes were invented at Twitter to stamp an identifier for a tweet or direct message.
117
+ Snowflake Id's were invented at Twitter to stamp an identifier for a tweet or direct message.
102
118
  It is an 8-byte integer intended to be time-sorted and unique across the fleet of servers saving messages.
103
119
  It is a bit-mapped integer consisting of these parts:
104
120
 
@@ -137,18 +153,6 @@ records or when you need a slowflake ID but have a UUID column to fill.
137
153
  MakeID.snowflake_datetime_uuid #=> "20240904-1418-5332-2000-3a38e61d5582"
138
154
  #------------------------>YYYYMMDD-hhmm-ssuu-uwww-rrrrrrrrrrrr
139
155
 
140
- ## Experimental Id's
141
-
142
- The `event_id` is a string, sortable by creation time, with visible time seperator columns.
143
- It is of the format "YMDhmsuurrrr", using Base62, with an optional check_sum characer.
144
- It also used the application epoch described under `snowflake_id`. "uu" represents the fractional
145
- seconds that can be represented in Base62, and a 4-character random Base64 "nano_id".
146
-
147
- MakeId.epoch = 2020
148
- MakeId.event_id #=> "493KgpQGErTB"
149
- #------------------->YMDhmsuurrrr ()
150
- MakeId.event_id(check_digit: true) #=> "493Kkha6HZa2" (3 random chars + check digit)
151
-
152
156
  ## Development
153
157
 
154
158
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MakeId
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/make_id.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require_relative "make_id/version"
4
4
  require "securerandom"
5
5
  require "zlib"
6
+ require "digest"
6
7
 
7
8
  # MakeID generates record Identifiers other than sequential integers.
8
9
  # MakeId - From the "make_id" gem found at https://github.com/afair/make_id
@@ -73,24 +74,11 @@ module MakeId
73
74
  end
74
75
 
75
76
  ##############################################################################
76
- # Random Strings
77
- ##############################################################################
78
-
79
- # Returns a random alphanumeric string of the given base, default of 62.
80
- # Base 64 uses URL-safe characters. Bases 19-32 and below use a special
81
- # character set that avoids visually ambiguous characters. Other bases
82
- # utilize the full alphanumeric characer set (digits, lower/upper letters).
83
- def self.random(size = 16, base: 62, chars: nil)
84
- _, chars = base_characters(base, chars)
85
- SecureRandom.alphanumeric(size, chars: chars.chars)
86
- end
87
-
88
- ##############################################################################
89
- # Integers
77
+ # Numeric Identifiers (of any suported base)
90
78
  ##############################################################################
91
79
 
92
80
  # Random Integer ID
93
- def self.random_id(bytes: 8, base: 10, absolute: true, check_digit: false)
81
+ def self.id(bytes: 8, base: 10, absolute: true, check_digit: false)
94
82
  id = SecureRandom.random_number(2**(bytes * 8) - 2) + 1 # +1 to avoid zero
95
83
  id = id.abs if absolute
96
84
  id = int_to_base(id, base) unless base == 10
@@ -98,17 +86,41 @@ module MakeId
98
86
  id
99
87
  end
100
88
 
101
- def self.random_id_password(bytes: 8, base: 10, absolute: true, alpha: nil)
102
- id = random_id(bytes: bytes)
103
- pass = random_id(bytes: 16)
89
+ def self.id_password(bytes: 8, base: 10, absolute: true, alpha: nil)
90
+ id = id(bytes: bytes)
91
+ pass = id(bytes: 16)
104
92
  [int_to_base(id, base), encode_alphabet(pass, alpha || BASE94, seed: id)]
105
93
  end
106
94
 
95
+ # Generates a 8-byte "nano id", a string of random characters of the given alphabet,
96
+ # suitable for URL's or where you don't want to show a sequential number.
97
+ # A check digit can be added to the end to help prevent typos.
98
+ def self.nano_id(bytes: 8, base: 62, check_digit: true)
99
+ bytes -= 1 if check_digit
100
+ id = id(bytes: bytes, base: base)
101
+ check_digit ? append_check_digit(id, base) : id
102
+ end
103
+
104
+ # Takes a string and returns an 8-byte integer for the string.
105
+ # This implementation uses the SHA256 hash of the string to generate the ID.
106
+ # You can also have random bits added to the ID to make it unique.
107
+ # This is not a unique ID, but a repeatable ID for the same string
108
+ # (unless random_bits is used), and can be used like a hashing value.
109
+ def self.string_id(string, random_bits: 0, base: 10, bytes: 8)
110
+ id = Digest::SHA256.hexdigest(string)[0, bytes * 2].to_i(16)
111
+ if random_bits > 0
112
+ id >> random_bits
113
+ id << random_bits
114
+ id |= SecureRandom.random_number(2**random_bits - 1)
115
+ end
116
+ (base == 10) ? id : int_to_base(id, base)
117
+ end
118
+
107
119
  ##############################################################################
108
120
  # UUID - Universally Unique Identifier
109
121
  ##############################################################################
110
122
 
111
- # Returns a (securely) random generated UUID v4
123
+ # Returns a 16-byte securely-random generated UUID v4
112
124
  def self.uuid
113
125
  SecureRandom.uuid
114
126
  end
@@ -121,49 +133,40 @@ module MakeId
121
133
  end
122
134
 
123
135
  ##############################################################################
124
- # Nano Id - Simple, secure URL-friendly unique string ID generator
136
+ # Strings Identifiers
125
137
  ##############################################################################
126
138
 
127
- # Generates a "nano id", a string of random characters of the given alphabet,
128
- # suitable for URL's or where you don't want to show a sequential number.
129
- # A check digit is added to the end to help prevent typos.
130
- def self.nano_id(size: 20, base: 62, check_digit: true)
131
- # alpha = (base <= 32) ? BASE32 : BASE62
132
- size -= 1 if check_digit
133
- id = random(size, base: base)
134
- check_digit ? append_check_digit(id, base) : id
139
+ # Returns a random alphanumeric string of the given base, default of 62.
140
+ # Base 64 uses URL-safe characters. Bases 19-32 and below use a special
141
+ # character set that avoids visually ambiguous characters. Other bases
142
+ # utilize the full alphanumeric characer set (digits, lower/upper letters).
143
+ def self.token(size = 16, base: 62, chars: nil)
144
+ _, chars = base_characters(base, chars)
145
+ SecureRandom.alphanumeric(size, chars: chars.chars)
146
+ end
147
+
148
+ # Returns a new, ramdonly-generated Base-32 code (no ambiguous characters).
149
+ # Use this for Two-Factor Authorization, and serial number codes to be
150
+ # input by users. Use verify_code() to "fix" user-input of codes.
151
+ def self.code(size = 8, group: 0, delimiter: "-")
152
+ id = token(size, base: 32)
153
+ id = id.chars.each_slice(group).map(&:join).join(delimiter) if group > 0
154
+ id
135
155
  end
136
156
 
137
157
  # Given a nano_id, replaces visually ambiguous characters and verifies the
138
158
  # check digit. Returns the corrected id or nil if the check digit is invalid.
139
- def self.verify_base32_id(nanoid)
140
- nanoid.gsub!(/[oO]/, "0")
141
- nanoid.gsub!(/[lLiI]/, "1")
142
- nanoid.downcase
143
- valid_check_digit?(nanoid, base: 32)
144
- end
145
-
146
- # Manual Id is a code and/or identifier that is manually entered by a user.
147
- # Examples of this would be a Two-Factor Authentication challenge, a code
148
- # used for confirmation, redemption, or a short-term record lookup code
149
- # (like an airline ticket/itenerary code)
150
- # It uses a base-32 (non-ambiguous character set) by default,
151
- def self.manual_id(size: 6, base: 32, check_digit: false)
152
- base = 32 if base > 36 # For upcasing
153
- nano_id(size: size, base: base, check_digit: check_digit).upcase
154
- end
155
-
156
- def self.fix_manual_id(id, base: 32, check_digit: false)
157
- if base == 32
158
- id = id.gsub(/[oO]/, "0")
159
- id = id.gsub(/[lLiI]/, "1")
160
- end
161
- id = valid_check_digit?(id.downcase, base: 32) if check_digit
162
- id.upcase
159
+ def self.verify_code(nanoid, check_digit: false)
160
+ nanoid = nanoid.gsub(/\W/, "")
161
+ nanoid = nanoid.gsub(/[oO]/, "0")
162
+ nanoid = nanoid.gsub(/[lLiI]/, "1")
163
+ nanoid = nanoid.upcase
164
+ return valid_check_digit?(nanoid, base: 32) if check_digit
165
+ nanoid
163
166
  end
164
167
 
165
168
  ##############################################################################
166
- # TEMPORAL ID's
169
+ # TEMPORAL Identifiers
167
170
  ##############################################################################
168
171
 
169
172
  # Event Id - A nano_id, but timestamped event identifier: YMDHMSUUrrrrc
@@ -180,7 +183,7 @@ module MakeId
180
183
  usec.rjust(2, "0") # 2-chars, 0..3843
181
184
  ]
182
185
  nano_size = size - 8 - (check_digit ? 1 : 0)
183
- parts << nano_id(size: nano_size, base: 62) if nano_size > 0
186
+ parts << token(nano_size, base: 62) if nano_size > 0
184
187
  id = check_digit ? append_check_digit(parts.join, 62) : parts.join
185
188
  id[0, size]
186
189
  end
@@ -206,10 +209,40 @@ module MakeId
206
209
  int_to_base((time.subsec.to_f * 32 * 32).to_i, 32), # 2 chars
207
210
  sequence.to_s(32).rjust(2, "0"), # 2 chars "-",
208
211
  (app_worker_id % 1024).to_s(32).rjust(2, "0"), # 2 chars
209
- random(3, base: 32)
212
+ token(3, base: 32)
210
213
  ].join
211
214
  end
212
215
 
216
+ # Returns an id using the current epoch time with floating precision and random bits.
217
+ # Be default, this returns an 8-byte integer in ascending order within precision.
218
+ # Format is approximately "SSSSSSSSSMMMRRR" (seconds, milliseconds, random).
219
+ # * precision (1..5) The number of milliseconds decimeal places to use. Default is 4.
220
+ # * random_bits (1..) The number of bits to use for the random number. Default is 18.
221
+ # * time - Time object to use for the token. Default is Time.now
222
+ # * base - The base to use for the id. Default is 10
223
+ # * chars - The character set to use for the base conversion. Default is BASE62
224
+ def self.time_id(base: 10, precision: 4, random_bits: 18, time: nil, chars: nil)
225
+ seconds = ((time || Time.now).to_f * (10**precision)).to_i
226
+ id = (seconds * (2**random_bits)) | SecureRandom.random_number(2**random_bits - 1)
227
+ (base == 10) ? id : int_to_base(id, base, chars: chars)
228
+ end
229
+
230
+ # Returns a string from the time with floating precision, and a random number appended.
231
+ # This calls time_id() and returns a token of the given size.
232
+ # If size is zero, the full token is returned. If size is greater than the token,
233
+ # the token is right-justified with zeros. If the token is larger than size, the
234
+ # right-most characters are returned.
235
+ def self.time_token(size: 0, base: 62, chars: nil, time: nil, precision: 3, random_bits: 8)
236
+ token = time_id(base: base, precision: precision, random_bits: random_bits, time: time, chars: chars)
237
+ if size == 0
238
+ token
239
+ elsif token.size < size
240
+ token.rjust(size, "0")
241
+ else
242
+ token[-size, size]
243
+ end
244
+ end
245
+
213
246
  ##############################################################################
214
247
  # Snowflake Id - Epoch + millisecond + worker_id id + sequence number
215
248
  # Snowflakes are a form of unique identifier used in distributed computing.
@@ -323,7 +356,7 @@ module MakeId
323
356
 
324
357
  # Parses a string as a base n number and returns its decimal integer value
325
358
  def self.base_to_int(string, base = 62, check_digit: false)
326
- # TODO check_digit
359
+ # TODO: check_digit
327
360
  _, chars = base_characters(base, chars)
328
361
  decode_alphabet(string, chars)
329
362
  end
@@ -367,9 +400,7 @@ module MakeId
367
400
  else
368
401
  chars = BASE62[0..(base - 1)]
369
402
  end
370
- if shuffle_seed
371
- chars = chars.chars.shuffle(random: Random.new(shuffle_seed)).join
372
- end
403
+ chars = chars.chars.shuffle(random: Random.new(shuffle_seed)).join if shuffle_seed
373
404
  base = chars.size
374
405
 
375
406
  [base, chars]
@@ -385,10 +416,17 @@ module MakeId
385
416
  id.to_s + compute_check_digit(id, base)
386
417
  end
387
418
 
419
+ # Removes and validates the check digit. Retuns nil if the check digit is invalid.
420
+ def self.remove_check_digit(id, base = 10)
421
+ id, cd = id.to_s[0..-2], id.to_s[-1]
422
+ valid_check_digit?(id + cd, base) ? id : nil
423
+ end
424
+
388
425
  # Returns a character computed using the CRC32 algorithm
389
426
  # Uses a pre-defined check_proc if configured. See check_proc=().
390
427
  def self.compute_check_digit(id, base = 10)
391
428
  return @@check_proc.call(id, base) if @@check_proc.is_a?(Proc)
429
+
392
430
  int_to_base(Zlib.crc32(id.to_s) % base, base)
393
431
  end
394
432
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: make_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Allen Fair
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-11 00:00:00.000000000 Z
10
+ date: 2025-03-18 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: base64
@@ -24,6 +23,20 @@ dependencies:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
25
  version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: irb
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
27
40
  description: MakeId is a collection of record Identifier generators
28
41
  email:
29
42
  - allen.fair@gmail.com
@@ -49,7 +62,6 @@ licenses:
49
62
  metadata:
50
63
  homepage_uri: https://github.com/afair/make_id
51
64
  source_code_uri: https://github.com/afair/make_id
52
- post_install_message:
53
65
  rdoc_options: []
54
66
  require_paths:
55
67
  - lib
@@ -64,8 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
64
76
  - !ruby/object:Gem::Version
65
77
  version: '0'
66
78
  requirements: []
67
- rubygems_version: 3.5.20
68
- signing_key:
79
+ rubygems_version: 3.6.2
69
80
  specification_version: 4
70
81
  summary: MakeId provides a collection of record Identifier generators
71
82
  test_files: []