hashids 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hashids.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Peter Hellberg
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,202 @@
1
+ # Hashids
2
+
3
+ A small Ruby gem to generate YouTube-like hashes from one or many numbers.
4
+ Use hashids when you do not want to expose your database ids to the user.
5
+
6
+ [http://www.hashids.org/ruby/](http://www.hashids.org/ruby/)
7
+
8
+ ## What is it?
9
+
10
+ hashids (Hash ID's) creates short, unique, decryptable hashes from unsigned integers.
11
+
12
+ It was designed for websites to use in URL shortening, tracking stuff, or
13
+ making pages private (or at least unguessable).
14
+
15
+ This algorithm tries to satisfy the following requirements:
16
+
17
+ 1. Hashes must be unique and decryptable.
18
+ 2. They should be able to contain more than one integer (so you can use them in complex or clustered systems).
19
+ 3. You should be able to specify minimum hash length.
20
+ 4. Hashes should not contain basic English curse words (since they are meant to appear in public places - like the URL).
21
+
22
+ Instead of showing items as `1`, `2`, or `3`, you could show them as `U6dc`, `u87U`, and `HMou`.
23
+ You don't have to store these hashes in the database, but can encrypt + decrypt on the fly.
24
+
25
+ All integers need to be greater than or equal to zero.
26
+
27
+ ## Installation
28
+
29
+ Add this line to your application's Gemfile:
30
+
31
+ gem 'hashids'
32
+
33
+ And then execute:
34
+
35
+ $ bundle
36
+
37
+ Or install it yourself as:
38
+
39
+ $ gem install hashids
40
+
41
+ ## Usage
42
+
43
+ #### Encrypting one number
44
+
45
+ You can pass a unique salt value so your hashes differ from everyone else's. I use "**this is my salt**" as an example.
46
+
47
+ ```ruby
48
+ hashids = Hashids.new("this is my salt")
49
+ hash = hashids.encrypt(12345)
50
+ ```
51
+
52
+ `hash` is now going to be:
53
+
54
+ ryBo
55
+
56
+ #### Decrypting
57
+
58
+ Notice during decryption, same salt value is used:
59
+
60
+ ```ruby
61
+ hashids = Hashids.new("this is my salt")
62
+ numbers = hashids.decrypt("ryBo")
63
+ ```
64
+
65
+ `numbers` is now going to be:
66
+
67
+ [ 12345 ]
68
+
69
+ #### Decrypting with different salt
70
+
71
+ Decryption will not work if salt is changed:
72
+
73
+ ```ruby
74
+ hashids = Hashids.new("this is my pepper")
75
+ numbers = hashids.decrypt("ryBo")
76
+ ```
77
+
78
+ `numbers` is now going to be:
79
+
80
+ []
81
+
82
+ #### Encrypting several numbers
83
+
84
+ ```ruby
85
+ hashids = Hashids.new("this is my salt")
86
+ hash = hashids.encrypt(683, 94108, 123, 5)
87
+ ```
88
+
89
+ `hash` is now going to be:
90
+
91
+ zBphL54nuMyu5
92
+
93
+ #### Decrypting is done the same way
94
+
95
+ ```ruby
96
+ hashids = Hashids.new("this is my salt")
97
+ numbers = hashids.decrypt("zBphL54nuMyu5")
98
+ ```
99
+
100
+ `numbers` is now going to be:
101
+
102
+ [ 683, 94108, 123, 5 ]
103
+
104
+ #### Encrypting and specifying minimum hash length
105
+
106
+ Here we encrypt integer 1, and set the minimum hash length to **8** (by default it's **0** -- meaning hashes will be the shortest possible length).
107
+
108
+ ```ruby
109
+ hashids = Hashids.new("this is my salt", 8)
110
+ hash = hashids.encrypt(1)
111
+ ```
112
+
113
+ `hash` is now going to be:
114
+
115
+ b9iLXiAa
116
+
117
+ #### Decrypting
118
+
119
+ ```ruby
120
+ hashids = Hashids.new("this is my salt", 8)
121
+ numbers = hashids.decrypt("b9iLXiAa")
122
+ ```
123
+
124
+ `numbers` is now going to be:
125
+
126
+ [ 1 ]
127
+
128
+ #### Specifying custom hash alphabet
129
+
130
+ Here we set the alphabet to consist of only four letters: "abcd"
131
+
132
+ ```ruby
133
+ hashids = Hashids.new("this is my salt", 0, "abcd")
134
+ hash = hashids.encrypt(1, 2, 3, 4, 5)
135
+ ```
136
+
137
+ `hash` is now going to be:
138
+
139
+ adcdacddcdaacdad
140
+
141
+ ## Randomness
142
+
143
+ The primary purpose of hashids is to obfuscate ids. It's not meant or tested to be used for security purposes or compression.
144
+ Having said that, this algorithm does try to make these hashes unguessable and unpredictable:
145
+
146
+ #### Repeating numbers
147
+
148
+ ```ruby
149
+ hashids = Hashids.new("this is my salt")
150
+ hash = hashids.encrypt(5, 5, 5, 5)
151
+ ```
152
+
153
+ You don't see any repeating patterns that might show there's 4 identical numbers in the hash:
154
+
155
+ GLh5SMs9
156
+
157
+ Same with incremented numbers:
158
+
159
+ ```ruby
160
+ hashids = Hashids.new("this is my salt")
161
+ hash = hashids.encrypt(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
162
+ ```
163
+
164
+ `hash` will be :
165
+
166
+ zEUzfySGIpuyhpF6HaC7
167
+
168
+ ### Incrementing number hashes:
169
+
170
+ ```ruby
171
+ hashids = Hashids.new("this is my salt")
172
+
173
+ hashids.encrypt 1 #=> LX
174
+ hashids.encrypt 2 #=> ed
175
+ hashids.encrypt 3 #=> o9
176
+ hashids.encrypt 4 #=> 4n
177
+ hashids.encrypt 5 #=> a5
178
+ ```
179
+
180
+ ## Changelog
181
+
182
+ **0.0.1**
183
+
184
+ - First commit (Heavily based on the [CoffeeScript version](https://github.com/ivanakimov/hashids.coffee))
185
+
186
+ ## Contact
187
+
188
+ Follow me [@peterhellberg](http://twitter.com/peterhellberg)
189
+
190
+ Or [http://c7.se/](http://c7.se/)
191
+
192
+ ## License
193
+
194
+ MIT License. See the `LICENSE.txt` file.
195
+
196
+ ## Contributing
197
+
198
+ 1. Fork it
199
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
200
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
201
+ 4. Push to the branch (`git push origin my-new-feature`)
202
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "rake/testtask"
2
+ require "bundler/gem_tasks"
3
+
4
+ task :default => :spec
5
+
6
+ Rake::TestTask.new(:spec) do |t|
7
+ t.test_files = FileList['spec/**/*_spec.rb']
8
+ end
data/hashids.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'hashids'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "hashids"
8
+ gem.version = Hashids::VERSION
9
+ gem.authors = ["Peter Hellberg"]
10
+ gem.email = ["peter@c7.se"]
11
+ gem.summary = %q{Generate YouTube-like hashes from one or many numbers.}
12
+ gem.description = %q{Use hashids when you do not want to expose your database ids to the user.}
13
+ gem.homepage = "https://github.com/peterhellberg/hashids.rb"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.test_files = gem.files.grep(%r{^(spec)/})
17
+ gem.require_paths = ["lib"]
18
+ end
data/lib/hashids.rb ADDED
@@ -0,0 +1,264 @@
1
+ # encoding: utf-8
2
+
3
+ class Hashids
4
+ VERSION = "0.0.1"
5
+ DEFAULT_ALPHABET = "xcS4F6h89aUbideAI7tkynuopqrXCgTE5GBKHLMjfRsz"
6
+ PRIMES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43]
7
+
8
+ class SaltError < ArgumentError; end
9
+ class MinLengthError < ArgumentError; end
10
+ class AlphabetError < ArgumentError; end
11
+
12
+ def initialize(salt = "", min_length = 0, alphabet = DEFAULT_ALPHABET)
13
+ @salt = salt
14
+ @min_length = min_length
15
+ @alphabet = alphabet
16
+
17
+ validate_attributes
18
+ setup_alphabet
19
+ end
20
+
21
+ def encrypt(*numbers)
22
+ return "" unless numbers.any?
23
+
24
+ numbers.each do |number|
25
+ return "" if !number.kind_of?(Fixnum) || number < 0
26
+ end
27
+
28
+ encode(numbers, @alphabet, @salt, @min_length)
29
+ end
30
+
31
+ def decrypt(hash)
32
+ hash.empty? ? [] : decode(hash)
33
+ end
34
+
35
+ private
36
+
37
+ def validate_attributes
38
+ unless @salt.kind_of?(String)
39
+ raise Hashids::SaltError, "The salt must be a String"
40
+ end
41
+
42
+ unless @min_length.kind_of?(Fixnum)
43
+ raise Hashids::MinLengthError, "The min length must be a Fixnum"
44
+ end
45
+
46
+ unless @alphabet.kind_of?(String)
47
+ raise Hashids::AlphabetError, "The alphabet must be a String"
48
+ end
49
+ end
50
+
51
+ def setup_alphabet
52
+ @seps = []
53
+ @guards = []
54
+
55
+ @alphabet = @alphabet.split('').uniq.join
56
+
57
+ if @alphabet.length < 4
58
+ raise AlphabetError, "Alphabet must contain at least 4 unique characters."
59
+ end
60
+
61
+ PRIMES.each do |prime|
62
+ char = @alphabet[prime - 1]
63
+
64
+ break if char.nil?
65
+
66
+ @seps.push char
67
+ @alphabet.gsub!(char, ' ')
68
+ end
69
+
70
+ [0, 4, 8, 12].each do |index|
71
+ separator = @seps[index]
72
+
73
+ unless separator.nil?
74
+ @guards.push(separator)
75
+ @seps.delete_at(index)
76
+ end
77
+ end
78
+
79
+ @alphabet.gsub!(' ', '')
80
+ @alphabet = consistent_shuffle(@alphabet, @salt)
81
+ end
82
+
83
+ def encode(numbers, alphabet, salt, min_length = 0)
84
+ ret = ""
85
+
86
+ seps = consistent_shuffle(@seps, numbers).split("")
87
+ lottery_char = ""
88
+
89
+ numbers.each_with_index do |number, i|
90
+ if i == 0
91
+ lottery_salt = numbers.join('-')
92
+ numbers.each { |n| lottery_salt += "-" + ((n.to_i + 1) * 2).to_s }
93
+
94
+ lottery = consistent_shuffle(alphabet, lottery_salt)
95
+
96
+ ret += lottery_char = lottery[0]
97
+
98
+ alphabet = lottery_char + alphabet.gsub(lottery_char, '')
99
+ end
100
+
101
+ alphabet = consistent_shuffle(alphabet, "#{lottery_char.ord & 12345}#{salt}")
102
+ ret += hash(number, alphabet)
103
+
104
+ if (i + 1) < numbers.length
105
+ seps_index = (number + i) % seps.length
106
+ ret += seps[seps_index]
107
+ end
108
+ end
109
+
110
+ if ret.length < min_length
111
+ first_index = 0
112
+ numbers.each_with_index do |number, i|
113
+ first_index += (i + 1) * number
114
+ end
115
+
116
+ guard_index = first_index % @guards.length
117
+ guard = @guards[guard_index]
118
+
119
+ ret = guard + ret
120
+
121
+ if ret.length < min_length
122
+ guard_index = (guard_index + ret.length) % @guards.length
123
+ guard = @guards[guard_index]
124
+
125
+ ret += guard
126
+ end
127
+ end
128
+
129
+ while ret.length < min_length
130
+ pad_array = [alphabet[1].ord, alphabet[0].ord]
131
+ pad_left = encode(pad_array, alphabet, salt)
132
+ pad_right = encode(pad_array, alphabet, pad_array.join(''))
133
+
134
+ ret = pad_left + ret + pad_right
135
+ excess = ret.length - min_length
136
+
137
+ ret = ret[(excess/2), min_length] if excess > 0
138
+ alphabet = consistent_shuffle(alphabet, salt + ret)
139
+ end
140
+
141
+ ret
142
+ end
143
+
144
+ def decode(hash)
145
+ ret = []
146
+
147
+ if hash.length > 0
148
+ original_hash = hash
149
+ alphabet = ""
150
+ lottery_char = ""
151
+
152
+ @guards.each do |guard|
153
+ hash = hash.gsub(guard, ' ')
154
+ end
155
+
156
+ hash_split = hash.split(' ')
157
+
158
+ hash = hash_split[[3,2].include?(hash_split.length) ? 1 : 0]
159
+
160
+ @seps.each do |sep|
161
+ hash = hash.gsub(sep, ' ')
162
+ end
163
+
164
+ hash_array = hash.split(' ')
165
+
166
+ hash_array.each_with_index do |sub_hash, i|
167
+ if sub_hash.length > 0
168
+ if i == 0
169
+ lottery_char = hash[0]
170
+ sub_hash = sub_hash[1..-1]
171
+ alphabet = lottery_char + @alphabet.gsub(lottery_char, "")
172
+ end
173
+
174
+ if alphabet.length > 0 && lottery_char.length > 0
175
+ alphabet = consistent_shuffle(alphabet, (lottery_char.ord & 12345).to_s + "" + @salt)
176
+ ret.push unhash(sub_hash, alphabet)
177
+ end
178
+ end
179
+ end
180
+
181
+ ret = [] if encrypt(*ret) != original_hash
182
+ end
183
+
184
+ ret
185
+ end
186
+
187
+ def consistent_shuffle(alphabet, salt)
188
+ ret = ""
189
+
190
+ alphabet = alphabet.join "" if alphabet.respond_to? :join
191
+ salt = salt.join "" if salt.respond_to? :join
192
+
193
+ alphabet_array = alphabet.split('')
194
+ salt_array = salt.split('')
195
+ sorting_array = []
196
+
197
+ salt_array.push "" unless salt_array.any?
198
+
199
+ salt_array.each do |salt_char|
200
+ sorting_array.push salt_char.ord || 0
201
+ end
202
+
203
+ sorting_array.each_with_index do |int,i|
204
+ add = true
205
+ k = i
206
+
207
+ while k != (sorting_array.length + i - 1)
208
+ next_index = (k + 1) % sorting_array.length
209
+
210
+ if add
211
+ sorting_array[i] += sorting_array[next_index] + (k * i)
212
+ else
213
+ sorting_array[i] -= sorting_array[next_index]
214
+ end
215
+
216
+ add = !add
217
+ k += 1
218
+ end
219
+
220
+ sorting_array[i] = sorting_array[i].abs
221
+ end
222
+
223
+ i = 0
224
+
225
+ while alphabet_array.length > 0
226
+ alphabet_size = alphabet_array.length
227
+ pos = sorting_array[i]
228
+ pos %= alphabet_size if pos >= alphabet_size
229
+ ret += alphabet_array[pos]
230
+
231
+ alphabet_array.delete_at(pos)
232
+ i = (i+1) % sorting_array.length
233
+ end
234
+
235
+ ret
236
+ end
237
+
238
+ def hash(number, alphabet)
239
+ hash = ""
240
+ alphabet_length = alphabet.length
241
+
242
+ while number > 0
243
+ hash = alphabet[number % alphabet_length] + hash
244
+ number = number / alphabet_length
245
+ end
246
+
247
+ hash
248
+ end
249
+
250
+ def unhash(hash, alphabet)
251
+ number = 0
252
+
253
+ hash.split('').each_with_index { |char, i|
254
+ pos = alphabet.index char
255
+
256
+ return if pos.nil?
257
+
258
+ number += pos * alphabet.length ** (hash.length - i - 1)
259
+ }
260
+
261
+ number
262
+ end
263
+
264
+ end
@@ -0,0 +1,164 @@
1
+ # encoding: utf-8
2
+
3
+ require "minitest/spec"
4
+ require "minitest/pride"
5
+ require "minitest/autorun"
6
+
7
+ require_relative "../lib/hashids"
8
+
9
+ describe Hashids do
10
+ let(:salt) { 'this is my salt' }
11
+ let(:hashids) { Hashids.new(salt) }
12
+ let(:alphabet) { "xgzXjp48RaoeLdB6yrAMK9b5n7qEkG" }
13
+ let(:shuffled_alphabet) { "xMGAE6gXkpBR785zLraqnj49doyKeb" }
14
+ let(:default_alphabet) { "xcS4F6h89aUbideAI7tkynuopqrXCgTE5GBKHLMjfRsz" }
15
+ let(:seps) { ["S", "F", "h", "U", "I", "t", "u", "C", "H", "f", "s"] }
16
+ let(:examples) {
17
+ {
18
+ 'aa' => 155, 'ba' => 185, 'j9' => 814,
19
+ 'ky' => 342, 'Xg' => 559, 'GB' => 683,
20
+ 'B4' => 691, '9Xg' => 4159, 'jAz' => 24599
21
+ }
22
+ }
23
+
24
+ describe "setup" do
25
+ it "has a default alphabet" do
26
+ Hashids::DEFAULT_ALPHABET.must_equal default_alphabet
27
+ end
28
+
29
+ it "has the correct salt" do
30
+ hashids.instance_variable_get(:@salt).must_equal salt
31
+ end
32
+
33
+ it "defaults to a min_length of 0" do
34
+ hashids.instance_variable_get(:@min_length).must_equal 0
35
+ end
36
+
37
+ it "generates the correct @seps" do
38
+ hashids.instance_variable_get(:@seps).must_equal seps
39
+ end
40
+
41
+ it "generates the correct @guards" do
42
+ hashids.instance_variable_get(:@guards).must_equal ["c", "i", "T"]
43
+ end
44
+
45
+ it "generates the correct alphabet" do
46
+ hashids.instance_variable_get(:@alphabet).must_equal alphabet
47
+ end
48
+ end
49
+
50
+ describe "encrypt" do
51
+ it "encrypts a single number" do
52
+ hashids.encrypt(12345).must_equal 'ryBo'
53
+
54
+ hashids.tap { |h|
55
+ h.encrypt(-1).must_equal ''
56
+ h.encrypt(1).must_equal 'LX'
57
+ h.encrypt(22).must_equal '5B'
58
+ h.encrypt(333).must_equal 'o49'
59
+ h.encrypt(9999).must_equal 'GKnB'
60
+ }
61
+ end
62
+
63
+ it "can encrypt a list of numbers" do
64
+ hashids.encrypt(683, 94108, 123, 5).must_equal 'zBphL54nuMyu5'
65
+
66
+ hashids.tap { |h|
67
+ h.encrypt(1,2,3).must_equal 'eGtrS8'
68
+ h.encrypt(2,4,6).must_equal '9Kh7fz'
69
+ h.encrypt(99,25).must_equal 'dAECX'
70
+ }
71
+ end
72
+
73
+ it "returns an empty string if no numbers" do
74
+ hashids.encrypt.must_equal ""
75
+ end
76
+
77
+ it "returns an empty string if any of the numbers are negative" do
78
+ hashids.encrypt(-1).must_equal ""
79
+ hashids.encrypt(10,-10).must_equal ""
80
+ end
81
+
82
+ it "can encrypt to a minumum length" do
83
+ h = Hashids.new(salt, 8)
84
+ h.encrypt(1).must_equal "b9iLXiAa"
85
+ end
86
+
87
+ it "can encrypt with a custom alphabet" do
88
+ h = Hashids.new(salt, 0, "abcd")
89
+ h.encrypt(1,2,3,4,5).must_equal 'adcdacddcdaacdad'
90
+ end
91
+
92
+ it "doesn’t produce repeating patterns for identical numbers" do
93
+ hashids.encrypt(5,5,5,5).must_equal 'GLh5SMs9'
94
+ end
95
+
96
+ it "doesn’t produce repeating patterns for incremented numbers" do
97
+ hashids.encrypt(*(1..10).to_a).must_equal 'zEUzfySGIpuyhpF6HaC7'
98
+ end
99
+
100
+ it "doesn’t produce similarities between incrementing number hashes" do
101
+ hashids.encrypt(1).must_equal 'LX'
102
+ hashids.encrypt(2).must_equal 'ed'
103
+ hashids.encrypt(3).must_equal 'o9'
104
+ hashids.encrypt(4).must_equal '4n'
105
+ hashids.encrypt(5).must_equal 'a5'
106
+ end
107
+ end
108
+
109
+ describe "decrypt" do
110
+ it "decrypts an encrypted number" do
111
+ hashids.decrypt("ryBo").must_equal [12345]
112
+
113
+ hashids.tap { |h|
114
+ h.decrypt('qkpA').must_equal [1337]
115
+ h.decrypt('6aX').must_equal [808]
116
+ h.decrypt('gz9').must_equal [303]
117
+ }
118
+ end
119
+
120
+ it "decrypts a list of encrypted numbers" do
121
+ hashids.decrypt('zBphL54nuMyu5').must_equal [683, 94108, 123, 5]
122
+
123
+ hashids.tap { |h|
124
+ h.decrypt('kEFy').must_equal [1, 2]
125
+ h.decrypt('Aztn').must_equal [6, 5]
126
+ }
127
+ end
128
+
129
+ it "doesn’t decrypt with a different salt" do
130
+ peppers = Hashids.new('this is my pepper')
131
+ hashids.decrypt('ryBo').must_equal [12345]
132
+ peppers.decrypt('ryBo').must_equal []
133
+ end
134
+
135
+ it "can decrypt from a hash with a minimum length" do
136
+ h = Hashids.new(salt, 8)
137
+ h.decrypt("b9iLXiAa").must_equal [1]
138
+ end
139
+ end
140
+
141
+ describe "setup" do
142
+ it "raises an exception if the alphabet has less than 4 unique chars" do
143
+ -> { Hashids.new('salt', 0, 'abc') }.
144
+ must_raise Hashids::AlphabetError
145
+ end
146
+ end
147
+
148
+ describe "validation of attributes" do
149
+ it "raises an ArgumentError unless the salt is a String" do
150
+ -> { Hashids.new(:not_a_string) }.
151
+ must_raise Hashids::SaltError
152
+ end
153
+
154
+ it "raises an ArgumentError unless the min_length is a Fixnum" do
155
+ -> { Hashids.new('salt', :not_a_fixnum)}.
156
+ must_raise Hashids::MinLengthError
157
+ end
158
+
159
+ it "raises an ArgumentError unless the alphabet is a String" do
160
+ -> { Hashids.new('salt', 2, :not_a_string) }.
161
+ must_raise Hashids::AlphabetError
162
+ end
163
+ end
164
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hashids
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Peter Hellberg
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-14 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Use hashids when you do not want to expose your database ids to the user.
15
+ email:
16
+ - peter@c7.se
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - LICENSE.txt
24
+ - README.md
25
+ - Rakefile
26
+ - hashids.gemspec
27
+ - lib/hashids.rb
28
+ - spec/hashids_spec.rb
29
+ homepage: https://github.com/peterhellberg/hashids.rb
30
+ licenses: []
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ requirements: []
48
+ rubyforge_project:
49
+ rubygems_version: 1.8.24
50
+ signing_key:
51
+ specification_version: 3
52
+ summary: Generate YouTube-like hashes from one or many numbers.
53
+ test_files:
54
+ - spec/hashids_spec.rb
55
+ has_rdoc: