make_id 0.1.0 → 0.1.1

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: 90cbe0cbac923318d4badd6c848042404633a27c6fbecdd7e2509aee6c31ab5c
4
- data.tar.gz: 41f946d9367bb73257ac31ffce792bd309f2e707c44b39003d66dcc3d1890a49
3
+ metadata.gz: 30fbadcaf247ed032060da86a3634f1124cc550b6061fd8599dd63f8f07ce473
4
+ data.tar.gz: 1fed2e5cd4c0ace07c5457a7b0f3839ea0756d737a842f2876390121cf88374e
5
5
  SHA512:
6
- metadata.gz: da8822e1194eb4ed1e51f1090f120db17cdd421fc330f84dbe4b4556c1dd71eac4dc33dff678768999cee03ad21749a9f3bbb5a2949ced4f83c7f7afd802128a
7
- data.tar.gz: '038ae0b3bc50252cd75eec5fb7283feb27381e3362bd2681fbba6ff225865727ef4fa560f84d24e8c4703f5ba96ce168c9278462b9469b97323ab981869e387d'
6
+ metadata.gz: 234e0c71427fd3ec7f522862c42698afb475702baf6e70bf9e524d98ded74c26f9652b404e4afea82bbb0b0e2cd8daf181c505bb2a44c25ac12d5611418feb43
7
+ data.tar.gz: 2f682f097b4d791c956b381c9007c14a4748dc2e3e97c21f5e8a5942f54e1369c5cc361033a41aa88e65f2ce9f1fada51fac98b4f22c1684e36d3fbf6a46bc05
data/README.md CHANGED
@@ -40,18 +40,19 @@ numeric codes.
40
40
 
41
41
  Bases supported are:
42
42
 
43
- - Base62: digits, upper, and lower-case letters. No special characters
43
+ - Base94: Base64 (Upper, Lower, Digits) with 30 extra special characters
44
+ - Base64: Url-Safe version. Base64 but swaps the plus and slash by dash and underscore respectively.
45
+ - Base62: digits, upper, and lower-case letters. No special characters. The default.
44
46
  - Base32: digits and upper case without ambiguous characters "1lI" or "oO0"
45
47
  - Base 2 through 36 (except 32): Ruby's `Integer#to_s(base)` is used
46
- - Base64: Uses the `Base64.urlsafe_encode64` such has 2 special characters.
47
- - Base63: It is not implemented.
48
48
 
49
- The Base32 may seem out of place, but is useful for alpha-numeric codes the users are required to type, such as redemption codes.
50
- All letter are folded to upper-case, and ambiguous characters are converted to the canonical ones.
49
+ The Base32 may seem out of place, but is useful for alpha-numeric codes the users are required to type or speak,
50
+ such as serial numbers or license codes.
51
+ All letters are upper-case, and ambiguous characters are converted to the canonical ones.
51
52
 
52
- MakeId.int_to_base(123456789, 32) #=> "3nqk8n"
53
- MakeId.from_base("3nqk8n", 10) #=> 123456789
54
- MakeId.int_to_base(123456789, 32) #=> "3nqk8n"
53
+ MakeId.int_to_base(123456789, 32) #=> "3NQK8N"
54
+ MakeId.from_base("3NQK8N", 10) #=> 123456789
55
+ MakeId.int_to_base(123456789, 32) #=> "3NQK8N"
55
56
  MakeId.verify_base32_id("...") #=> corrected_id or nil if error
56
57
 
57
58
  ### Random Integer
@@ -120,7 +121,7 @@ You can also pass in options to return it as a different base, and with a check
120
121
 
121
122
  MakeId.app_worker_id = 234
122
123
  MakeId.snowflake_id => 618905333721374720
123
- MakeId.snowflake_id(worker_id: 12, base: 32, sequence_method: :random) #=> "2tmxk6ne81jd5"
124
+ MakeId.snowflake_id(worker_id: 12, base: 32, sequence_method: :random) #=> "2TMXK6NE81JD5"
124
125
 
125
126
  The `snowflake_uuid` method provides a time-based identifier, great for sorting just as sequential numbers, but unique enough to fit the bill.
126
127
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MakeId
4
- VERSION = "0.1.0"
4
+ VERSION = "0.1.1"
5
5
  end
data/lib/make_id.rb CHANGED
@@ -2,7 +2,6 @@
2
2
 
3
3
  require_relative "make_id/version"
4
4
  require "securerandom"
5
- require "base64"
6
5
  require "zlib"
7
6
 
8
7
  # MakeID generates record Identifiers other than sequential integers.
@@ -11,10 +10,27 @@ require "zlib"
11
10
  # Adopt - Copy this file to your application with the above attribution to
12
11
  # allow others to find fixes, documentation, and new features.
13
12
  module MakeId
