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.
- checksums.yaml +4 -4
- data/README.md +28 -24
- data/lib/make_id/version.rb +1 -1
- data/lib/make_id.rb +97 -59
- metadata +17 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f93b4ee4fa18932ab4bc7769214bee5a4297555d776ab4b38a31a56e3c98fe6
|
4
|
+
data.tar.gz: ea62bc4fa6bab34e649d9c3983146b55c81dbe469d16142c9847c00c8c291f49
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
65
|
-
MakeId.
|
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.
|
85
|
+
field, consume less space.
|
80
86
|
|
81
|
-
###
|
87
|
+
### Tokens
|
82
88
|
|
83
|
-
|
84
|
-
|
85
|
-
|
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
|
-
|
88
|
-
|
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
|
-
|
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.
|
data/lib/make_id/version.rb
CHANGED
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
|
-
#
|
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.
|
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.
|
102
|
-
id =
|
103
|
-
pass =
|
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
|
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
|
-
#
|
136
|
+
# Strings Identifiers
|
125
137
|
##############################################################################
|
126
138
|
|
127
|
-
#
|
128
|
-
#
|
129
|
-
#
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
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.
|
140
|
-
nanoid.gsub
|
141
|
-
nanoid.gsub
|
142
|
-
nanoid.
|
143
|
-
|
144
|
-
|
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
|
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 <<
|
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
|
-
|
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.
|
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:
|
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.
|
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: []
|