make_id 0.1.0 → 0.1.1

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