hashids 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: