lite-uxid 1.5.1 → 2.0.0

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: 5c35d2426c2fffc91f8195a96c94ff26df0c41ffdb79041c5499952c93ccd240
4
- data.tar.gz: 5a72d11d4fc6d3cec87411a623d072dec4ba1f17b810814b15c543da3a512d7b
3
+ metadata.gz: 767645737040b210cc058a0a781f89751f447e991aa03c527f5d12b039d1980a
4
+ data.tar.gz: 1c777db2675144f26b24ed2ec75d175c83f55d5ede9e83ab5d0d75ca2b76e692
5
5
  SHA512:
6
- metadata.gz: 0a84caf603e6393456ac15d30afaf23fda68eec1480997ce64a54339abefcfc5fadf1afe4fb505f6b3f54119188e3f3405e040771e4a76cc4cae3f8b7f842693
7
- data.tar.gz: 0d345ae245719fcfe669230925aee58feab58153c978c6089379cc750ae417325d98bd6b660aba695e2df85d540d88a543bfd5a11ce88556fb2ef835f81a860d
6
+ metadata.gz: 313da972f69d52e8951d552c3269500767e7fb80255de25c44d1bbe0d6497aaa3b0c398736b2e451d393ad0642446e298ebbad36a225528f010a96a8684f221e
7
+ data.tar.gz: 61ce05436d5e2793164007f1b28edd28ea138c9efbf02f0a6ac6ef021708d3171b61c77e2811dfe9c65ef764b46d55713e6121eefd57d87a22cb10e80a9c0eef
data/.rubocop.yml CHANGED
@@ -23,6 +23,8 @@ Layout/LineLength:
23
23
  Enabled: false
24
24
  Layout/SpaceAroundMethodCallOperator:
25
25
  Enabled: true
26
+ Lint/MissingSuper:
27
+ Enabled: false
26
28
  Lint/RaiseException:
27
29
  Enabled: true
28
30
  Lint/StructNewOverride:
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- lite-uxid (1.5.1)
4
+ lite-uxid (2.0.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
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 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
50
+ config.hashid_charset = ALPHANUMERIC
46
51
  config.hashid_salt = 1_369_136
47
52
  config.hashid_size = 16
48
- config.nanoid_charset = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
53
+ config.nanoid_charset = WEB_SAFE
49
54
  config.nanoid_size = 21
50
- config.ulid_charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
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
- ## Hashid
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
- :ulid_charset, :ulid_size,
14
+ :scatterid_spin,
15
+ :ulid_charset, :ulid_size,
11
16
  :uuid_version
12
17
 
13
18
  def initialize
14
- @hashid_charset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
19
+ @hashid_charset = ALPHANUMERIC
15
20
  @hashid_salt = 1_369_136
16
21
  @hashid_size = 16
17
- @nanoid_charset = "_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
22
+ @nanoid_charset = WEB_SAFE
18
23
  @nanoid_size = 21
19
- @ulid_charset = "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
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
- extend ActiveSupport::Concern
11
-
12
- included do
13
- after_commit :callback_generate_uxid!,
14
- if: proc { respond_to?(:uxid) && !uxid? },
15
- on: :create
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
- class_methods do
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
- extend ActiveSupport::Concern
11
-
12
- included do
13
- before_create :callback_generate_uxid!,
14
- if: proc { respond_to?(:uxid) && !uxid? }
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
- extend ActiveSupport::Concern
11
-
12
- included do
13
- before_create :callback_generate_uxid!,
14
- if: proc { respond_to?(:uxid) && !uxid? }
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
- extend ActiveSupport::Concern
11
-
12
- included do
13
- before_create :callback_generate_uxid!,
14
- if: proc { respond_to?(:uxid) && !uxid? }
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
@@ -3,7 +3,7 @@
3
3
  module Lite
4
4
  module Uxid
5
5
 
6
- VERSION = "1.5.1"
6
+ VERSION = "2.0.0"
7
7
 
8
8
  end
9
9
  end
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/irreversible"
8
- require "lite/uxid/base/reversible"
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: 1.5.1
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-20 00:00:00.000000000 Z
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/irreversible.rb
221
- - lib/lite/uxid/base/reversible.rb
220
+ - lib/lite/uxid/base.rb
222
221
  - lib/lite/uxid/configuration.rb
223
- - lib/lite/uxid/hashid.rb
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/ulid.rb
230
- - lib/lite/uxid/uuid.rb
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
@@ -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
@@ -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
@@ -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
@@ -1,18 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Lite
4
- module Uxid
5
- class Uuid < Base::Irreversible
6
-
7
- def encode
8
- case coder_version
9
- when 7
10
- SecureRandom.uuid_v7
11
- else
12
- SecureRandom.uuid
13
- end
14
- end
15
-
16
- end
17
- end
18
- end