lite-uxid 1.5.1 → 2.0.0
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/.rubocop.yml +2 -0
- data/CHANGELOG.md +12 -0
- data/Gemfile.lock +1 -1
- data/README.md +34 -12
- data/lib/generators/lite/uxid/templates/install.rb +1 -0
- data/lib/lite/uxid/base.rb +58 -0
- data/lib/lite/uxid/configuration.rb +10 -4
- data/lib/lite/uxid/irreversible/base.rb +33 -0
- data/lib/lite/uxid/irreversible/nanoid.rb +44 -0
- data/lib/lite/uxid/irreversible/ulid.rb +47 -0
- data/lib/lite/uxid/irreversible/uuid.rb +20 -0
- data/lib/lite/uxid/record/hashid.rb +13 -12
- data/lib/lite/uxid/record/nanoid.rb +6 -8
- data/lib/lite/uxid/record/scatterid.rb +58 -0
- data/lib/lite/uxid/record/ulid.rb +6 -8
- data/lib/lite/uxid/record/uuid.rb +6 -8
- data/lib/lite/uxid/reversible/base.rb +30 -0
- data/lib/lite/uxid/reversible/hashid.rb +52 -0
- data/lib/lite/uxid/reversible/scatterid.rb +71 -0
- data/lib/lite/uxid/version.rb +1 -1
- data/lib/lite/uxid.rb +10 -6
- metadata +11 -8
- data/lib/lite/uxid/base/irreversible.rb +0 -82
- data/lib/lite/uxid/base/reversible.rb +0 -36
- data/lib/lite/uxid/hashid.rb +0 -50
- data/lib/lite/uxid/nanoid.rb +0 -17
- data/lib/lite/uxid/ulid.rb +0 -45
- data/lib/lite/uxid/uuid.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 767645737040b210cc058a0a781f89751f447e991aa03c527f5d12b039d1980a
|
4
|
+
data.tar.gz: 1c777db2675144f26b24ed2ec75d175c83f55d5ede9e83ab5d0d75ca2b76e692
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 313da972f69d52e8951d552c3269500767e7fb80255de25c44d1bbe0d6497aaa3b0c398736b2e451d393ad0642446e298ebbad36a225528f010a96a8684f221e
|
7
|
+
data.tar.gz: 61ce05436d5e2793164007f1b28edd28ea138c9efbf02f0a6ac6ef021708d3171b61c77e2811dfe9c65ef764b46d55713e6121eefd57d87a22cb10e80a9c0eef
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [2.0.0] - 2024-09-23
|
10
|
+
### Added
|
11
|
+
- Scatterid reversible lib
|
12
|
+
### Changed
|
13
|
+
- Reorganize generators into better namespaces
|
14
|
+
- Fixed issue where nanoid would return without the prefix
|
15
|
+
- Moved charsets to constants
|
16
|
+
|
17
|
+
## [1.5.2] - 2024-09-20
|
18
|
+
### Changed
|
19
|
+
- Fixed nanoid of differing length error
|
20
|
+
|
9
21
|
## [1.5.1] - 2024-09-20
|
10
22
|
### Changed
|
11
23
|
- Use after commit on create instead of after create
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -29,6 +29,7 @@ Or install it yourself as:
|
|
29
29
|
* [Usage](#usage)
|
30
30
|
* [Hashid](#hashid)
|
31
31
|
* [NanoID](#nanoid)
|
32
|
+
* [Scatterid](#scatterid)
|
32
33
|
* [ULID](#ulid)
|
33
34
|
* [UUID](#uuid)
|
34
35
|
* [Options](#options)
|
@@ -41,13 +42,17 @@ Or install it yourself as:
|
|
41
42
|
`config/initalizers/lite_uxid.rb`
|
42
43
|
|
43
44
|
```ruby
|
45
|
+
Lite::Uxid::ALPHANUMERIC = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
46
|
+
Lite::Uxid::COCKFORDS_32 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
47
|
+
Lite::Uxid::WEB_SAFE = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_"
|
48
|
+
|
44
49
|
Lite::Uxid.configure do |config|
|
45
|
-
config.hashid_charset =
|
50
|
+
config.hashid_charset = ALPHANUMERIC
|
46
51
|
config.hashid_salt = 1_369_136
|
47
52
|
config.hashid_size = 16
|
48
|
-
config.nanoid_charset =
|
53
|
+
config.nanoid_charset = WEB_SAFE
|
49
54
|
config.nanoid_size = 21
|
50
|
-
config.ulid_charset =
|
55
|
+
config.ulid_charset = COCKFORDS_32
|
51
56
|
config.ulid_size = 26
|
52
57
|
config.uuid_version = 4
|
53
58
|
end
|
@@ -57,22 +62,22 @@ end
|
|
57
62
|
|
58
63
|
#### Instance
|
59
64
|
```ruby
|
60
|
-
coder = Lite::Uxid::Hashid.new(10, size: 12)
|
65
|
+
coder = Lite::Uxid::Reversible::Hashid.new(10, size: 12)
|
61
66
|
coder.encode #=> '67wGI0'
|
62
67
|
```
|
63
68
|
|
64
69
|
#### Class
|
65
70
|
```ruby
|
66
|
-
Lite::Uxid::Hashid.decode('67wGI0', size: 12) #=> 10
|
71
|
+
Lite::Uxid::Reversible::Hashid.decode('67wGI0', size: 12) #=> 10
|
67
72
|
```
|
68
73
|
|
69
|
-
##
|
74
|
+
## HashID
|
70
75
|
|
71
76
|
[More information](https://hashids.org)
|
72
77
|
|
73
78
|
```ruby
|
74
|
-
Lite::Uxid::Hashid.encode(10) #=> '1zWr1m0'
|
75
|
-
Lite::Uxid::Hashid.decode('1zWr1m0') #=> 10
|
79
|
+
Lite::Uxid::Reversible::Hashid.encode(10) #=> '1zWr1m0'
|
80
|
+
Lite::Uxid::Reversible::Hashid.decode('1zWr1m0') #=> 10
|
76
81
|
```
|
77
82
|
|
78
83
|
## NanoID
|
@@ -80,7 +85,16 @@ Lite::Uxid::Hashid.decode('1zWr1m0') #=> 10
|
|
80
85
|
[More information](https://github.com/ai/nanoid)
|
81
86
|
|
82
87
|
```ruby
|
83
|
-
Lite::Uxid::Nanoid.encode #=> 'sMuNUa3Cegn6r5GRQ4Ij2'
|
88
|
+
Lite::Uxid::Irreversible::Nanoid.encode #=> 'sMuNUa3Cegn6r5GRQ4Ij2'
|
89
|
+
```
|
90
|
+
|
91
|
+
## ScatterID
|
92
|
+
|
93
|
+
[More information](https://github.com/namick/scatter_swap)
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
Lite::Uxid::Reversible::Scatterid.encode(10) #=> '2056964183'
|
97
|
+
Lite::Uxid::Reversible::Scatterid.decode('2056964183') #=> 10
|
84
98
|
```
|
85
99
|
|
86
100
|
## ULID
|
@@ -88,7 +102,7 @@ Lite::Uxid::Nanoid.encode #=> 'sMuNUa3Cegn6r5GRQ4Ij2'
|
|
88
102
|
[More information](https://github.com/ulid/spec)
|
89
103
|
|
90
104
|
```ruby
|
91
|
-
Lite::Uxid::Ulid.encode #=> '01GJAY9KGR539EZF4QWYEJGSN7'
|
105
|
+
Lite::Uxid::Irreversible::Ulid.encode #=> '01GJAY9KGR539EZF4QWYEJGSN7'
|
92
106
|
```
|
93
107
|
|
94
108
|
## UUID
|
@@ -96,7 +110,7 @@ Lite::Uxid::Ulid.encode #=> '01GJAY9KGR539EZF4QWYEJGSN7'
|
|
96
110
|
Implements `v4` and `v7` of the specification. [More information](https://en.wikipedia.org/wiki/Universally_unique_identifier)
|
97
111
|
|
98
112
|
```ruby
|
99
|
-
Lite::Uxid::Uuid.encode #=> '4376a67e-1189-44b3-a599-7f7566bf105b'
|
113
|
+
Lite::Uxid::Irreversible::Uuid.encode #=> '4376a67e-1189-44b3-a599-7f7566bf105b'
|
100
114
|
```
|
101
115
|
|
102
116
|
## Options
|
@@ -104,7 +118,7 @@ Lite::Uxid::Uuid.encode #=> '4376a67e-1189-44b3-a599-7f7566bf105b'
|
|
104
118
|
Local options can be passed to override global options.
|
105
119
|
|
106
120
|
```ruby
|
107
|
-
Lite::Uxid::Ulid.encode(charset: 'abc123', size: 12) #=> 'a3b12c12c3ca'
|
121
|
+
Lite::Uxid::Irreversible::Ulid.encode(charset: 'abc123', size: 12) #=> 'a3b12c12c3ca'
|
108
122
|
```
|
109
123
|
|
110
124
|
Passable options are:
|
@@ -114,6 +128,7 @@ Passable options are:
|
|
114
128
|
charset: 'string', # Available for: hashid, nanoid, ulid
|
115
129
|
salt: 'string', # Available for: hashid
|
116
130
|
size: 'integer', # Available for: hashid, nanoid, ulid
|
131
|
+
spin: 'integer', # Available for: scatterid
|
117
132
|
version: 'integer', # Available for: uuid
|
118
133
|
prefix: 'string' # Available for: hashid, nanoid
|
119
134
|
}
|
@@ -155,6 +170,13 @@ class User < ActiveRecord::Base
|
|
155
170
|
end
|
156
171
|
```
|
157
172
|
|
173
|
+
#### ScatterID
|
174
|
+
```ruby
|
175
|
+
class User < ActiveRecord::Base
|
176
|
+
include Lite::Uxid::Record::Scatterid
|
177
|
+
end
|
178
|
+
```
|
179
|
+
|
158
180
|
#### ULID
|
159
181
|
```ruby
|
160
182
|
class User < ActiveRecord::Base
|
@@ -6,6 +6,7 @@ Lite::Uxid.configure do |config|
|
|
6
6
|
config.hashid_size = 16
|
7
7
|
config.nanoid_charset = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
8
8
|
config.nanoid_size = 21
|
9
|
+
config.scatterid_spin = 0
|
9
10
|
config.ulid_charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
10
11
|
config.ulid_size = 26
|
11
12
|
config.uuid_version = 4
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Uxid
|
5
|
+
module Base
|
6
|
+
|
7
|
+
def encode
|
8
|
+
raise NotImplementedError, "override encode method in #{coder_class}"
|
9
|
+
end
|
10
|
+
|
11
|
+
def decode
|
12
|
+
raise NotImplementedError, "override decode method in #{coder_class}"
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def coder_value_for(key)
|
18
|
+
sym_key = :"#{coder_class.downcase}_#{key}"
|
19
|
+
return unless Lite::Uxid.configuration.respond_to?(sym_key)
|
20
|
+
|
21
|
+
opts.delete(key) || Lite::Uxid.configuration.send(sym_key)
|
22
|
+
end
|
23
|
+
|
24
|
+
def coder_charset
|
25
|
+
@coder_charset ||= coder_value_for(:charset)
|
26
|
+
end
|
27
|
+
|
28
|
+
def coder_class
|
29
|
+
@coder_class ||= self.class.name.split("::").last
|
30
|
+
end
|
31
|
+
|
32
|
+
def coder_length
|
33
|
+
@coder_length ||= coder_charset.size
|
34
|
+
end
|
35
|
+
|
36
|
+
def coder_prefix
|
37
|
+
@coder_prefix ||= opts.delete(:prefix)
|
38
|
+
end
|
39
|
+
|
40
|
+
def coder_salt
|
41
|
+
@coder_salt ||= coder_value_for(:salt)
|
42
|
+
end
|
43
|
+
|
44
|
+
def coder_size
|
45
|
+
@coder_size ||= coder_value_for(:size)
|
46
|
+
end
|
47
|
+
|
48
|
+
def coder_spin
|
49
|
+
@coder_spin ||= coder_value_for(:spin)
|
50
|
+
end
|
51
|
+
|
52
|
+
def coder_version
|
53
|
+
@coder_version ||= coder_value_for(:version)
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -3,20 +3,26 @@
|
|
3
3
|
module Lite
|
4
4
|
module Uxid
|
5
5
|
|
6
|
+
ALPHANUMERIC = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
7
|
+
COCKFORDS_32 = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
|
8
|
+
WEB_SAFE = "#{ALPHANUMERIC}-_".freeze
|
9
|
+
|
6
10
|
class Configuration
|
7
11
|
|
8
12
|
attr_accessor :hashid_charset, :hashid_size, :hashid_salt,
|
9
13
|
:nanoid_charset, :nanoid_size,
|
10
|
-
:
|
14
|
+
:scatterid_spin,
|
15
|
+
:ulid_charset, :ulid_size,
|
11
16
|
:uuid_version
|
12
17
|
|
13
18
|
def initialize
|
14
|
-
@hashid_charset =
|
19
|
+
@hashid_charset = ALPHANUMERIC
|
15
20
|
@hashid_salt = 1_369_136
|
16
21
|
@hashid_size = 16
|
17
|
-
@nanoid_charset =
|
22
|
+
@nanoid_charset = WEB_SAFE
|
18
23
|
@nanoid_size = 21
|
19
|
-
@
|
24
|
+
@scatterid_spin = 0
|
25
|
+
@ulid_charset = COCKFORDS_32
|
20
26
|
@ulid_size = 26
|
21
27
|
@uuid_version = 4
|
22
28
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Uxid
|
5
|
+
module Irreversible
|
6
|
+
class Base
|
7
|
+
|
8
|
+
include Lite::Uxid::Base
|
9
|
+
|
10
|
+
attr_reader :opts
|
11
|
+
|
12
|
+
def initialize(opts = {})
|
13
|
+
@opts = opts
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.encode(opts = {})
|
17
|
+
klass = new(opts)
|
18
|
+
klass.encode
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.decode(opts = {})
|
22
|
+
klass = new(opts)
|
23
|
+
klass.decode
|
24
|
+
end
|
25
|
+
|
26
|
+
def decode
|
27
|
+
raise "#{coder_class} does not support decoding"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Uxid
|
5
|
+
module Irreversible
|
6
|
+
class Nanoid < Base
|
7
|
+
|
8
|
+
def encode
|
9
|
+
encoded_id = +""
|
10
|
+
|
11
|
+
loop do
|
12
|
+
cached_bytes = bytes
|
13
|
+
|
14
|
+
(0...step).each do |idx|
|
15
|
+
byte = cached_bytes[idx] & mask
|
16
|
+
char = byte && coder_charset[byte]
|
17
|
+
next unless char
|
18
|
+
|
19
|
+
encoded_id << char
|
20
|
+
return "#{coder_prefix}#{encoded_id}" if encoded_id.size == coder_size
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
"#{coder_prefix}#{encoded_id}"
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def bytes
|
30
|
+
SecureRandom.random_bytes(coder_size).bytes
|
31
|
+
end
|
32
|
+
|
33
|
+
def mask
|
34
|
+
@mask ||= (2 << (Math.log(coder_length - 1) / Math.log(2))) - 1
|
35
|
+
end
|
36
|
+
|
37
|
+
def step
|
38
|
+
@step = (1.6 * mask * coder_size / coder_length).ceil
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Uxid
|
5
|
+
module Irreversible
|
6
|
+
class Ulid < Base
|
7
|
+
|
8
|
+
MASK = 0x1f
|
9
|
+
|
10
|
+
def encode
|
11
|
+
encoded_id = "0" * coder_size
|
12
|
+
oct = octect
|
13
|
+
pos = coder_size - 1
|
14
|
+
|
15
|
+
while oct.positive?
|
16
|
+
encoded_id[pos] = coder_charset[oct & MASK]
|
17
|
+
oct >>= 5
|
18
|
+
pos -= 1
|
19
|
+
end
|
20
|
+
|
21
|
+
encoded_id
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def bytes
|
27
|
+
"#{unixtime_48bit}#{SecureRandom.random_bytes(10)}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def octect
|
31
|
+
(hi, lo) = bytes.unpack("Q>Q>")
|
32
|
+
(hi << 64) | lo
|
33
|
+
end
|
34
|
+
|
35
|
+
def unixtime_ms
|
36
|
+
time = Time.respond_to?(:current) ? Time.current : Time.now
|
37
|
+
time.to_i * 1_000
|
38
|
+
end
|
39
|
+
|
40
|
+
def unixtime_48bit
|
41
|
+
[unixtime_ms].pack("Q>")[2..-1]
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Uxid
|
5
|
+
module Irreversible
|
6
|
+
class Uuid < Base
|
7
|
+
|
8
|
+
def encode
|
9
|
+
case coder_version
|
10
|
+
when 7
|
11
|
+
SecureRandom.uuid_v7
|
12
|
+
else
|
13
|
+
SecureRandom.uuid
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,23 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support" unless defined?(ActiveSupport)
|
4
|
-
|
5
3
|
module Lite
|
6
4
|
module Uxid
|
7
5
|
module Record
|
8
6
|
module Hashid
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
base.class_eval do
|
11
|
+
after_commit :callback_generate_uxid!,
|
12
|
+
if: proc { respond_to?(:uxid) && !uxid? },
|
13
|
+
on: :create
|
14
|
+
end
|
16
15
|
end
|
17
16
|
|
18
|
-
|
17
|
+
module ClassMethods
|
18
|
+
|
19
19
|
def find_by_uxid(uxid)
|
20
|
-
decoded_id = Lite::Uxid::Hashid.decode(uxid, prefix: new.uxid_prefix)
|
20
|
+
decoded_id = Lite::Uxid::Reversible::Hashid.decode(uxid, prefix: new.uxid_prefix)
|
21
21
|
find_by(id: decoded_id)
|
22
22
|
end
|
23
23
|
|
@@ -27,18 +27,19 @@ module Lite
|
|
27
27
|
|
28
28
|
raise ActiveRecord::RecordNotFound
|
29
29
|
end
|
30
|
+
|
30
31
|
end
|
31
32
|
|
32
33
|
def id_to_uxid
|
33
34
|
return unless respond_to?(:uxid)
|
34
35
|
|
35
|
-
Lite::Uxid::Hashid.encode(id, prefix: uxid_prefix)
|
36
|
+
Lite::Uxid::Reversible::Hashid.encode(id, prefix: uxid_prefix)
|
36
37
|
end
|
37
38
|
|
38
39
|
def uxid_to_id
|
39
40
|
return unless respond_to?(:uxid)
|
40
41
|
|
41
|
-
Lite::Uxid::Hashid.decode(uxid, prefix: uxid_prefix)
|
42
|
+
Lite::Uxid::Reversible::Hashid.decode(uxid, prefix: uxid_prefix)
|
42
43
|
end
|
43
44
|
|
44
45
|
def uxid_prefix
|
@@ -1,17 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support" unless defined?(ActiveSupport)
|
4
|
-
|
5
3
|
module Lite
|
6
4
|
module Uxid
|
7
5
|
module Record
|
8
6
|
module Nanoid
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval do
|
10
|
+
before_create :callback_generate_uxid!,
|
11
|
+
if: proc { respond_to?(:uxid) && !uxid? }
|
12
|
+
end
|
15
13
|
end
|
16
14
|
|
17
15
|
def uxid_prefix
|
@@ -24,7 +22,7 @@ module Lite
|
|
24
22
|
random_nanoid = nil
|
25
23
|
|
26
24
|
loop do
|
27
|
-
random_nanoid = Lite::Uxid::Nanoid.encode(prefix: uxid_prefix)
|
25
|
+
random_nanoid = Lite::Uxid::Irreversible::Nanoid.encode(prefix: uxid_prefix)
|
28
26
|
break unless self.class.exists?(uxid: random_nanoid)
|
29
27
|
end
|
30
28
|
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Uxid
|
5
|
+
module Record
|
6
|
+
module Scatterid
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.extend ClassMethods
|
10
|
+
base.class_eval do
|
11
|
+
after_commit :callback_generate_uxid!,
|
12
|
+
if: proc { respond_to?(:uxid) && !uxid? },
|
13
|
+
on: :create
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
|
19
|
+
def find_by_uxid(uxid)
|
20
|
+
decoded_id = Lite::Uxid::Reversible::Scatterid.decode(uxid, prefix: new.uxid_prefix)
|
21
|
+
find_by(id: decoded_id)
|
22
|
+
end
|
23
|
+
|
24
|
+
def find_by_uxid!(uxid)
|
25
|
+
record = find_by_uxid(uxid)
|
26
|
+
return record unless record.nil?
|
27
|
+
|
28
|
+
raise ActiveRecord::RecordNotFound
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
def id_to_uxid
|
34
|
+
return unless respond_to?(:uxid)
|
35
|
+
|
36
|
+
Lite::Uxid::Reversible::Scatterid.encode(id, prefix: uxid_prefix)
|
37
|
+
end
|
38
|
+
|
39
|
+
def uxid_to_id
|
40
|
+
return unless respond_to?(:uxid)
|
41
|
+
|
42
|
+
Lite::Uxid::Reversible::Scatterid.decode(uxid, prefix: uxid_prefix)
|
43
|
+
end
|
44
|
+
|
45
|
+
def uxid_prefix
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def callback_generate_uxid!
|
52
|
+
update_column(:uxid, id_to_uxid)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -1,23 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support" unless defined?(ActiveSupport)
|
4
|
-
|
5
3
|
module Lite
|
6
4
|
module Uxid
|
7
5
|
module Record
|
8
6
|
module Ulid
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval do
|
10
|
+
before_create :callback_generate_uxid!,
|
11
|
+
if: proc { respond_to?(:uxid) && !uxid? }
|
12
|
+
end
|
15
13
|
end
|
16
14
|
|
17
15
|
private
|
18
16
|
|
19
17
|
def callback_generate_uxid!
|
20
|
-
self.uxid = Lite::Uxid::Ulid.encode
|
18
|
+
self.uxid = Lite::Uxid::Irreversible::Ulid.encode
|
21
19
|
end
|
22
20
|
|
23
21
|
end
|
@@ -1,23 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "active_support" unless defined?(ActiveSupport)
|
4
|
-
|
5
3
|
module Lite
|
6
4
|
module Uxid
|
7
5
|
module Record
|
8
6
|
module Uuid
|
9
7
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval do
|
10
|
+
before_create :callback_generate_uxid!,
|
11
|
+
if: proc { respond_to?(:uxid) && !uxid? }
|
12
|
+
end
|
15
13
|
end
|
16
14
|
|
17
15
|
private
|
18
16
|
|
19
17
|
def callback_generate_uxid!
|
20
|
-
self.uxid = Lite::Uxid::Uuid.encode
|
18
|
+
self.uxid = Lite::Uxid::Irreversible::Uuid.encode
|
21
19
|
end
|
22
20
|
|
23
21
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Uxid
|
5
|
+
module Reversible
|
6
|
+
class Base
|
7
|
+
|
8
|
+
include Lite::Uxid::Base
|
9
|
+
|
10
|
+
attr_reader :id, :opts
|
11
|
+
|
12
|
+
def initialize(id, opts = {})
|
13
|
+
@id = id
|
14
|
+
@opts = opts
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.encode(id, opts = {})
|
18
|
+
klass = new(id, opts)
|
19
|
+
klass.encode
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.decode(id, opts = {})
|
23
|
+
klass = new(id, opts)
|
24
|
+
klass.decode
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Uxid
|
5
|
+
module Reversible
|
6
|
+
class Hashid < Base
|
7
|
+
|
8
|
+
def encode
|
9
|
+
encoded_id = encode_chars((id + coder_salt) << coder_size)
|
10
|
+
"#{coder_prefix}#{encoded_id}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def decode
|
14
|
+
encoded_id = id.delete_prefix(coder_prefix.to_s)
|
15
|
+
(decode_chars(encoded_id) >> coder_size) - coder_salt
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def encode_chars(decoded_id)
|
21
|
+
return "0" if decoded_id.zero?
|
22
|
+
return nil if decoded_id.negative?
|
23
|
+
|
24
|
+
str = ""
|
25
|
+
|
26
|
+
while decoded_id.positive?
|
27
|
+
str = "#{coder_charset[decoded_id % coder_length]}#{str}"
|
28
|
+
decoded_id /= coder_length
|
29
|
+
end
|
30
|
+
|
31
|
+
str
|
32
|
+
end
|
33
|
+
|
34
|
+
def decode_chars(encoded_id)
|
35
|
+
pos = 0
|
36
|
+
num = 0
|
37
|
+
len = encoded_id.size
|
38
|
+
max = len - 1
|
39
|
+
|
40
|
+
while pos < len
|
41
|
+
pow = coder_length**(max - pos)
|
42
|
+
num += coder_charset.index(encoded_id[pos]) * pow
|
43
|
+
pos += 1
|
44
|
+
end
|
45
|
+
|
46
|
+
num
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Lite
|
4
|
+
module Uxid
|
5
|
+
module Reversible
|
6
|
+
class Scatterid < Base
|
7
|
+
|
8
|
+
def encode
|
9
|
+
swap
|
10
|
+
scatter
|
11
|
+
|
12
|
+
"#{coder_prefix}#{joined_array}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def decode
|
16
|
+
@id = id.delete_prefix(coder_prefix.to_s)
|
17
|
+
|
18
|
+
unscatter
|
19
|
+
unswap
|
20
|
+
joined_array.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def zero_padded_id
|
26
|
+
@zero_padded_id ||= id.to_s.rjust(10, "0")
|
27
|
+
end
|
28
|
+
|
29
|
+
def coder_array
|
30
|
+
@coder_array ||= zero_padded_id.chars.collect(&:to_i)
|
31
|
+
end
|
32
|
+
|
33
|
+
def joined_array
|
34
|
+
coder_array.join
|
35
|
+
end
|
36
|
+
|
37
|
+
def swapper_map(index)
|
38
|
+
array = (0..9).to_a
|
39
|
+
Array.new(10) { |i| array.rotate!((index + i) ^ coder_spin).pop }
|
40
|
+
end
|
41
|
+
|
42
|
+
def swap
|
43
|
+
coder_array.map!.with_index { |digit, i| swapper_map(i)[digit] }
|
44
|
+
end
|
45
|
+
|
46
|
+
def unswap
|
47
|
+
coder_array.map!.with_index { |digit, i| swapper_map(i).rindex(digit) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def scatter
|
51
|
+
sum_of_digits = coder_array.sum.to_i
|
52
|
+
@coder_array = Array.new(10) { coder_array.rotate!(coder_spin ^ sum_of_digits).pop }
|
53
|
+
end
|
54
|
+
|
55
|
+
def unscatter
|
56
|
+
scattered_array = coder_array
|
57
|
+
sum_of_digits = scattered_array.sum.to_i
|
58
|
+
@coder_array = []
|
59
|
+
|
60
|
+
coder_array.tap do |unscatter|
|
61
|
+
10.times do
|
62
|
+
unscatter.push(scattered_array.pop)
|
63
|
+
unscatter.rotate!((sum_of_digits ^ coder_spin) * -1)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
data/lib/lite/uxid/version.rb
CHANGED
data/lib/lite/uxid.rb
CHANGED
@@ -1,16 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "securerandom" unless defined?(SecureRandom)
|
3
4
|
require "generators/lite/uxid/install_generator" if defined?(Rails::Generators)
|
4
5
|
|
5
6
|
require "lite/uxid/version"
|
6
7
|
require "lite/uxid/configuration"
|
7
|
-
require "lite/uxid/base
|
8
|
-
require "lite/uxid/base
|
8
|
+
require "lite/uxid/base"
|
9
|
+
require "lite/uxid/irreversible/base"
|
10
|
+
require "lite/uxid/irreversible/nanoid"
|
11
|
+
require "lite/uxid/irreversible/ulid"
|
12
|
+
require "lite/uxid/irreversible/uuid"
|
13
|
+
require "lite/uxid/reversible/base"
|
14
|
+
require "lite/uxid/reversible/hashid"
|
15
|
+
require "lite/uxid/reversible/scatterid"
|
9
16
|
require "lite/uxid/record/hashid"
|
10
17
|
require "lite/uxid/record/nanoid"
|
18
|
+
require "lite/uxid/record/scatterid"
|
11
19
|
require "lite/uxid/record/ulid"
|
12
20
|
require "lite/uxid/record/uuid"
|
13
|
-
require "lite/uxid/hashid"
|
14
|
-
require "lite/uxid/nanoid"
|
15
|
-
require "lite/uxid/ulid"
|
16
|
-
require "lite/uxid/uuid"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lite-uxid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Juan Gomez
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-09-
|
11
|
+
date: 2024-09-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -217,17 +217,20 @@ files:
|
|
217
217
|
- lib/generators/lite/uxid/install_generator.rb
|
218
218
|
- lib/generators/lite/uxid/templates/install.rb
|
219
219
|
- lib/lite/uxid.rb
|
220
|
-
- lib/lite/uxid/base
|
221
|
-
- lib/lite/uxid/base/reversible.rb
|
220
|
+
- lib/lite/uxid/base.rb
|
222
221
|
- lib/lite/uxid/configuration.rb
|
223
|
-
- lib/lite/uxid/
|
224
|
-
- lib/lite/uxid/nanoid.rb
|
222
|
+
- lib/lite/uxid/irreversible/base.rb
|
223
|
+
- lib/lite/uxid/irreversible/nanoid.rb
|
224
|
+
- lib/lite/uxid/irreversible/ulid.rb
|
225
|
+
- lib/lite/uxid/irreversible/uuid.rb
|
225
226
|
- lib/lite/uxid/record/hashid.rb
|
226
227
|
- lib/lite/uxid/record/nanoid.rb
|
228
|
+
- lib/lite/uxid/record/scatterid.rb
|
227
229
|
- lib/lite/uxid/record/ulid.rb
|
228
230
|
- lib/lite/uxid/record/uuid.rb
|
229
|
-
- lib/lite/uxid/
|
230
|
-
- lib/lite/uxid/
|
231
|
+
- lib/lite/uxid/reversible/base.rb
|
232
|
+
- lib/lite/uxid/reversible/hashid.rb
|
233
|
+
- lib/lite/uxid/reversible/scatterid.rb
|
231
234
|
- lib/lite/uxid/version.rb
|
232
235
|
- lite-uxid.gemspec
|
233
236
|
homepage: http://drexed.github.io/lite-uxid
|
@@ -1,82 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "securerandom" unless defined?(SecureRandom)
|
4
|
-
|
5
|
-
module Lite
|
6
|
-
module Uxid
|
7
|
-
module Base
|
8
|
-
class Irreversible
|
9
|
-
|
10
|
-
attr_reader :opts
|
11
|
-
|
12
|
-
def initialize(opts = {})
|
13
|
-
@opts = opts
|
14
|
-
end
|
15
|
-
|
16
|
-
class << self
|
17
|
-
|
18
|
-
def encode(opts = {})
|
19
|
-
klass = new(opts)
|
20
|
-
klass.encode
|
21
|
-
end
|
22
|
-
|
23
|
-
def decode(opts = {})
|
24
|
-
klass = new(opts)
|
25
|
-
klass.decode
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
def encode
|
31
|
-
raise NotImplementedError, "override method in #{coder_class}"
|
32
|
-
end
|
33
|
-
|
34
|
-
def decode
|
35
|
-
raise NotImplementedError, "coder does not support decoding"
|
36
|
-
end
|
37
|
-
|
38
|
-
private
|
39
|
-
|
40
|
-
def coder_value_for(key)
|
41
|
-
sym_key = :"#{coder_class.downcase}_#{key}"
|
42
|
-
return unless Lite::Uxid.configuration.respond_to?(sym_key)
|
43
|
-
|
44
|
-
opts.delete(key) || Lite::Uxid.configuration.send(sym_key)
|
45
|
-
end
|
46
|
-
|
47
|
-
def coder_bytes
|
48
|
-
@coder_bytes ||= SecureRandom.random_bytes(coder_size).bytes
|
49
|
-
end
|
50
|
-
|
51
|
-
def coder_charset
|
52
|
-
@coder_charset ||= coder_value_for(:charset)
|
53
|
-
end
|
54
|
-
|
55
|
-
def coder_class
|
56
|
-
@coder_class ||= self.class.name.split("::").last
|
57
|
-
end
|
58
|
-
|
59
|
-
def coder_length
|
60
|
-
@coder_length ||= coder_charset.size
|
61
|
-
end
|
62
|
-
|
63
|
-
def coder_prefix
|
64
|
-
@coder_prefix ||= opts.delete(:prefix)
|
65
|
-
end
|
66
|
-
|
67
|
-
def coder_salt
|
68
|
-
@coder_salt ||= coder_value_for(:salt)
|
69
|
-
end
|
70
|
-
|
71
|
-
def coder_size
|
72
|
-
@coder_size ||= coder_value_for(:size)
|
73
|
-
end
|
74
|
-
|
75
|
-
def coder_version
|
76
|
-
@coder_version ||= coder_value_for(:version)
|
77
|
-
end
|
78
|
-
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Lite
|
4
|
-
module Uxid
|
5
|
-
module Base
|
6
|
-
class Reversible < Irreversible
|
7
|
-
|
8
|
-
attr_reader :id
|
9
|
-
|
10
|
-
def initialize(id, opts = {})
|
11
|
-
@id = id
|
12
|
-
super(opts)
|
13
|
-
end
|
14
|
-
|
15
|
-
class << self
|
16
|
-
|
17
|
-
def encode(id, opts = {})
|
18
|
-
klass = new(id, opts)
|
19
|
-
klass.encode
|
20
|
-
end
|
21
|
-
|
22
|
-
def decode(id, opts = {})
|
23
|
-
klass = new(id, opts)
|
24
|
-
klass.decode
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
28
|
-
|
29
|
-
def decode
|
30
|
-
raise NotImplementedError, "override method in #{coder_class}"
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
data/lib/lite/uxid/hashid.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Lite
|
4
|
-
module Uxid
|
5
|
-
class Hashid < Base::Reversible
|
6
|
-
|
7
|
-
def encode
|
8
|
-
uxid = encode_chars((id + coder_salt) << coder_size)
|
9
|
-
"#{coder_prefix}#{uxid}"
|
10
|
-
end
|
11
|
-
|
12
|
-
def decode
|
13
|
-
uxid = id.delete_prefix(coder_prefix.to_s)
|
14
|
-
(decode_chars(uxid) >> coder_size) - coder_salt
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def encode_chars(decoded_id)
|
20
|
-
return "0" if decoded_id.zero?
|
21
|
-
return nil if decoded_id.negative?
|
22
|
-
|
23
|
-
str = ""
|
24
|
-
|
25
|
-
while decoded_id.positive?
|
26
|
-
str = "#{coder_charset[decoded_id % coder_length]}#{str}"
|
27
|
-
decoded_id /= coder_length
|
28
|
-
end
|
29
|
-
|
30
|
-
str
|
31
|
-
end
|
32
|
-
|
33
|
-
def decode_chars(encoded_id)
|
34
|
-
pos = 0
|
35
|
-
num = 0
|
36
|
-
len = encoded_id.size
|
37
|
-
max = len - 1
|
38
|
-
|
39
|
-
while pos < len
|
40
|
-
pow = coder_length**(max - pos)
|
41
|
-
num += coder_charset.index(encoded_id[pos]) * pow
|
42
|
-
pos += 1
|
43
|
-
end
|
44
|
-
|
45
|
-
num
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
data/lib/lite/uxid/nanoid.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Lite
|
4
|
-
module Uxid
|
5
|
-
class Nanoid < Base::Irreversible
|
6
|
-
|
7
|
-
def encode
|
8
|
-
uxid = (0...coder_size).each_with_object(+"") do |i, str|
|
9
|
-
str << coder_charset[coder_bytes[i] & 63]
|
10
|
-
end
|
11
|
-
|
12
|
-
"#{coder_prefix}#{uxid}"
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
data/lib/lite/uxid/ulid.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Lite
|
4
|
-
module Uxid
|
5
|
-
class Ulid < Base::Irreversible
|
6
|
-
|
7
|
-
MASK = 0x1f
|
8
|
-
|
9
|
-
def encode
|
10
|
-
oct = octect
|
11
|
-
ele = "0" * coder_size
|
12
|
-
pos = coder_size - 1
|
13
|
-
|
14
|
-
while oct.positive?
|
15
|
-
ele[pos] = coder_charset[oct & MASK]
|
16
|
-
oct >>= 5
|
17
|
-
pos -= 1
|
18
|
-
end
|
19
|
-
|
20
|
-
ele
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def bytes
|
26
|
-
"#{unixtime_48bit}#{SecureRandom.random_bytes(10)}"
|
27
|
-
end
|
28
|
-
|
29
|
-
def octect
|
30
|
-
(hi, lo) = bytes.unpack("Q>Q>")
|
31
|
-
(hi << 64) | lo
|
32
|
-
end
|
33
|
-
|
34
|
-
def unixtime_ms
|
35
|
-
time = Time.respond_to?(:current) ? Time.current : Time.now
|
36
|
-
time.to_i * 1_000
|
37
|
-
end
|
38
|
-
|
39
|
-
def unixtime_48bit
|
40
|
-
[unixtime_ms].pack("Q>")[2..-1]
|
41
|
-
end
|
42
|
-
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
data/lib/lite/uxid/uuid.rb
DELETED