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 +4 -4
- data/README.md +10 -9
- data/lib/make_id/version.rb +1 -1
- data/lib/make_id.rb +91 -54
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 30fbadcaf247ed032060da86a3634f1124cc550b6061fd8599dd63f8f07ce473
|
4
|
+
data.tar.gz: 1fed2e5cd4c0ace07c5457a7b0f3839ea0756d737a842f2876390121cf88374e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
-
|
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
|
50
|
-
|
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) #=> "
|
53
|
-
MakeId.from_base("
|
54
|
-
MakeId.int_to_base(123456789, 32) #=> "
|
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) #=> "
|
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
|
|
data/lib/make_id/version.rb
CHANGED
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
|
-
|
13
|
+
class Error < StandardError; end
|
15
14
|
|
16
|
-
|
17
|
-
|
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
|
-
|
69
|
-
|
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) ?
|
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
|
-
#
|
166
|
+
# TEMPORAL ID's
|
152
167
|
##############################################################################
|
153
168
|
|
154
|
-
#
|
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
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
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
|
-
|
303
|
-
|
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
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
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
|
-
|
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.
|
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-
|
11
|
+
date: 2024-11-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: base64
|