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