base58_id 1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 350af1c45ca377d67a9d8223e069a2fe4d7578ba2a1428f54fa0ed9dfc3935e0
4
+ data.tar.gz: bb0a090804ba8890d5d3c1a851932dd137ff64985b806c01736c1b3c9b63fe0a
5
+ SHA512:
6
+ metadata.gz: 39220e72e36409269d9d6d6dc74bf54a6aef3c40a45096f049190e3e89a0b68df150e6ab843deadb951b99c6e630981a29b05db18a757293845caeaea938a2b6
7
+ data.tar.gz: 5450dc9d8a03b82716ba14c8bb9000654b80918200586b10c1ba1211626962d73fbb9a0f698ec8098e6c6951608373537cdb746790130034e7a816e87be4cca6
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ coverage/
2
+ *.gem
data/.rubocop.yml ADDED
@@ -0,0 +1,49 @@
1
+ require:
2
+ - rubocop-performance
3
+ - rubocop-rspec
4
+
5
+ AllCops:
6
+ TargetRubyVersion: 2.7
7
+ NewCops: enable
8
+
9
+ Metrics/BlockLength:
10
+ Exclude:
11
+ - 'spec/**/*.rb'
12
+ Metrics/MethodLength:
13
+ Exclude:
14
+ - 'spec/**/*.rb'
15
+
16
+ Layout/LineLength:
17
+ Max: 120
18
+ Layout/HashAlignment:
19
+ Enabled: false
20
+ Layout/SpaceInsideArrayPercentLiteral:
21
+ Enabled: false
22
+ Layout/ArrayAlignment:
23
+ Enabled: false
24
+
25
+ Style/Documentation:
26
+ Enabled: false
27
+ Style/WhileUntilModifier:
28
+ Enabled: false
29
+ Style/WordArray:
30
+ Enabled: false
31
+ Style/SymbolArray:
32
+ Enabled: false
33
+ Style/NumericLiterals:
34
+ Enabled: false
35
+ Style/StringLiterals:
36
+ Enabled: false
37
+ Style/TrailingCommaInArrayLiteral:
38
+ Enabled: false
39
+ Style/TrailingCommaInHashLiteral:
40
+ Enabled: false
41
+
42
+ RSpec/ExampleLength:
43
+ Enabled: false
44
+ RSpec/MultipleExpectations:
45
+ Enabled: false
46
+ RSpec/NestedGroups:
47
+ Enabled: false
48
+ RSpec/LetBeforeExamples:
49
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Elias Rodrigues
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,129 @@
1
+ # Base58Id
2
+
3
+ Convert an Integer ID or a UUID String to/from Base58 String
4
+
5
+ ## Install
6
+
7
+ Install it from rubygems.org in your terminal:
8
+
9
+ ```sh
10
+ gem install base58_id
11
+ ```
12
+
13
+ Or via `Gemfile` in your project:
14
+
15
+ ```sh
16
+ source 'https://rubygems.org'
17
+
18
+ gem 'base58_id', '~> 1.0'
19
+ ```
20
+
21
+ Or build and install the gem locally:
22
+
23
+ ```sh
24
+ gem build base58_id.gemspec
25
+ gem install base58_id-1.0.0.gem
26
+ ```
27
+
28
+ Require it in your Ruby code and the `Base58Id` class will be available:
29
+
30
+ ```rb
31
+ require 'base58_id'
32
+ ```
33
+
34
+ ## Alphabet
35
+
36
+ It is based on Base64 URL-safe alphabet but with `I`, `O`, `l`, `0`, `-`, `_` removed:
37
+
38
+ ```
39
+ A B C D E F G H J K L M N P Q R S T U V W X Y Z
40
+ a b c d e f g h i j k m n o p q r s t u v w x y z
41
+ 1 2 3 4 5 6 7 8 9
42
+ ```
43
+
44
+ ## Example
45
+
46
+ ```rb
47
+ require 'base58_id'
48
+
49
+ Base58Id.uuid_to_base58('058917af-0d2e-4755-b0bd-25b02b249824')
50
+ # => "qocqGYw9LEfu4WVujMzJv"
51
+
52
+ Base58Id.base58_to_uuid('qocqGYw9LEfu4WVujMzJv')
53
+ # => "058917af-0d2e-4755-b0bd-25b02b249824"
54
+
55
+ Base58Id.uuid_to_integer('058917af-0d2e-4755-b0bd-25b02b249824')
56
+ # => 7357965012972427318415208560898381860
57
+
58
+ Base58Id.integer_to_uuid(7357965012972427318415208560898381860)
59
+ # => "058917af-0d2e-4755-b0bd-25b02b249824"
60
+
61
+ Base58Id.base58_to_integer('qocqGYw9LEfu4WVujMzJv')
62
+ # => 7357965012972427318415208560898381860
63
+
64
+ Base58Id.integer_to_base58(7357965012972427318415208560898381860)
65
+ # => "qocqGYw9LEfu4WVujMzJv"
66
+
67
+ Base58Id.valid_base58?('qocqGYw9LEfu4WVujMzJv')
68
+ # => true
69
+
70
+ Base58Id.valid_base58?('IOl0-_')
71
+ # => false
72
+
73
+ # It accepts UUID String in many formats:
74
+ [
75
+ '058917af0d2e4755b0bd25b02b249824',
76
+ '058917AF0D2E4755B0BD25B02B249824',
77
+ '0x058917af0d2e4755b0bd25b02b249824',
78
+ ].map { |uuid| Base58Id.valid_uuid?(uuid) }
79
+ # => [true, true, true]
80
+
81
+ Base58Id::UUID_PATTERN
82
+ # => /\A(0x)?[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}\z/i
83
+ ```
84
+
85
+ ### Note
86
+
87
+ As it performs an integer conversion, Base58 leading zeros (represented by `A`)
88
+ are ignored/lost:
89
+
90
+ ```rb
91
+ Base58Id.base58_to_integer('A') == Base58Id.base58_to_integer('AAAAAA')
92
+ # => true
93
+
94
+ Base58Id.base58_to_integer('AAAAAAqocqGYw9LEfu4WVujMzJv') ==
95
+ Base58Id.base58_to_integer('qocqGYw9LEfu4WVujMzJv')
96
+ # => true
97
+
98
+ Base58Id.integer_to_base58(Base58Id.base58_to_integer('AAAAAAqocqGYw9LEfu4WVujMzJv'))
99
+ # => "qocqGYw9LEfu4WVujMzJv"
100
+ ```
101
+
102
+ And an empty Base58 String also represents zero:
103
+
104
+ ```rb
105
+ Base58Id.base58_to_integer('')
106
+ # => 0
107
+
108
+ Base58Id.base58_to_uuid('')
109
+ # => "00000000-0000-0000-0000-000000000000"
110
+
111
+ Base58Id.base58_to_integer('') == Base58Id.base58_to_integer('A')
112
+ # => true
113
+ ```
114
+
115
+ ## Tests
116
+
117
+ Run tests with:
118
+
119
+ ```sh
120
+ bundle exec rspec
121
+ ```
122
+
123
+ ## Linter
124
+
125
+ Check your code with:
126
+
127
+ ```sh
128
+ bundle exec rubocop
129
+ ```
data/base58_id.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/base58_id/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'base58_id'
7
+ s.version = Base58Id::VERSION
8
+ s.license = 'MIT'
9
+ s.summary = 'Convert an Integer ID or a UUID String to/from Base58 String'
10
+ s.homepage = 'https://github.com/elias19r/base58_id'
11
+ s.author = 'Elias Rodrigues'
12
+
13
+ s.files = Dir[
14
+ 'lib/**/*',
15
+ 'spec/**/*',
16
+ '.gitignore',
17
+ '.rubocop.yml',
18
+ 'Gemfile',
19
+ 'LICENSE',
20
+ 'README.md',
21
+ 'base58_id.gemspec'
22
+ ]
23
+
24
+ s.required_ruby_version = '>= 2.7'
25
+ s.metadata = {
26
+ 'source_code_uri' => "https://github.com/elias19r/base58_id/tree/v#{Base58Id::VERSION}",
27
+ 'rubygems_mfa_required' => 'true'
28
+ }
29
+
30
+ s.add_development_dependency 'bundler', '~> 2.1'
31
+ s.add_development_dependency 'pry-byebug', '~> 3.9'
32
+ s.add_development_dependency 'rspec', '~> 3.11'
33
+ s.add_development_dependency 'rubocop', '~> 1.31'
34
+ s.add_development_dependency 'rubocop-performance', '~> 1.14'
35
+ s.add_development_dependency 'rubocop-rspec', '~> 2.11'
36
+ s.add_development_dependency 'simplecov', '~> 0.21.0'
37
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Base58Id
4
+ VERSION = '1.0.0'
5
+ end
data/lib/base58_id.rb ADDED
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Base58Id
4
+ # Based on Base64 URL-safe alphabet but with I, O, l, 0, -, _ removed.
5
+ ALPHABET_58 = %w[
6
+ A B C D E F G H J K L M N P Q R S T U V W X Y Z
7
+ a b c d e f g h i j k m n o p q r s t u v w x y z
8
+ 1 2 3 4 5 6 7 8 9
9
+ ].freeze
10
+
11
+ ALPHABET_58_INVERT = ALPHABET_58.each_with_index.to_h
12
+ ALPHABET_58_CHARS = ALPHABET_58.join
13
+ CARET_ALPHABET_58_CHARS = "^#{ALPHABET_58_CHARS}"
14
+
15
+ UUID_PATTERN = /\A(0x)?[0-9a-f]{8}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{4}-?[0-9a-f]{12}\z/i.freeze
16
+ UUID_BYTES_FORMAT = '%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x'
17
+
18
+ def self.integer_to_base58(integer)
19
+ raise ArgumentError, 'argument must be an Integer' unless integer.is_a?(Integer)
20
+ raise ArgumentError, 'argument must be greater than or equal to zero' if integer.negative?
21
+
22
+ integer.digits(58).reduce('') { |str, digit| ALPHABET_58[digit] + str }
23
+ end
24
+
25
+ def self.base58_to_integer(base58)
26
+ raise ArgumentError, 'argument must be a valid Base58 String' unless valid_base58?(base58)
27
+
28
+ base58.chars.reduce(0) { |integer, digit| (integer * 58) + ALPHABET_58_INVERT[digit] }
29
+ end
30
+
31
+ def self.uuid_to_base58(uuid)
32
+ raise ArgumentError, 'argument must be a valid UUID String' unless valid_uuid?(uuid)
33
+
34
+ integer_to_base58(uuid_to_integer(uuid))
35
+ end
36
+
37
+ def self.base58_to_uuid(base58)
38
+ integer_to_uuid(base58_to_integer(base58))
39
+ end
40
+
41
+ def self.uuid_to_integer(uuid)
42
+ raise ArgumentError, 'argument must be a valid UUID String' unless valid_uuid?(uuid)
43
+
44
+ uuid.delete('-').to_i(16)
45
+ end
46
+
47
+ def self.integer_to_uuid(integer)
48
+ raise ArgumentError, 'argument must be an Integer' unless integer.is_a?(Integer)
49
+ raise ArgumentError, 'argument must be greater than or equal to zero' if integer.negative?
50
+
51
+ bytes_array = integer.digits(256)
52
+
53
+ raise ArgumentError, 'argument size must not require more than 16 bytes' if bytes_array.size > 16
54
+
55
+ while bytes_array.size < 16
56
+ bytes_array.push(0)
57
+ end
58
+
59
+ UUID_BYTES_FORMAT % bytes_array.reverse
60
+ end
61
+
62
+ def self.valid_base58?(value)
63
+ raise ArgumentError, 'argument must be a String' unless value.is_a?(String)
64
+
65
+ value.count(CARET_ALPHABET_58_CHARS).zero?
66
+ end
67
+
68
+ def self.valid_uuid?(value)
69
+ raise ArgumentError, 'argument must be a String' unless value.is_a?(String)
70
+
71
+ value.match?(UUID_PATTERN)
72
+ end
73
+ end
@@ -0,0 +1,494 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'base58_id'
5
+
6
+ RSpec.describe Base58Id do
7
+ it 'converts an Integer ID or a UUID String to/from Base58 String' do
8
+ base58 = generate_base58
9
+
10
+ expect(
11
+ described_class.integer_to_base58(
12
+ described_class.base58_to_integer(base58)
13
+ )
14
+ ).to eq(base58)
15
+
16
+ expect(
17
+ described_class.uuid_to_base58(
18
+ described_class.base58_to_uuid(base58)
19
+ )
20
+ ).to eq(base58)
21
+
22
+ base58_with_leading_zeros = "AAAAAAAA#{base58}"
23
+
24
+ expect(
25
+ described_class.integer_to_base58(
26
+ described_class.base58_to_integer(base58_with_leading_zeros)
27
+ )
28
+ ).to eq(base58)
29
+
30
+ expect(
31
+ described_class.uuid_to_base58(
32
+ described_class.base58_to_uuid(base58_with_leading_zeros)
33
+ )
34
+ ).to eq(base58)
35
+
36
+ base58_empty = ''
37
+
38
+ expect(
39
+ described_class.integer_to_base58(
40
+ described_class.base58_to_integer(base58_empty)
41
+ )
42
+ ).to eq('A')
43
+
44
+ expect(
45
+ described_class.uuid_to_base58(
46
+ described_class.base58_to_uuid(base58_empty)
47
+ )
48
+ ).to eq('A')
49
+
50
+ integer = generate_non_negative_integer
51
+
52
+ expect(
53
+ described_class.base58_to_integer(
54
+ described_class.integer_to_base58(integer)
55
+ )
56
+ ).to eq(integer)
57
+
58
+ expect(
59
+ described_class.uuid_to_integer(
60
+ described_class.integer_to_uuid(integer)
61
+ )
62
+ ).to eq(integer)
63
+
64
+ uuid = generate_uuid
65
+
66
+ expect(
67
+ described_class.base58_to_uuid(
68
+ described_class.uuid_to_base58(uuid)
69
+ )
70
+ ).to eq(uuid)
71
+
72
+ expect(
73
+ described_class.integer_to_uuid(
74
+ described_class.uuid_to_integer(uuid)
75
+ )
76
+ ).to eq(uuid)
77
+ end
78
+
79
+ describe '.integer_to_base58' do
80
+ context 'when argument is not an Integer' do
81
+ it 'raises an ArgumentError' do
82
+ non_integer = Object.new
83
+
84
+ expect do
85
+ described_class.integer_to_base58(non_integer)
86
+ end.to raise_error(ArgumentError, 'argument must be an Integer')
87
+ end
88
+ end
89
+
90
+ context 'when argument is an Integer but negative' do
91
+ it 'raises an ArgumentError' do
92
+ negative_integer = generate_negative_integer
93
+
94
+ expect do
95
+ described_class.integer_to_base58(negative_integer)
96
+ end.to raise_error(ArgumentError, 'argument must be greater than or equal to zero')
97
+ end
98
+ end
99
+
100
+ context 'when argument is a non-negative Integer' do
101
+ it 'formats unsigned integer as Base58 String' do
102
+ integer = generate_non_negative_integer
103
+
104
+ expect(described_class.integer_to_base58(integer).count("^#{base58_chars.join}")).to eq(0)
105
+
106
+ some_test_cases.each do |test_case|
107
+ expect(described_class.integer_to_base58(test_case[:integer])).to eq(test_case[:base58])
108
+ end
109
+ end
110
+ end
111
+ end
112
+
113
+ describe '.base58_to_integer' do
114
+ context 'when value is not a valid Base58 String' do
115
+ it 'raises an ArgumentError' do
116
+ non_string = Object.new
117
+
118
+ expect do
119
+ described_class.base58_to_integer(non_string)
120
+ end.to raise_error(ArgumentError, 'argument must be a String')
121
+
122
+ non_base58_chars.each do |non_base58|
123
+ expect do
124
+ described_class.base58_to_integer(non_base58)
125
+ end.to raise_error(ArgumentError, 'argument must be a valid Base58 String')
126
+ end
127
+ end
128
+ end
129
+
130
+ context 'when value is a valid Base58 String' do
131
+ it 'returns the corresponding Integer' do
132
+ big_base58 = generate_big_base58
133
+
134
+ expect(described_class.base58_to_integer(big_base58)).to be_an(Integer)
135
+
136
+ base58_empty = ''
137
+
138
+ expect(described_class.base58_to_integer(base58_empty)).to eq(0)
139
+
140
+ some_test_cases.each do |test_case|
141
+ expect(described_class.base58_to_integer(test_case[:base58])).to eq(test_case[:integer])
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ describe '.uuid_to_base58' do
148
+ context 'when argument is not a valid UUID String' do
149
+ it 'raises an ArgumentError' do
150
+ non_string = Object.new
151
+
152
+ expect do
153
+ described_class.uuid_to_base58(non_string)
154
+ end.to raise_error(ArgumentError, 'argument must be a String')
155
+
156
+ non_uuid = '123'
157
+
158
+ expect do
159
+ described_class.uuid_to_base58(non_uuid)
160
+ end.to raise_error(ArgumentError, 'argument must be a valid UUID String')
161
+ end
162
+ end
163
+
164
+ context 'when argument is a valid UUID String' do
165
+ it 'returns the corresponding Base58' do
166
+ uuid = generate_uuid
167
+
168
+ expect(described_class.uuid_to_base58(uuid).count("^#{base58_chars.join}")).to eq(0)
169
+
170
+ some_test_cases.each do |test_case|
171
+ expect(described_class.uuid_to_base58(test_case[:uuid])).to eq(test_case[:base58])
172
+ end
173
+ end
174
+ end
175
+ end
176
+
177
+ describe '.base58_to_uuid' do
178
+ context 'when value is not a valid Base58 String' do
179
+ it 'raises an ArgumentError' do
180
+ non_string = Object.new
181
+
182
+ expect do
183
+ described_class.base58_to_uuid(non_string)
184
+ end.to raise_error(ArgumentError, 'argument must be a String')
185
+
186
+ non_base58_chars.each do |non_base58|
187
+ expect do
188
+ described_class.base58_to_uuid(non_base58)
189
+ end.to raise_error(ArgumentError, 'argument must be a valid Base58 String')
190
+ end
191
+ end
192
+ end
193
+
194
+ context 'when value is a valid Base58 String but numeric value requires more than 16 bytes' do
195
+ it 'raises an ArgumentError' do
196
+ big_base58 = generate_big_base58
197
+
198
+ expect do
199
+ described_class.base58_to_uuid(big_base58)
200
+ end.to raise_error(ArgumentError, 'argument size must not require more than 16 bytes')
201
+ end
202
+ end
203
+
204
+ context 'when value is a valid Base58 String and numeric value does not require more than 16 bytes' do
205
+ it 'returns the corresponding UUID String' do
206
+ base58 = generate_base58
207
+
208
+ expect(described_class.base58_to_uuid(base58))
209
+ .to match(/\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/)
210
+
211
+ base58_empty = ''
212
+
213
+ expect(described_class.base58_to_uuid(base58_empty)).to eq('00000000-0000-0000-0000-000000000000')
214
+
215
+ some_test_cases.each do |test_case|
216
+ expect(described_class.base58_to_uuid(test_case[:base58])).to eq(test_case[:uuid])
217
+ end
218
+ end
219
+ end
220
+ end
221
+
222
+ describe '.uuid_to_integer' do
223
+ context 'when argument is not a valid UUID String' do
224
+ it 'raises an ArgumentError' do
225
+ non_string = Object.new
226
+
227
+ expect do
228
+ described_class.uuid_to_integer(non_string)
229
+ end.to raise_error(ArgumentError, 'argument must be a String')
230
+
231
+ non_uuid = '123'
232
+
233
+ expect do
234
+ described_class.uuid_to_integer(non_uuid)
235
+ end.to raise_error(ArgumentError, 'argument must be a valid UUID String')
236
+ end
237
+ end
238
+
239
+ context 'when argument is a valid UUID String' do
240
+ it 'returns the corresponding Integer' do
241
+ uuid = generate_uuid
242
+
243
+ expect(described_class.uuid_to_integer(uuid)).to be_an(Integer)
244
+
245
+ some_test_cases.each do |test_case|
246
+ expect(described_class.uuid_to_integer(test_case[:uuid])).to eq(test_case[:integer])
247
+ end
248
+ end
249
+ end
250
+ end
251
+
252
+ describe '.integer_to_uuid' do
253
+ context 'when argument is not an Integer' do
254
+ it 'raises an ArgumentError' do
255
+ non_integer = Object.new
256
+
257
+ expect do
258
+ described_class.integer_to_uuid(non_integer)
259
+ end.to raise_error(ArgumentError, 'argument must be an Integer')
260
+ end
261
+ end
262
+
263
+ context 'when argument is an Integer but negative' do
264
+ it 'raises an ArgumentError' do
265
+ negative_integer = generate_negative_integer
266
+
267
+ expect do
268
+ described_class.integer_to_uuid(negative_integer)
269
+ end.to raise_error(ArgumentError, 'argument must be greater than or equal to zero')
270
+ end
271
+ end
272
+
273
+ context 'when argument is a non-negative Integer but requires more than 16 bytes' do
274
+ it 'raises an ArgumentError' do
275
+ big_integer = generate_big_integer
276
+
277
+ expect do
278
+ described_class.integer_to_uuid(big_integer)
279
+ end.to raise_error(ArgumentError, 'argument size must not require more than 16 bytes')
280
+ end
281
+ end
282
+
283
+ context 'when argument is a non-negative Integer and does not require more than 16 bytes' do
284
+ it 'formats 128-bit unsigned integer as UUID String' do
285
+ uuid = generate_uuid
286
+ integer = uuid.delete('-').to_i(16)
287
+
288
+ expect(described_class.integer_to_uuid(integer)).to eq(uuid)
289
+
290
+ some_test_cases.each do |test_case|
291
+ expect(described_class.integer_to_uuid(test_case[:integer])).to eq(test_case[:uuid])
292
+ end
293
+ end
294
+ end
295
+ end
296
+
297
+ describe '.valid_base58?' do
298
+ context 'when value is not a String' do
299
+ it 'raises an ArgumentError' do
300
+ value = Object.new
301
+
302
+ expect do
303
+ described_class.valid_base58?(value)
304
+ end.to raise_error(ArgumentError, 'argument must be a String')
305
+ end
306
+ end
307
+
308
+ context 'when value is a String but contains chars not present in the Base58 alphabet' do
309
+ it 'returns false' do
310
+ non_base58_chars.each do |value|
311
+ expect(described_class.valid_base58?(value)).to be(false)
312
+ end
313
+ end
314
+ end
315
+
316
+ context 'when value is a String and contains only chars present in the Base58 alphabet' do
317
+ it 'returns true' do
318
+ base58_chars.each do |value|
319
+ expect(described_class.valid_base58?(value)).to be(true)
320
+ end
321
+
322
+ value = ''
323
+
324
+ expect(described_class.valid_base58?(value)).to be(true)
325
+
326
+ value = generate_big_base58
327
+
328
+ expect(described_class.valid_base58?(value)).to be(true)
329
+ end
330
+ end
331
+ end
332
+
333
+ describe '.valid_uuid?' do
334
+ context 'when value is not a String' do
335
+ it 'raises an ArgumentError' do
336
+ value = Object.new
337
+
338
+ expect do
339
+ described_class.valid_uuid?(value)
340
+ end.to raise_error(ArgumentError, 'argument must be a String')
341
+ end
342
+ end
343
+
344
+ context 'when value is a String but does not match the UUID pattern' do
345
+ it 'returns false' do
346
+ value = '123'
347
+
348
+ expect(described_class.valid_uuid?(value)).to be(false)
349
+ end
350
+ end
351
+
352
+ context 'when value is a String and matches the UUID pattern' do
353
+ it 'returns true' do
354
+ value = generate_uuid
355
+
356
+ expect(described_class.valid_uuid?(value)).to be(true)
357
+ expect(described_class.valid_uuid?(value.delete('-'))).to be(true)
358
+ expect(described_class.valid_uuid?("0x#{value.delete('-')}")).to be(true)
359
+ expect(described_class.valid_uuid?("0X#{value.delete('-')}")).to be(true)
360
+
361
+ expect(described_class.valid_uuid?(value.upcase)).to be(true)
362
+ expect(described_class.valid_uuid?(value.upcase.delete('-'))).to be(true)
363
+ expect(described_class.valid_uuid?("0x#{value.upcase.delete('-')}")).to be(true)
364
+ expect(described_class.valid_uuid?("0X#{value.upcase.delete('-')}")).to be(true)
365
+ end
366
+ end
367
+ end
368
+
369
+ def generate_uuid
370
+ SecureRandom.uuid
371
+ end
372
+
373
+ def generate_negative_integer
374
+ rand((-2**63)..-1)
375
+ end
376
+
377
+ def generate_non_negative_integer
378
+ rand(0..((2**63) - 1))
379
+ end
380
+
381
+ def generate_big_integer
382
+ rand((2**128)..(2**144))
383
+ end
384
+
385
+ def generate_base58
386
+ generate_non_zero_base58_digit + base58_chars.sample(19).join
387
+ end
388
+
389
+ def generate_big_base58
390
+ generate_non_zero_base58_digit + base58_chars.sample(24).join
391
+ end
392
+
393
+ def generate_non_zero_base58_digit
394
+ base58_chars[1..].sample
395
+ end
396
+
397
+ let(:base58_chars) do
398
+ %w[
399
+ A B C D E F G H J K L M N P Q R S T U V W X Y Z
400
+ a b c d e f g h i j k m n o p q r s t u v w x y z
401
+ 1 2 3 4 5 6 7 8 9
402
+ ]
403
+ end
404
+
405
+ let(:non_base58_chars) do
406
+ (' '..'~').to_a - base58_chars
407
+ end
408
+
409
+ let(:some_test_cases) do
410
+ [
411
+ { integer: 0, base58: 'A', uuid: '00000000-0000-0000-0000-000000000000' },
412
+ { integer: 1, base58: 'B', uuid: '00000000-0000-0000-0000-000000000001' },
413
+ { integer: 2, base58: 'C', uuid: '00000000-0000-0000-0000-000000000002' },
414
+ { integer: 3, base58: 'D', uuid: '00000000-0000-0000-0000-000000000003' },
415
+ { integer: 4, base58: 'E', uuid: '00000000-0000-0000-0000-000000000004' },
416
+ { integer: 5, base58: 'F', uuid: '00000000-0000-0000-0000-000000000005' },
417
+ { integer: 6, base58: 'G', uuid: '00000000-0000-0000-0000-000000000006' },
418
+ { integer: 7, base58: 'H', uuid: '00000000-0000-0000-0000-000000000007' },
419
+ { integer: 8, base58: 'J', uuid: '00000000-0000-0000-0000-000000000008' },
420
+ { integer: 9, base58: 'K', uuid: '00000000-0000-0000-0000-000000000009' },
421
+ { integer: 10, base58: 'L', uuid: '00000000-0000-0000-0000-00000000000a' },
422
+ { integer: 11, base58: 'M', uuid: '00000000-0000-0000-0000-00000000000b' },
423
+ { integer: 12, base58: 'N', uuid: '00000000-0000-0000-0000-00000000000c' },
424
+ { integer: 13, base58: 'P', uuid: '00000000-0000-0000-0000-00000000000d' },
425
+ { integer: 14, base58: 'Q', uuid: '00000000-0000-0000-0000-00000000000e' },
426
+ { integer: 15, base58: 'R', uuid: '00000000-0000-0000-0000-00000000000f' },
427
+ { integer: 16, base58: 'S', uuid: '00000000-0000-0000-0000-000000000010' },
428
+ { integer: 17, base58: 'T', uuid: '00000000-0000-0000-0000-000000000011' },
429
+ { integer: 18, base58: 'U', uuid: '00000000-0000-0000-0000-000000000012' },
430
+ { integer: 19, base58: 'V', uuid: '00000000-0000-0000-0000-000000000013' },
431
+ { integer: 20, base58: 'W', uuid: '00000000-0000-0000-0000-000000000014' },
432
+ { integer: 21, base58: 'X', uuid: '00000000-0000-0000-0000-000000000015' },
433
+ { integer: 22, base58: 'Y', uuid: '00000000-0000-0000-0000-000000000016' },
434
+ { integer: 23, base58: 'Z', uuid: '00000000-0000-0000-0000-000000000017' },
435
+ { integer: 24, base58: 'a', uuid: '00000000-0000-0000-0000-000000000018' },
436
+ { integer: 25, base58: 'b', uuid: '00000000-0000-0000-0000-000000000019' },
437
+ { integer: 26, base58: 'c', uuid: '00000000-0000-0000-0000-00000000001a' },
438
+ { integer: 27, base58: 'd', uuid: '00000000-0000-0000-0000-00000000001b' },
439
+ { integer: 28, base58: 'e', uuid: '00000000-0000-0000-0000-00000000001c' },
440
+ { integer: 29, base58: 'f', uuid: '00000000-0000-0000-0000-00000000001d' },
441
+ { integer: 30, base58: 'g', uuid: '00000000-0000-0000-0000-00000000001e' },
442
+ { integer: 31, base58: 'h', uuid: '00000000-0000-0000-0000-00000000001f' },
443
+ { integer: 32, base58: 'i', uuid: '00000000-0000-0000-0000-000000000020' },
444
+ { integer: 33, base58: 'j', uuid: '00000000-0000-0000-0000-000000000021' },
445
+ { integer: 34, base58: 'k', uuid: '00000000-0000-0000-0000-000000000022' },
446
+ { integer: 35, base58: 'm', uuid: '00000000-0000-0000-0000-000000000023' },
447
+ { integer: 36, base58: 'n', uuid: '00000000-0000-0000-0000-000000000024' },
448
+ { integer: 37, base58: 'o', uuid: '00000000-0000-0000-0000-000000000025' },
449
+ { integer: 38, base58: 'p', uuid: '00000000-0000-0000-0000-000000000026' },
450
+ { integer: 39, base58: 'q', uuid: '00000000-0000-0000-0000-000000000027' },
451
+ { integer: 40, base58: 'r', uuid: '00000000-0000-0000-0000-000000000028' },
452
+ { integer: 41, base58: 's', uuid: '00000000-0000-0000-0000-000000000029' },
453
+ { integer: 42, base58: 't', uuid: '00000000-0000-0000-0000-00000000002a' },
454
+ { integer: 43, base58: 'u', uuid: '00000000-0000-0000-0000-00000000002b' },
455
+ { integer: 44, base58: 'v', uuid: '00000000-0000-0000-0000-00000000002c' },
456
+ { integer: 45, base58: 'w', uuid: '00000000-0000-0000-0000-00000000002d' },
457
+ { integer: 46, base58: 'x', uuid: '00000000-0000-0000-0000-00000000002e' },
458
+ { integer: 47, base58: 'y', uuid: '00000000-0000-0000-0000-00000000002f' },
459
+ { integer: 48, base58: 'z', uuid: '00000000-0000-0000-0000-000000000030' },
460
+ { integer: 49, base58: '1', uuid: '00000000-0000-0000-0000-000000000031' },
461
+ { integer: 50, base58: '2', uuid: '00000000-0000-0000-0000-000000000032' },
462
+ { integer: 51, base58: '3', uuid: '00000000-0000-0000-0000-000000000033' },
463
+ { integer: 52, base58: '4', uuid: '00000000-0000-0000-0000-000000000034' },
464
+ { integer: 53, base58: '5', uuid: '00000000-0000-0000-0000-000000000035' },
465
+ { integer: 54, base58: '6', uuid: '00000000-0000-0000-0000-000000000036' },
466
+ { integer: 55, base58: '7', uuid: '00000000-0000-0000-0000-000000000037' },
467
+ { integer: 56, base58: '8', uuid: '00000000-0000-0000-0000-000000000038' },
468
+ { integer: 57, base58: '9', uuid: '00000000-0000-0000-0000-000000000039' },
469
+ { integer: 58, base58: 'BA', uuid: '00000000-0000-0000-0000-00000000003a' },
470
+
471
+ { integer: 58 * 2, base58: 'CA', uuid: '00000000-0000-0000-0000-000000000074' },
472
+ { integer: 58 * 3, base58: 'DA', uuid: '00000000-0000-0000-0000-0000000000ae' },
473
+ { integer: 58 * 10, base58: 'LA', uuid: '00000000-0000-0000-0000-000000000244' },
474
+ { integer: 58 * 20, base58: 'WA', uuid: '00000000-0000-0000-0000-000000000488' },
475
+ { integer: 58 * 58, base58: 'BAA', uuid: '00000000-0000-0000-0000-000000000d24' },
476
+
477
+ { integer: 231, base58: 'D9', uuid: '00000000-0000-0000-0000-0000000000e7' },
478
+ { integer: 232, base58: 'EA', uuid: '00000000-0000-0000-0000-0000000000e8' },
479
+ { integer: 255, base58: 'EZ', uuid: '00000000-0000-0000-0000-0000000000ff' },
480
+ { integer: 256, base58: 'Ea', uuid: '00000000-0000-0000-0000-000000000100' },
481
+ { integer: 280, base58: 'Ez', uuid: '00000000-0000-0000-0000-000000000118' },
482
+ { integer: 281, base58: 'E1', uuid: '00000000-0000-0000-0000-000000000119' },
483
+ { integer: 289, base58: 'E9', uuid: '00000000-0000-0000-0000-000000000121' },
484
+ { integer: 290, base58: 'FA', uuid: '00000000-0000-0000-0000-000000000122' },
485
+
486
+ { integer: (58**10) - 1, base58: '9999999999', uuid: '00000000-0000-0000-05fa-8624c7fba3ff' },
487
+ { integer: 58**10, base58: 'BAAAAAAAAAA', uuid: '00000000-0000-0000-05fa-8624c7fba400' },
488
+ { integer: 58**20, base58: 'BAAAAAAAAAAAAAAAAAAAA', uuid: '0023be67-b5f0-f288-9aaf-505301100000' },
489
+
490
+ { integer: (2**128) - 2, base58: 'hmep7uZkFTa9zuEuQB3XV4', uuid: 'ffffffff-ffff-ffff-ffff-fffffffffffe' },
491
+ { integer: (2**128) - 1, base58: 'hmep7uZkFTa9zuEuQB3XV5', uuid: 'ffffffff-ffff-ffff-ffff-ffffffffffff' },
492
+ ]
493
+ end
494
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ require 'securerandom'
7
+ require 'rspec'
8
+ require 'pry-byebug'
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: base58_id
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Elias Rodrigues
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-06-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry-byebug
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.9'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.11'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.11'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.31'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.31'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop-performance
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.14'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.14'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.11'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.11'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.21.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.21.0
111
+ description:
112
+ email:
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - ".gitignore"
118
+ - ".rubocop.yml"
119
+ - Gemfile
120
+ - LICENSE
121
+ - README.md
122
+ - base58_id.gemspec
123
+ - lib/base58_id.rb
124
+ - lib/base58_id/version.rb
125
+ - spec/base58_id_spec.rb
126
+ - spec/spec_helper.rb
127
+ homepage: https://github.com/elias19r/base58_id
128
+ licenses:
129
+ - MIT
130
+ metadata:
131
+ source_code_uri: https://github.com/elias19r/base58_id/tree/v1.0.0
132
+ rubygems_mfa_required: 'true'
133
+ post_install_message:
134
+ rdoc_options: []
135
+ require_paths:
136
+ - lib
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ">="
140
+ - !ruby/object:Gem::Version
141
+ version: '2.7'
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubygems_version: 3.1.6
149
+ signing_key:
150
+ specification_version: 4
151
+ summary: Convert an Integer ID or a UUID String to/from Base58 String
152
+ test_files: []