hashids 0.0.1 → 0.0.2
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 +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +5 -0
- data/README.md +5 -0
- data/hashids.gemspec +2 -0
- data/lib/hashids.rb +33 -41
- data/spec/hashids_profile.rb +21 -0
- data/spec/hashids_spec.rb +4 -4
- metadata +8 -3
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -5,10 +5,15 @@ Use hashids when you do not want to expose your database ids to the user.
|
|
5
5
|
|
6
6
|
[http://www.hashids.org/ruby/](http://www.hashids.org/ruby/)
|
7
7
|
|
8
|
+
[](http://travis-ci.org/peterhellberg/hashids.rb)
|
9
|
+
(1.9.2, 1.9.3, jruby-19mode, rbx-19mode and ruby-head)
|
10
|
+
|
8
11
|
## What is it?
|
9
12
|
|
10
13
|
hashids (Hash ID's) creates short, unique, decryptable hashes from unsigned integers.
|
11
14
|
|
15
|
+
_(NOTE: This is **NOT** a true cryptographic hash, since it is reversible)_
|
16
|
+
|
12
17
|
It was designed for websites to use in URL shortening, tracking stuff, or
|
13
18
|
making pages private (or at least unguessable).
|
14
19
|
|
data/hashids.gemspec
CHANGED
@@ -12,6 +12,8 @@ Gem::Specification.new do |gem|
|
|
12
12
|
gem.description = %q{Use hashids when you do not want to expose your database ids to the user.}
|
13
13
|
gem.homepage = "https://github.com/peterhellberg/hashids.rb"
|
14
14
|
|
15
|
+
gem.required_ruby_version = '>= 1.9.2'
|
16
|
+
|
15
17
|
gem.files = `git ls-files`.split($/)
|
16
18
|
gem.test_files = gem.files.grep(%r{^(spec)/})
|
17
19
|
gem.require_paths = ["lib"]
|
data/lib/hashids.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
class Hashids
|
4
|
-
VERSION = "0.0.
|
4
|
+
VERSION = "0.0.2"
|
5
5
|
DEFAULT_ALPHABET = "xcS4F6h89aUbideAI7tkynuopqrXCgTE5GBKHLMjfRsz"
|
6
6
|
PRIMES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43]
|
7
7
|
|
@@ -10,22 +10,22 @@ class Hashids
|
|
10
10
|
class AlphabetError < ArgumentError; end
|
11
11
|
|
12
12
|
def initialize(salt = "", min_length = 0, alphabet = DEFAULT_ALPHABET)
|
13
|
-
@salt
|
14
|
-
@min_length
|
15
|
-
@alphabet
|
13
|
+
@salt = salt
|
14
|
+
@min_length = min_length
|
15
|
+
@alphabet = alphabet
|
16
|
+
|
17
|
+
@chars_regex = /./
|
16
18
|
|
17
19
|
validate_attributes
|
18
20
|
setup_alphabet
|
19
21
|
end
|
20
22
|
|
21
23
|
def encrypt(*numbers)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
if numbers.empty? || numbers.reject { |n| Integer(n) && n > 0 }.any?
|
25
|
+
""
|
26
|
+
else
|
27
|
+
encode(numbers, @alphabet, @salt, @min_length)
|
26
28
|
end
|
27
|
-
|
28
|
-
encode(numbers, @alphabet, @salt, @min_length)
|
29
29
|
end
|
30
30
|
|
31
31
|
def decrypt(hash)
|
@@ -52,7 +52,7 @@ class Hashids
|
|
52
52
|
@seps = []
|
53
53
|
@guards = []
|
54
54
|
|
55
|
-
@alphabet = @alphabet.
|
55
|
+
@alphabet = @alphabet.scan(@chars_regex).uniq.join('')
|
56
56
|
|
57
57
|
if @alphabet.length < 4
|
58
58
|
raise AlphabetError, "Alphabet must contain at least 4 unique characters."
|
@@ -63,7 +63,7 @@ class Hashids
|
|
63
63
|
|
64
64
|
break if char.nil?
|
65
65
|
|
66
|
-
@seps
|
66
|
+
@seps << char
|
67
67
|
@alphabet.gsub!(char, ' ')
|
68
68
|
end
|
69
69
|
|
@@ -71,7 +71,7 @@ class Hashids
|
|
71
71
|
separator = @seps[index]
|
72
72
|
|
73
73
|
unless separator.nil?
|
74
|
-
@guards
|
74
|
+
@guards << separator
|
75
75
|
@seps.delete_at(index)
|
76
76
|
end
|
77
77
|
end
|
@@ -83,13 +83,13 @@ class Hashids
|
|
83
83
|
def encode(numbers, alphabet, salt, min_length = 0)
|
84
84
|
ret = ""
|
85
85
|
|
86
|
-
seps = consistent_shuffle(@seps, numbers).
|
86
|
+
seps = consistent_shuffle(@seps, numbers).scan(@chars_regex)
|
87
87
|
lottery_char = ""
|
88
88
|
|
89
89
|
numbers.each_with_index do |number, i|
|
90
90
|
if i == 0
|
91
91
|
lottery_salt = numbers.join('-')
|
92
|
-
numbers.each { |n| lottery_salt += "
|
92
|
+
numbers.each { |n| lottery_salt += "-#{(n + 1) * 2}" }
|
93
93
|
|
94
94
|
lottery = consistent_shuffle(alphabet, lottery_salt)
|
95
95
|
|
@@ -172,8 +172,8 @@ class Hashids
|
|
172
172
|
end
|
173
173
|
|
174
174
|
if alphabet.length > 0 && lottery_char.length > 0
|
175
|
-
alphabet = consistent_shuffle(alphabet, (lottery_char.ord & 12345).to_s +
|
176
|
-
ret
|
175
|
+
alphabet = consistent_shuffle(alphabet, (lottery_char.ord & 12345).to_s + @salt)
|
176
|
+
ret << unhash(sub_hash, alphabet)
|
177
177
|
end
|
178
178
|
end
|
179
179
|
end
|
@@ -187,24 +187,21 @@ class Hashids
|
|
187
187
|
def consistent_shuffle(alphabet, salt)
|
188
188
|
ret = ""
|
189
189
|
|
190
|
-
alphabet = alphabet.join
|
191
|
-
salt = salt.join
|
190
|
+
alphabet = alphabet.join("") if alphabet.respond_to? :join
|
191
|
+
salt = salt.join("") if salt.respond_to? :join
|
192
192
|
|
193
|
-
alphabet_array = alphabet.
|
194
|
-
salt_array
|
195
|
-
sorting_array
|
193
|
+
alphabet_array = alphabet.scan(@chars_regex)
|
194
|
+
salt_array = salt.scan(@chars_regex)
|
195
|
+
sorting_array = []
|
196
196
|
|
197
|
-
salt_array
|
198
|
-
|
199
|
-
salt_array.each do |salt_char|
|
200
|
-
sorting_array.push salt_char.ord || 0
|
201
|
-
end
|
197
|
+
salt_array << "" if salt_array.empty?
|
198
|
+
salt_array.each { |char| sorting_array << char.ord }
|
202
199
|
|
203
200
|
sorting_array.each_with_index do |int,i|
|
204
201
|
add = true
|
205
202
|
k = i
|
206
203
|
|
207
|
-
while k !=
|
204
|
+
while k != sorting_array.length + i - 1
|
208
205
|
next_index = (k + 1) % sorting_array.length
|
209
206
|
|
210
207
|
if add
|
@@ -223,9 +220,8 @@ class Hashids
|
|
223
220
|
i = 0
|
224
221
|
|
225
222
|
while alphabet_array.length > 0
|
226
|
-
alphabet_size = alphabet_array.length
|
227
223
|
pos = sorting_array[i]
|
228
|
-
pos %=
|
224
|
+
pos %= alphabet_array.length if pos >= alphabet_array.length
|
229
225
|
ret += alphabet_array[pos]
|
230
226
|
|
231
227
|
alphabet_array.delete_at(pos)
|
@@ -237,11 +233,10 @@ class Hashids
|
|
237
233
|
|
238
234
|
def hash(number, alphabet)
|
239
235
|
hash = ""
|
240
|
-
alphabet_length = alphabet.length
|
241
236
|
|
242
237
|
while number > 0
|
243
|
-
hash = alphabet[number %
|
244
|
-
number = number /
|
238
|
+
hash = alphabet[number % alphabet.length] + hash
|
239
|
+
number = number / alphabet.length
|
245
240
|
end
|
246
241
|
|
247
242
|
hash
|
@@ -250,15 +245,12 @@ class Hashids
|
|
250
245
|
def unhash(hash, alphabet)
|
251
246
|
number = 0
|
252
247
|
|
253
|
-
hash.split('').each_with_index
|
254
|
-
pos = alphabet.index
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
number += pos * alphabet.length ** (hash.length - i - 1)
|
259
|
-
}
|
248
|
+
hash.split('').each_with_index do |char, i|
|
249
|
+
if pos = alphabet.index(char)
|
250
|
+
number += pos * alphabet.length ** (hash.length - i - 1)
|
251
|
+
end
|
252
|
+
end
|
260
253
|
|
261
254
|
number
|
262
255
|
end
|
263
|
-
|
264
256
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Profile with perftools.rb
|
4
|
+
#
|
5
|
+
# CPUPROFILE=/tmp/hashids_profile \
|
6
|
+
# RUBYOPT="-r`gem which 'perftools.rb' | tail -1`" \
|
7
|
+
# ruby spec/hashids_profile.rb
|
8
|
+
#
|
9
|
+
# Generate diagram
|
10
|
+
#
|
11
|
+
# pprof.rb --gif /tmp/hashids_profile > /tmp/hashids_profile.gif && \
|
12
|
+
# open /tmp/hashids_profile.gif
|
13
|
+
#
|
14
|
+
|
15
|
+
require_relative "../lib/hashids"
|
16
|
+
|
17
|
+
h = Hashids.new('this is my salt')
|
18
|
+
|
19
|
+
10_000.times do |n|
|
20
|
+
h.decrypt(h.encrypt(n))
|
21
|
+
end
|
data/spec/hashids_spec.rb
CHANGED
@@ -89,15 +89,15 @@ describe Hashids do
|
|
89
89
|
h.encrypt(1,2,3,4,5).must_equal 'adcdacddcdaacdad'
|
90
90
|
end
|
91
91
|
|
92
|
-
it "
|
92
|
+
it "does not produce repeating patterns for identical numbers" do
|
93
93
|
hashids.encrypt(5,5,5,5).must_equal 'GLh5SMs9'
|
94
94
|
end
|
95
95
|
|
96
|
-
it "
|
96
|
+
it "does not produce repeating patterns for incremented numbers" do
|
97
97
|
hashids.encrypt(*(1..10).to_a).must_equal 'zEUzfySGIpuyhpF6HaC7'
|
98
98
|
end
|
99
99
|
|
100
|
-
it "
|
100
|
+
it "does not produce similarities between incrementing number hashes" do
|
101
101
|
hashids.encrypt(1).must_equal 'LX'
|
102
102
|
hashids.encrypt(2).must_equal 'ed'
|
103
103
|
hashids.encrypt(3).must_equal 'o9'
|
@@ -126,7 +126,7 @@ describe Hashids do
|
|
126
126
|
}
|
127
127
|
end
|
128
128
|
|
129
|
-
it "
|
129
|
+
it "does not decrypt with a different salt" do
|
130
130
|
peppers = Hashids.new('this is my pepper')
|
131
131
|
hashids.decrypt('ryBo').must_equal [12345]
|
132
132
|
peppers.decrypt('ryBo').must_equal []
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hashids
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -19,12 +19,14 @@ extensions: []
|
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
21
|
- .gitignore
|
22
|
+
- .travis.yml
|
22
23
|
- Gemfile
|
23
24
|
- LICENSE.txt
|
24
25
|
- README.md
|
25
26
|
- Rakefile
|
26
27
|
- hashids.gemspec
|
27
28
|
- lib/hashids.rb
|
29
|
+
- spec/hashids_profile.rb
|
28
30
|
- spec/hashids_spec.rb
|
29
31
|
homepage: https://github.com/peterhellberg/hashids.rb
|
30
32
|
licenses: []
|
@@ -37,13 +39,16 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
39
|
requirements:
|
38
40
|
- - ! '>='
|
39
41
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
42
|
+
version: 1.9.2
|
41
43
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
44
|
none: false
|
43
45
|
requirements:
|
44
46
|
- - ! '>='
|
45
47
|
- !ruby/object:Gem::Version
|
46
48
|
version: '0'
|
49
|
+
segments:
|
50
|
+
- 0
|
51
|
+
hash: 2124593182474944467
|
47
52
|
requirements: []
|
48
53
|
rubyforge_project:
|
49
54
|
rubygems_version: 1.8.24
|
@@ -51,5 +56,5 @@ signing_key:
|
|
51
56
|
specification_version: 3
|
52
57
|
summary: Generate YouTube-like hashes from one or many numbers.
|
53
58
|
test_files:
|
59
|
+
- spec/hashids_profile.rb
|
54
60
|
- spec/hashids_spec.rb
|
55
|
-
has_rdoc:
|