14
- # class Error < StandardError; end
13
+ class Error < StandardError; end
15
14
 
16
- CHARS32 = "0123456789abcdefghjkmnpqrstvwxyz" # Avoiding ambiguous 0/o i/l/I
17
- CHARS62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
15
+ # Base32 avoids ambiguous letters for 0/o/O and i/I/l/1. This is useful
16
+ # for human-interpreted codes for serial numbers, license keys, etc.
17
+ BASE32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
18
+
19
+ # Ruby's Integer.to_s(2..36) uses extended Hexadecimal: 0-9,a-z.
20
+ # Base62 includes upper-case letters as well, maintaining ASCII cardinality.
21
+ BASE62 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
22
+
23
+ # Base64 Does not use ASCII-collating (sort) character set
24
+ BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
25
+
26
+ # Base94 extends Base64 with all printable ASCII special characters.
27
+ # Using Base of 90 won't use quotes, backslash
28
+ BASE94 = BASE64 + %q(!$%&()*,-.:;<=>?@[]^_{|}~#`'"\\)
29
+
30
+ # URL-Encoded Base64 swaps the + and / for - and _ respectively to avoid URL Encoding
31
+ URL_BASE64 = BASE64.tr("+/", "-_")
32
+
33
+ # TWitter Snowflake starts its epoch at this time.
18
34
  EPOCH_TWITTER = Time.utc(2006, 3, 21, 20, 50, 14)
19
35
 
20
36
  @@app_worker_id = ENV.fetch("APP_WORKER_ID", 0)
@@ -64,16 +80,9 @@ module MakeId
64
80
  # Base 64 uses URL-safe characters. Bases 19-32 and below use a special
65
81
  # character set that avoids visually ambiguous characters. Other bases
66
82
  # utilize the full alphanumeric characer set (digits, lower/upper letters).
67
- def self.random(size = 16, base: 62)
68
- raise "Base must be between 2 and 62, or 64, not #{base}" unless base < 63 || base == 64
69
- if base == 62
70
- SecureRandom.alphanumeric(size)
71
- elsif base == 64
72
- SecureRandom.urlsafe_base64(size)
73
- else
74
- alpha = (base <= 32) ? CHARS32 : CHARS62
75
- (1..size).map { alpha[SecureRandom.rand(base - 1)] }.join
76
- end
83
+ def self.random(size = 16, base: 62, chars: nil)
84
+ _, chars = base_characters(base, chars)
85
+ SecureRandom.alphanumeric(size, chars: chars.chars)
77
86
  end
78
87
 
79
88
  ##############################################################################
@@ -89,6 +98,12 @@ module MakeId
89
98
  id
90
99
  end
91
100
 
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)
104
+ [int_to_base(id, base), encode_alphabet(pass, alpha || BASE94, seed: id)]
105
+ end
106
+
92
107
  ##############################################################################
93
108
  # UUID - Universally Unique Identifier
94
109
  ##############################################################################
@@ -113,7 +128,7 @@ module MakeId
113
128
  # suitable for URL's or where you don't want to show a sequential number.
114
129
  # A check digit is added to the end to help prevent typos.
115
130
  def self.nano_id(size: 20, base: 62, check_digit: true)
116
- # alpha = (base <= 32) ? CHARS32 : CHARS62
131
+ # alpha = (base <= 32) ? BASE32 : BASE62
117
132
  size -= 1 if check_digit
118
133
  id = random(size, base: base)
119
134
  check_digit ? append_check_digit(id, base) : id
@@ -148,20 +163,20 @@ module MakeId
148
163
  end
149
164
 
150
165
  ##############################################################################
151
- # Event Id - A nano_id, but timestamped event identifier: YMDHMSUUrrrrc
166
+ # TEMPORAL ID's
152
167
  ##############################################################################
153
168
 
154
- # Returns an event timestamp of the form YMDHMSUUrrrrc
169
+ # Event Id - A nano_id, but timestamped event identifier: YMDHMSUUrrrrc
155
170
  def self.event_id(size: 12, check_digit: false, time: nil)
156
171
  time ||= Time.new.utc
157
172
  usec = int_to_base((time.subsec.to_f * 62 * 62).to_i, 62)
158
173
  parts = [
159
- CHARS62[time.year % @@epoch.year],
160
- CHARS62[time.month],
161
- CHARS62[time.day],
162
- CHARS62[time.hour],
163
- CHARS62[time.min],
164
- CHARS62[time.sec],
174
+ BASE62[time.year % @@epoch.year],
175
+ BASE62[time.month],
176
+ BASE62[time.day],
177
+ BASE62[time.hour],
178
+ BASE62[time.min],
179
+ BASE62[time.sec],
165
180
  usec.rjust(2, "0") # 2-chars, 0..3843
166
181
  ]
167
182
  nano_size = size - 8 - (check_digit ? 1 : 0)
@@ -183,10 +198,10 @@ module MakeId
183
198
  end
184
199
 
185
200
  [
186
- CHARS62[time.year % @@epoch.year],
187
- CHARS62[time.month],
188
- CHARS62[time.day], # "-",
189
- CHARS62[time.hour].downcase,
201
+ BASE62[time.year % @@epoch.year],
202
+ BASE62[time.month],
203
+ BASE62[time.day], # "-",
204
+ BASE62[time.hour].downcase,
190
205
  int_to_base(seconds, 32).rjust(3, "0"), # 3 chars
191
206
  int_to_base((time.subsec.to_f * 32 * 32).to_i, 32), # 2 chars
192
207
  sequence.to_s(32).rjust(2, "0"), # 2 chars "-",
@@ -298,23 +313,9 @@ module MakeId
298
313
  # Ruby's int.to_s(base) only goes to 36. Base 32 is special as it does not
299
314
  # contain visually ambiguous characters (1, not i, I, l, L) and (0, not o or O)
300
315
  # Which is useful for serial numbers or codes the user has to read or type
301
- def self.int_to_base(int, base = 62, check_digit: false)
302
- int = int.to_i
303
- if base == 10
304
- id = int.to_s
305
- elsif base == 64
306
- id = Base64.urlsafe_encode64(int.to_s).delete("=")
307
- elsif base == 32 || base > 36
308
- alpha = (base <= 32) ? CHARS32 : CHARS62
309
- id = ""
310
- while int > (base - 1)
311
- id = alpha[int % base] + id
312
- int /= base
313
- end
314
- id = alpha[int] + id
315
- else
316
- id = int.to_s(base)
317
- end
316
+ def self.int_to_base(int, base = 62, check_digit: false, chars: nil)
317
+ base, chars = base_characters(base, chars)
318
+ id = encode_alphabet(int, chars)
318
319
  check_digit ? append_check_digit(id, base) : id
319
320
  end
320
321
 
@@ -323,20 +324,56 @@ module MakeId
323
324
  # Parses a string as a base n number and returns its decimal integer value
324
325
  def self.base_to_int(string, base = 62, check_digit: false)
325
326
  # TODO check_digit
326
- if base == 64
327
- int = Base64.urlsafe_decode64(string.to_s + "==")
328
- elsif base == 32 || base > 36
329
- alpha = (base <= 32) ? CHARS32 : CHARS62
330
- string = string.to_s
331
- int = 0
332
- string.each_char { |c| int = int * base + alpha.index(c) }
333
- else
334
- int = string.to_i(base)
327
+ _, chars = base_characters(base, chars)
328
+ decode_alphabet(string, chars)
329
+ end
330
+
331
+ singleton_class.alias_method :from_base, :base_to_int
332
+
333
+ def self.encode_alphabet(int, alpha = BASE62, seed: nil)
334
+ base = alpha.size
335
+ alpha = alpha.chars.shuffle(random: Random.new(seed)).join if seed
336
+ id = ""
337
+ while int > (base - 1)
338
+ id = alpha[int % base] + id
339
+ int /= base
335
340
  end
341
+ alpha[int] + id
342
+ end
343
+
344
+ def self.decode_alphabet(string, alpha = BASE32, seed: nil, base: nil)
345
+ base ||= alpha.size
346
+ alpha = alpha.chars.shuffle(random: Random.new(seed)).join if seed
347
+ int = 0
348
+ string.each_char { |c| int = int * base + alpha.index(c) }
336
349
  int
350
+ rescue
351
+ nil
337
352
  end
338
353
 
339
- singleton_class.alias_method :from_base, :base_to_int
354
+ # Returns the refined base and characters used for the base conversions
355
+ def self.base_characters(base, chars = nil, shuffle_seed: nil)
356
+ if chars
357
+ base ||= chars.size
358
+ chars = chars[0..(base - 1)]
359
+ elsif base > 94 || base < 2
360
+ raise Error.new("Base#{base} is not supported")
361
+ elsif base > 64
362
+ chars = BASE94[0..(base - 1)]
363
+ elsif base > 62
364
+ chars = URL_BASE64[0..(base - 1)]
365
+ elsif base == 32
366
+ chars = BASE32
367
+ else
368
+ chars = BASE62[0..(base - 1)]
369
+ end
370
+ if shuffle_seed
371
+ chars = chars.chars.shuffle(random: Random.new(shuffle_seed)).join
372
+ end
373
+ base = chars.size
374
+
375
+ [base, chars]
376
+ end
340
377
 
341
378
  ##############################################################################
342
379
  # Check Digit
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: make_id
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Allen Fair
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-13 00:00:00.000000000 Z
11
+ date: 2024-11-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64