sqids 0.1.2 → 0.2.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: 8c79695a1c507e1785e6c454fed0e0f4bca689598608d5b680b019493dfe9406
4
- data.tar.gz: 9c64e11d7b0a8c551caf8b24d73fd01cb0aaf8ade971a4b6d535f120e1be8d9a
3
+ metadata.gz: f504d0947f3ffd6f0fe3f9dfb4044a056b4c0d30a9d2b95a8ac5b5a043a5c45b
4
+ data.tar.gz: 62fc2e00992383a7632b5f2a155ecfc09a2d221db4e2a31c505b8c54ea6ec12e
5
5
  SHA512:
6
- metadata.gz: 9992329b56a99da95e7ee85e1b928407d7cbb40f5312cbc18a0f17ed95b0ba51b175088dc84db69d894bfa0814d587f5cdbe327c7a6a1d56c89eb004c68a83ae
7
- data.tar.gz: 00d797e9d692a9c4ad82f80971649d2e4553d317c78b72e258ae497fa94521246205e56873971685b42d9b4ee7d8507d71bdf4c05fd80eab9735f58176adf230
6
+ metadata.gz: 596b48c403f99904706390dfeb23cfd9c59a075da4d970fada135aa75b9a54e39744bfd810de68b62852c7effe64fe21e3edf376d8f965455a8add0a594ab66e
7
+ data.tar.gz: 74fbcc76ed257c80fd0a03fc4133ccb1b897ec62381c5377c84b873e372bc58d777194359e2765a67d9e4e38cbac5927a89336eec9a2b0241fe11e29d2e92bc6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ **v0.2.0:** **⚠️ BREAKING CHANGE**
4
+ - **Breaking change**: IDs change. Algorithm has been fine-tuned for better performance [[Issue #11](https://github.com/sqids/sqids-spec/issues/11)]
5
+ - `alphabet` cannot contain multibyte characters
6
+ - `min_length` upper limit has increased from alphabet length to `255`
7
+ - Max blocklist re-encoding attempts has been capped at the length of the alphabet - 1
8
+ - Minimum alphabet length has changed from 5 to 3
9
+ - `min_value()` and `max_value()` functions have been removed
10
+
3
11
  **v0.1.2:**
4
12
  - Bug fix: spec update (PR #7): blocklist filtering in uppercase-only alphabet [[PR #7](https://github.com/sqids/sqids-spec/pull/7)]
5
13
  - Lower uniques test from 1_000_000 to 10_000
data/README.md CHANGED
@@ -55,34 +55,34 @@ Simple encode & decode:
55
55
 
56
56
  ```ruby
57
57
  sqids = Sqids.new
58
- id = sqids.encode([1, 2, 3]) # '8QRLaD'
58
+ id = sqids.encode([1, 2, 3]) # '86Rf07'
59
59
  numbers = sqids.decode(id) # [1, 2, 3]
60
60
  ```
61
61
 
62
62
  > **Note**
63
63
  > 🚧 Because of the algorithm's design, **multiple IDs can decode back into the same sequence of numbers**. If it's important to your design that IDs are canonical, you have to manually re-encode decoded numbers and check that the generated ID matches.
64
64
 
65
- Randomize IDs by providing a custom alphabet:
65
+ Enforce a *minimum* length for IDs:
66
66
 
67
67
  ```ruby
68
- sqids = Sqids.new(alphabet: 'FxnXM1kBN6cuhsAvjW3Co7l2RePyY8DwaU04Tzt9fHQrqSVKdpimLGIJOgb5ZE')
69
- id = sqids.encode([1, 2, 3]) # 'B5aMa3'
68
+ sqids = Sqids.new(min_length: 10)
69
+ id = sqids.encode([1, 2, 3]) # '86Rf07xd4z'
70
70
  numbers = sqids.decode(id) # [1, 2, 3]
71
71
  ```
72
72
 
73
- Enforce a *minimum* length for IDs:
73
+ Randomize IDs by providing a custom alphabet:
74
74
 
75
75
  ```ruby
76
- sqids = Sqids.new(min_length: 10)
77
- id = sqids.encode([1, 2, 3]) # '75JT1cd0dL'
76
+ sqids = Sqids.new(alphabet: 'FxnXM1kBN6cuhsAvjW3Co7l2RePyY8DwaU04Tzt9fHQrqSVKdpimLGIJOgb5ZE')
77
+ id = sqids.encode([1, 2, 3]) # 'B4aajs'
78
78
  numbers = sqids.decode(id) # [1, 2, 3]
79
79
  ```
80
80
 
81
81
  Prevent specific words from appearing anywhere in the auto-generated IDs:
82
82
 
83
83
  ```ruby
84
- sqids = Sqids.new(blocklist: Set.new(%w[word1 word2]))
85
- id = sqids.encode([1, 2, 3]) # '8QRLaD'
84
+ sqids = Sqids.new(blocklist: Set.new(%w[86Rf07]))
85
+ id = sqids.encode([1, 2, 3]) # 'se8ojk'
86
86
  numbers = sqids.decode(id) # [1, 2, 3]
87
87
  ```
88
88
 
data/lib/sqids.rb CHANGED
@@ -15,16 +15,18 @@ class Sqids
15
15
  min_length = options[:min_length] || DEFAULT_MIN_LENGTH
16
16
  blocklist = options[:blocklist] || DEFAULT_BLOCKLIST
17
17
 
18
- raise ArgumentError, 'Alphabet length must be at least 5' if alphabet.length < 5
18
+ raise ArgumentError, 'Alphabet cannot contain multibyte characters' if contains_multibyte_chars(alphabet)
19
+ raise ArgumentError, 'Alphabet length must be at least 3' if alphabet.length < 3
19
20
 
20
21
  if alphabet.chars.uniq.size != alphabet.length
21
22
  raise ArgumentError,
22
23
  'Alphabet must contain unique characters'
23
24
  end
24
25
 
25
- unless min_length.is_a?(Integer) && min_length >= Sqids.min_value && min_length <= alphabet.length
26
+ min_length_limit = 255
27
+ unless min_length.is_a?(Integer) && min_length >= 0 && min_length <= min_length_limit
26
28
  raise TypeError,
27
- "Minimum length has to be between #{Sqids.min_value} and #{alphabet.length}"
29
+ "Minimum length has to be between 0 and #{min_length_limit}"
28
30
  end
29
31
 
30
32
  filtered_blocklist = blocklist.select do |word|
@@ -39,13 +41,13 @@ class Sqids
39
41
  def encode(numbers)
40
42
  return '' if numbers.empty?
41
43
 
42
- in_range_numbers = numbers.select { |n| n >= Sqids.min_value && n <= Sqids.max_value }
44
+ in_range_numbers = numbers.select { |n| n >= 0 && n <= Sqids.max_value }
43
45
  unless in_range_numbers.length == numbers.length
44
46
  raise ArgumentError,
45
- "Encoding supports numbers between #{Sqids.min_value} and #{Sqids.max_value}"
47
+ "Encoding supports numbers between 0 and #{Sqids.max_value}"
46
48
  end
47
49
 
48
- encode_numbers(in_range_numbers, partitioned: false)
50
+ encode_numbers(in_range_numbers)
49
51
  end
50
52
 
51
53
  def decode(id)
@@ -61,26 +63,18 @@ class Sqids
61
63
  prefix = id[0]
62
64
  offset = @alphabet.index(prefix)
63
65
  alphabet = @alphabet.slice(offset, @alphabet.length) + @alphabet.slice(0, offset)
64
- partition = alphabet[1]
65
- alphabet = alphabet.slice(2, alphabet.length)
66
+ alphabet = alphabet.reverse
66
67
 
67
68
  id = id[1, id.length]
68
69
 
69
- partition_index = id.index(partition)
70
- if !partition_index.nil? && partition_index.positive? && partition_index < id.length - 1
71
- id = id[partition_index + 1, id.length]
72
- alphabet = shuffle(alphabet)
73
- end
74
-
75
70
  while id.length.positive?
76
- separator = alphabet[-1]
77
- chunks = id.split(separator, 2)
71
+ separator = alphabet[0]
78
72
 
73
+ chunks = id.split(separator, 2)
79
74
  if chunks.any?
80
- alphabet_without_separator = alphabet.slice(0, alphabet.length - 1)
81
- return [] unless chunks[0].chars.all? { |c| alphabet_without_separator.include?(c) }
75
+ return ret if chunks[0] == ''
82
76
 
83
- ret.push(to_number(chunks[0], alphabet_without_separator))
77
+ ret.push(to_number(chunks[0], alphabet.slice(1, alphabet.length - 1)))
84
78
  alphabet = shuffle(alphabet) if chunks.length > 1
85
79
  end
86
80
 
@@ -90,14 +84,6 @@ class Sqids
90
84
  ret
91
85
  end
92
86
 
93
- def self.min_value
94
- 0
95
- end
96
-
97
- def self.max_value
98
- defined?(Integer::MAX) ? Integer::MAX : ((2**((0.size * 8) - 2)) - 1)
99
- end
100
-
101
87
  private
102
88
 
103
89
  def shuffle(alphabet)
@@ -115,59 +101,42 @@ class Sqids
115
101
  chars.join
116
102
  end
117
103
 
118
- def encode_numbers(numbers, partitioned: false)
104
+ def encode_numbers(numbers, increment: 0)
105
+ raise ArgumentError, 'Reached max attempts to re-generate the ID' if increment > @alphabet.length
106
+
119
107
  offset = numbers.length
120
108
  numbers.each_with_index do |v, i|
121
109
  offset += @alphabet[v % @alphabet.length].ord + i
122
110
  end
123
111
  offset = offset % @alphabet.length
112
+ offset = (offset + increment) % @alphabet.length
124
113
 
125
114
  alphabet = @alphabet.slice(offset, @alphabet.length) + @alphabet.slice(0, offset)
126
115
  prefix = alphabet[0]
127
- partition = alphabet[1]
128
- alphabet = alphabet.slice(2, alphabet.length)
116
+ alphabet = alphabet.reverse
129
117
  ret = [prefix]
130
118
 
131
119
  numbers.each_with_index do |num, i|
132
- alphabet_without_separator = alphabet.slice(0, alphabet.length - 1)
133
- ret.push(to_id(num, alphabet_without_separator))
120
+ ret.push(to_id(num, alphabet.slice(1, alphabet.length - 1)))
134
121
 
135
122
  next unless i < numbers.length - 1
136
123
 
137
- separator = alphabet[-1]
138
- if partitioned && i.zero?
139
- ret.push(partition)
140
- else
141
- ret.push(separator)
142
- end
143
-
124
+ ret.push(alphabet[0])
144
125
  alphabet = shuffle(alphabet)
145
126
  end
146
127
 
147
128
  id = ret.join
148
129
 
149
130
  if @min_length > id.length
150
- unless partitioned
151
- numbers = [0] + numbers
152
- id = encode_numbers(numbers, partitioned: true)
153
- end
131
+ id += alphabet[0]
154
132
 
155
- if @min_length > id.length
156
- id = id[0] + alphabet[0,
157
- @min_length - id.length] + id[1,
158
- id.length - 1]
133
+ while (@min_length - id.length).positive?
134
+ alphabet = shuffle(alphabet)
135
+ id += alphabet.slice(0, [@min_length - id.length, alphabet.length].min)
159
136
  end
160
137
  end
161
138
 
162
- if blocked_id?(id)
163
- if partitioned
164
- numbers[0] += 1
165
- else
166
- numbers = [0] + numbers
167
- end
168
-
169
- id = encode_numbers(numbers, partitioned: true)
170
- end
139
+ id = encode_numbers(numbers, increment: increment + 1) if blocked_id?(id)
171
140
 
172
141
  id
173
142
  end
@@ -206,4 +175,16 @@ class Sqids
206
175
  end
207
176
  end
208
177
  end
178
+
179
+ def contains_multibyte_chars(input_str)
180
+ input_str.each_char do |char|
181
+ return true if char.bytesize > 1
182
+ end
183
+
184
+ false
185
+ end
186
+
187
+ def self.max_value
188
+ defined?(Integer::MAX) ? Integer::MAX : ((2**((0.size * 8) - 2)) - 1)
189
+ end
209
190
  end
@@ -8,14 +8,14 @@ describe Sqids do
8
8
  sqids = Sqids.new(alphabet: '0123456789abcdef')
9
9
 
10
10
  numbers = [1, 2, 3]
11
- id = '4d9fd2'
11
+ id = '489158'
12
12
 
13
13
  expect(sqids.encode(numbers)).to eq(id)
14
14
  expect(sqids.decode(id)).to eq(numbers)
15
15
  end
16
16
 
17
17
  it 'decodes after encoding with a short alphabet' do
18
- sqids = Sqids.new(alphabet: 'abcde')
18
+ sqids = Sqids.new(alphabet: 'abc')
19
19
 
20
20
  numbers = [1, 2, 3]
21
21
  encoded = sqids.encode(numbers)
@@ -33,6 +33,12 @@ describe Sqids do
33
33
  expect(sqids.decode(encoded)).to eq(numbers)
34
34
  end
35
35
 
36
+ it 'fails when alphabet has multibyte characters' do
37
+ expect do
38
+ Sqids.new(alphabet: 'ë1092')
39
+ end.to raise_error(ArgumentError)
40
+ end
41
+
36
42
  it 'fails when alphabet characters are repeated' do
37
43
  expect do
38
44
  Sqids.new(alphabet: 'aabcdefg')
@@ -41,7 +47,7 @@ describe Sqids do
41
47
 
42
48
  it 'fails when alphabet is too short' do
43
49
  expect do
44
- Sqids.new(alphabet: 'abcd')
50
+ Sqids.new(alphabet: 'ab')
45
51
  end.to raise_error(ArgumentError)
46
52
  end
47
53
  end
@@ -8,59 +8,74 @@ describe Sqids do
8
8
  it 'uses default blocklist if no custom blocklist is provided' do
9
9
  sqids = Sqids.new
10
10
 
11
- expect(sqids.decode('sexy')).to eq([200_044])
12
- expect(sqids.encode([200_044])).to eq('d171vI')
11
+ expect(sqids.decode('aho1e')).to eq([4_572_721])
12
+ expect(sqids.encode([4_572_721])).to eq('JExTR')
13
13
  end
14
14
 
15
15
  it 'does not use any blocklist if an empty blocklist is provided' do
16
16
  sqids = Sqids.new(blocklist: Set.new([]))
17
17
 
18
- expect(sqids.decode('sexy')).to eq([200_044])
19
- expect(sqids.encode([200_044])).to eq('sexy')
18
+ expect(sqids.decode('aho1e')).to eq([4_572_721])
19
+ expect(sqids.encode([4_572_721])).to eq('aho1e')
20
20
  end
21
21
 
22
22
  it 'uses provided blocklist if non-empty blocklist is provided' do
23
- sqids = Sqids.new(blocklist: Set.new(['AvTg']))
23
+ sqids = Sqids.new(blocklist: Set.new(['ArUO']))
24
24
 
25
- expect(sqids.decode('sexy')).to eq([200_044])
26
- expect(sqids.encode([200_044])).to eq('sexy')
25
+ expect(sqids.decode('aho1e')).to eq([4_572_721])
26
+ expect(sqids.encode([4_572_721])).to eq('aho1e')
27
27
 
28
- expect(sqids.decode('AvTg')).to eq([100_000])
29
- expect(sqids.encode([100_000])).to eq('7T1X8k')
30
- expect(sqids.decode('7T1X8k')).to eq([100_000])
28
+ expect(sqids.decode('ArUO')).to eq([100_000])
29
+ expect(sqids.encode([100_000])).to eq('QyG4')
30
+ expect(sqids.decode('QyG4')).to eq([100_000])
31
31
  end
32
32
 
33
33
  it 'uses blocklist to prevent certain encodings' do
34
- sqids = Sqids.new(blocklist: Set.new(%w[8QRLaD 7T1cd0dL UeIe imhw LfUQ]))
34
+ sqids = Sqids.new(blocklist: Set.new(%w[JSwXFaosAN OCjV9JK64o rBHf 79SM 7tE6]))
35
35
 
36
- expect(sqids.encode([1, 2, 3])).to eq('TM0x1Mxz')
37
- expect(sqids.decode('TM0x1Mxz')).to eq([1, 2, 3])
36
+ expect(sqids.encode([1_000_000, 2_000_000])).to eq('1aYeB7bRUt')
37
+ expect(sqids.decode('1aYeB7bRUt')).to eq([1_000_000, 2_000_000])
38
38
  end
39
39
 
40
40
  it 'can decode blocklist words' do
41
- sqids = Sqids.new(blocklist: Set.new(%w[8QRLaD 7T1cd0dL RA8UeIe7 WM3Limhw LfUQh4HN]))
41
+ sqids = Sqids.new(blocklist: Set.new(%w[86Rf07 se8ojk ARsz1p Q8AI49 5sQRZO]))
42
42
 
43
- expect(sqids.decode('8QRLaD')).to eq([1, 2, 3])
44
- expect(sqids.decode('7T1cd0dL')).to eq([1, 2, 3])
45
- expect(sqids.decode('RA8UeIe7')).to eq([1, 2, 3])
46
- expect(sqids.decode('WM3Limhw')).to eq([1, 2, 3])
47
- expect(sqids.decode('LfUQh4HN')).to eq([1, 2, 3])
43
+ expect(sqids.decode('86Rf07')).to eq([1, 2, 3])
44
+ expect(sqids.decode('se8ojk')).to eq([1, 2, 3])
45
+ expect(sqids.decode('ARsz1p')).to eq([1, 2, 3])
46
+ expect(sqids.decode('Q8AI49')).to eq([1, 2, 3])
47
+ expect(sqids.decode('5sQRZO')).to eq([1, 2, 3])
48
48
  end
49
49
 
50
50
  it 'matches against a short blocklist word' do
51
- sqids = Sqids.new(blocklist: Set.new(['pPQ']))
51
+ sqids = Sqids.new(blocklist: Set.new(['pnd']))
52
52
 
53
53
  expect(sqids.decode(sqids.encode([1_000]))).to eq([1_000])
54
54
  end
55
55
 
56
56
  it 'blocklist filtering in constructor' do
57
57
  # lowercase blocklist in only-uppercase alphabet
58
- sqids = Sqids.new(alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', blocklist: Set.new(['sqnmpn']))
58
+ sqids = Sqids.new(alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', blocklist: Set.new(['sxnzkl']))
59
59
 
60
60
  id = sqids.encode([1, 2, 3])
61
61
  numbers = sqids.decode(id)
62
62
 
63
- expect(id).to eq('ULPBZGBM') # without blocklist, would've been "SQNMPN"
63
+ expect(id).to eq('IBSHOZ') # without blocklist, would've been "SXNZKL"
64
64
  expect(numbers).to eq([1, 2, 3])
65
65
  end
66
+
67
+ it 'max encoding attempts' do
68
+ alphabet = 'abc'
69
+ min_length = 3
70
+ blocklist = Set.new(%w[cab abc bca])
71
+
72
+ sqids = Sqids.new(alphabet: alphabet, min_length: min_length, blocklist: blocklist)
73
+
74
+ expect(min_length).to eq(alphabet.length)
75
+ expect(min_length).to eq(blocklist.size)
76
+
77
+ expect do
78
+ sqids.encode([0])
79
+ end.to raise_error(ArgumentError)
80
+ end
66
81
  end
@@ -8,7 +8,7 @@ describe 'Sqids' do
8
8
  sqids = Sqids.new
9
9
 
10
10
  numbers = [1, 2, 3]
11
- id = '8QRLaD'
11
+ id = '86Rf07'
12
12
 
13
13
  expect(sqids.encode(numbers)).to eq(id)
14
14
  expect(sqids.decode(id)).to eq(numbers)
@@ -25,16 +25,16 @@ describe 'Sqids' do
25
25
  sqids = Sqids.new
26
26
 
27
27
  ids = {
28
- 'bV' => [0],
29
- 'U9' => [1],
30
- 'g8' => [2],
31
- 'Ez' => [3],
32
- 'V8' => [4],
33
- 'ul' => [5],
34
- 'O3' => [6],
35
- 'AF' => [7],
36
- 'ph' => [8],
37
- 'n8' => [9]
28
+ 'bM' => [0],
29
+ 'Uk' => [1],
30
+ 'gb' => [2],
31
+ 'Ef' => [3],
32
+ 'Vq' => [4],
33
+ 'uw' => [5],
34
+ 'OI' => [6],
35
+ 'AX' => [7],
36
+ 'p6' => [8],
37
+ 'nJ' => [9]
38
38
  }
39
39
 
40
40
  ids.each do |id, numbers|
@@ -47,16 +47,16 @@ describe 'Sqids' do
47
47
  sqids = Sqids.new
48
48
 
49
49
  ids = {
50
- 'SrIu' => [0, 0],
51
- 'nZqE' => [0, 1],
52
- 'tJyf' => [0, 2],
53
- 'e86S' => [0, 3],
54
- 'rtC7' => [0, 4],
55
- 'sQ8R' => [0, 5],
56
- 'uz2n' => [0, 6],
57
- '7Td9' => [0, 7],
58
- '3nWE' => [0, 8],
59
- 'mIxM' => [0, 9]
50
+ 'SvIz' => [0, 0],
51
+ 'n3qa' => [0, 1],
52
+ 'tryF' => [0, 2],
53
+ 'eg6q' => [0, 3],
54
+ 'rSCF' => [0, 4],
55
+ 'sR8x' => [0, 5],
56
+ 'uY2M' => [0, 6],
57
+ '74dI' => [0, 7],
58
+ '30WX' => [0, 8],
59
+ 'moxr' => [0, 9]
60
60
  }
61
61
 
62
62
  ids.each do |id, numbers|
@@ -69,16 +69,16 @@ describe 'Sqids' do
69
69
  sqids = Sqids.new
70
70
 
71
71
  ids = {
72
- 'SrIu' => [0, 0],
73
- 'nbqh' => [1, 0],
74
- 't4yj' => [2, 0],
75
- 'eQ6L' => [3, 0],
76
- 'r4Cc' => [4, 0],
77
- 'sL82' => [5, 0],
78
- 'uo2f' => [6, 0],
79
- '7Zdq' => [7, 0],
80
- '36Wf' => [8, 0],
81
- 'm4xT' => [9, 0]
72
+ 'SvIz' => [0, 0],
73
+ 'nWqP' => [1, 0],
74
+ 'tSyw' => [2, 0],
75
+ 'eX68' => [3, 0],
76
+ 'rxCY' => [4, 0],
77
+ 'sV8a' => [5, 0],
78
+ 'uf2K' => [6, 0],
79
+ '7Cdk' => [7, 0],
80
+ '3aWP' => [8, 0],
81
+ 'm2xn' => [9, 0]
82
82
  }
83
83
 
84
84
  ids.each do |id, numbers|
@@ -110,14 +110,9 @@ describe 'Sqids' do
110
110
  expect(sqids.decode('*')).to eq([])
111
111
  end
112
112
 
113
- it 'decoding an invalid ID with a repeating reserved character' do
114
- sqids = Sqids.new
115
- expect(sqids.decode('fff')).to eq([])
116
- end
117
-
118
113
  it 'encode out-of-range numbers' do
119
114
  sqids = Sqids.new
120
- expect { sqids.encode([Sqids.min_value - 1]) }.to raise_error(ArgumentError)
115
+ expect { sqids.encode([-1]) }.to raise_error(ArgumentError)
121
116
  expect { sqids.encode([Sqids.max_value + 1]) }.to raise_error(ArgumentError)
122
117
  end
123
118
  end
@@ -10,26 +10,52 @@ describe Sqids do
10
10
  sqids = Sqids.new(min_length: default_alphabet.length)
11
11
 
12
12
  numbers = [1, 2, 3]
13
- id = '75JILToVsGerOADWmHlY38xvbaNZKQ9wdFS0B6kcMEtnRpgizhjU42qT1cd0dL'
13
+ id = '86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTM'
14
14
 
15
15
  expect(sqids.encode(numbers)).to eq(id)
16
16
  expect(sqids.decode(id)).to eq(numbers)
17
17
  end
18
18
 
19
+ it 'encodes and decodes incremental' do
20
+ numbers = [1, 2, 3]
21
+ map = {
22
+ 6 => '86Rf07',
23
+ 7 => '86Rf07x',
24
+ 8 => '86Rf07xd',
25
+ 9 => '86Rf07xd4',
26
+ 10 => '86Rf07xd4z',
27
+ 11 => '86Rf07xd4zB',
28
+ 12 => '86Rf07xd4zBm',
29
+ 13 => '86Rf07xd4zBmi'
30
+ }
31
+ map[default_alphabet.length + 0] = '86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTM'
32
+ map[default_alphabet.length + 1] = '86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMy'
33
+ map[default_alphabet.length + 2] = '86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMyf'
34
+ map[default_alphabet.length + 3] = '86Rf07xd4zBmiJXQG6otHEbew02c3PWsUOLZxADhCpKj7aVFv9I8RquYrNlSTMyf1'
35
+
36
+ map.each do |min_length, id|
37
+ sqids = Sqids.new(min_length: min_length)
38
+
39
+ expect(sqids.encode(numbers)).to eq(id)
40
+ expect(sqids.encode(numbers).length).to eq(min_length)
41
+ expect(sqids.decode(id)).to eq(numbers)
42
+ end
43
+ end
44
+
19
45
  it 'encodes and decodes incremental numbers' do
20
46
  sqids = Sqids.new(min_length: default_alphabet.length)
21
47
 
22
48
  ids = {
23
- 'jf26PLNeO5WbJDUV7FmMtlGXps3CoqkHnZ8cYd19yIiTAQuvKSExzhrRghBlwf' => [0, 0],
24
- 'vQLUq7zWXC6k9cNOtgJ2ZK8rbxuipBFAS10yTdYeRa3ojHwGnmMV4PDhESI2jL' => [0, 1],
25
- 'YhcpVK3COXbifmnZoLuxWgBQwtjsSaDGAdr0ReTHM16yI9vU8JNzlFq5Eu2oPp' => [0, 2],
26
- 'OTkn9daFgDZX6LbmfxI83RSKetJu0APihlsrYoz5pvQw7GyWHEUcN2jBqd4kJ9' => [0, 3],
27
- 'h2cV5eLNYj1x4ToZpfM90UlgHBOKikQFvnW36AC8zrmuJ7XdRytIGPawqYEbBe' => [0, 4],
28
- '7Mf0HeUNkpsZOTvmcj836P9EWKaACBubInFJtwXR2DSzgYGhQV5i4lLxoT1qdU' => [0, 5],
29
- 'APVSD1ZIY4WGBK75xktMfTev8qsCJw6oyH2j3OnLcXRlhziUmpbuNEar05QCsI' => [0, 6],
30
- 'P0LUhnlT76rsWSofOeyRGQZv1cC5qu3dtaJYNEXwk8Vpx92bKiHIz4MgmiDOF7' => [0, 7],
31
- 'xAhypZMXYIGCL4uW0te6lsFHaPc3SiD1TBgw5O7bvodzjqUn89JQRfk2Nvm4JI' => [0, 8],
32
- '94dRPIZ6irlXWvTbKywFuAhBoECQOVMjDJp53s2xeqaSzHY8nc17tmkLGwfGNl' => [0, 9]
49
+ 'SvIzsqYMyQwI3GWgJAe17URxX8V924Co0DaTZLtFjHriEn5bPhcSkfmvOslpBu' => [0, 0],
50
+ 'n3qafPOLKdfHpuNw3M61r95svbeJGk7aAEgYn4WlSjXURmF8IDqZBy0CT2VxQc' => [0, 1],
51
+ 'tryFJbWcFMiYPg8sASm51uIV93GXTnvRzyfLleh06CpodJD42B7OraKtkQNxUZ' => [0, 2],
52
+ 'eg6ql0A3XmvPoCzMlB6DraNGcWSIy5VR8iYup2Qk4tjZFKe1hbwfgHdUTsnLqE' => [0, 3],
53
+ 'rSCFlp0rB2inEljaRdxKt7FkIbODSf8wYgTsZM1HL9JzN35cyoqueUvVWCm4hX' => [0, 4],
54
+ 'sR8xjC8WQkOwo74PnglH1YFdTI0eaf56RGVSitzbjuZ3shNUXBrqLxEJyAmKv2' => [0, 5],
55
+ 'uY2MYFqCLpgx5XQcjdtZK286AwWV7IBGEfuS9yTmbJvkzoUPeYRHr4iDs3naN0' => [0, 6],
56
+ '74dID7X28VLQhBlnGmjZrec5wTA1fqpWtK4YkaoEIM9SRNiC3gUJH0OFvsPDdy' => [0, 7],
57
+ '30WXpesPhgKiEI5RHTY7xbB1GnytJvXOl2p0AcUjdF6waZDo9Qk8VLzMuWrqCS' => [0, 8],
58
+ 'moxr3HqLAK0GsTND6jowfZz3SUx7cQ8aC54Pl1RbIvFXmEJuBMYVeW9yrdOtin' => [0, 9]
33
59
  }
34
60
 
35
61
  ids.each do |id, numbers|
@@ -41,7 +67,7 @@ describe Sqids do
41
67
  it 'encodes with different min lengths' do
42
68
  [0, 1, 5, 10, default_alphabet.length].each do |min_length|
43
69
  [
44
- [Sqids.min_value],
70
+ [0],
45
71
  [0, 0, 0, 0, 0],
46
72
  [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
47
73
  [100, 200, 300],
@@ -60,6 +86,6 @@ describe Sqids do
60
86
 
61
87
  it 'raises error for out-of-range invalid min lengths' do
62
88
  expect { Sqids.new(min_length: -1) }.to raise_error(TypeError)
63
- expect { Sqids.new(min_length: default_alphabet.length + 1) }.to raise_error(TypeError)
89
+ expect { Sqids.new(min_length: 256) }.to raise_error(TypeError)
64
90
  end
65
91
  end
data/spec/uniques_spec.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  require 'rspec'
4
4
  require_relative '../lib/sqids'
5
5
 
6
- upper = 10_000
6
+ upper = 1_000
7
7
 
8
8
  describe Sqids do
9
9
  let(:sqids) { Sqids.new(min_length: Sqids::DEFAULT_ALPHABET.length) }
data/sqids.gemspec CHANGED
@@ -7,7 +7,7 @@ require 'sqids'
7
7
 
8
8
  Gem::Specification.new do |gem|
9
9
  gem.name = 'sqids'
10
- gem.version = '0.1.2'
10
+ gem.version = '0.2.0'
11
11
  gem.authors = ['Sqids Maintainers']
12
12
  gem.summary = 'Generate YouTube-like ids from numbers.'
13
13
  gem.homepage = 'https://sqids.org/ruby'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sqids
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sqids Maintainers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-08-31 00:00:00.000000000 Z
11
+ date: 2023-09-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email: