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