sqids 0.1.2 → 0.2.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: 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: