fixed_length_encoder 1.2.1 → 2.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.
- data/.gitignore +2 -0
- data/.rspec +1 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +80 -0
- data/Rakefile +15 -0
- data/extra/readme.rb +12 -0
- data/extra/stats.rb +85 -0
- data/fixed_length_encoder.gemspec +26 -0
- data/lib/fixed_length_encoder.rb +10 -14
- data/lib/version.rb +3 -0
- data/spec/fixed_length_encoder_spec.rb +125 -0
- metadata +59 -7
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2013 Brett Pontarelli
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# fixed_length_encoder
|
2
|
+
|
3
|
+
A one-to-one mapping function between integers and fixed length strings, such that sequential
|
4
|
+
integers are mapped to non-sequential strings. In other words you can obfuscate user ids for use
|
5
|
+
in urls.
|
6
|
+
|
7
|
+
* https://rubygems.org/gems/fixed_length_encoder
|
8
|
+
* http://github.com/brettwp/fixed_length_encoder
|
9
|
+
|
10
|
+
## How it works
|
11
|
+
|
12
|
+
### Encoding
|
13
|
+
|
14
|
+
Converts an integer value to a string of fixed length (default is 8). As of `1.2` the maximum encodable value is the same as the alphabet maximum. For example the default 36 character alphabet and 8 character fixed length can encoded numbers between 0 and 2,821,109,907,455 = `36**8 - 1`.
|
15
|
+
|
16
|
+
### Decoding
|
17
|
+
|
18
|
+
Given a valid string returns the decoded number. Note that the two operations are reversible and
|
19
|
+
adjacent values are unlikely to return adjacent strings (See Stats section below). For example, using the default configuration:
|
20
|
+
|
21
|
+
FixedLengthEncoder.decode(FixedLengthEncoder.encode(100)) == 100
|
22
|
+
FixedLengthEncoder.encode(100) == 'ycxk2ntw'
|
23
|
+
FixedLengthEncoder.encode(101) == 'd8gxk24x'
|
24
|
+
|
25
|
+
## How to install
|
26
|
+
|
27
|
+
sudo gem install fixed_length_encoder
|
28
|
+
|
29
|
+
## How to use
|
30
|
+
|
31
|
+
require 'fixed_length_encoder'
|
32
|
+
|
33
|
+
FixedLengthEncoder.encode(100)
|
34
|
+
FixedLengthEncoder.decode('ycxk2ntw')
|
35
|
+
|
36
|
+
FixedLengthEncoder.encode(42, 3)
|
37
|
+
FixedLengthEncoder.decode('5c4')
|
38
|
+
|
39
|
+
## Changing the length
|
40
|
+
|
41
|
+
FixedLengthEncoder::MESSAGE_LENGTH = 10
|
42
|
+
|
43
|
+
## Changing the alphabet and encoding
|
44
|
+
|
45
|
+
The `ALPHABET`, `ENCODE_MAP` and `DECODE_MAP` must all work together. The two maps must also be
|
46
|
+
reversible. For example, for an alphabet of 62 characters you will need to build two maps of
|
47
|
+
length `62**2 - 1` such that `DECODE_MAP[ENCODE_MAP[x]] == x`. One such way to do this would be:
|
48
|
+
|
49
|
+
max = 62*62 - 1
|
50
|
+
ENCODE_MAP = (0..max).to_a.shuffle
|
51
|
+
DECODE_MAP = []
|
52
|
+
(0..max).each { |i| DECODE_MAP[ENCODE_MAP[i]] = i }
|
53
|
+
|
54
|
+
Then, hard code these results into your application. You will have three lines much like the lines
|
55
|
+
that define the default `ALPHABET`, `ENCODE_MAP` and `DECODE_MAP` in the `FixedLengthEncoded`:
|
56
|
+
|
57
|
+
FixedLengthEncoder::ALPHABET = 'abcdefg'
|
58
|
+
FixedLengthEncoder::ENCODE_MAP = [19, 22, 25, 44, 17, 21, 33, 48, 39, 0, 16, 20, 29, 40, 43, 23, 3, 41, 12, 35, 7, 14, 10, 32, 46, 38, 9, 11, 27, 31, 26, 18, 34, 24, 4, 42, 47, 5, 1, 36, 13, 37, 30, 15, 45, 2, 8, 28, 6]
|
59
|
+
FixedLengthEncoder::DECODE_MAP = [9, 38, 45, 16, 34, 37, 48, 20, 46, 26, 22, 27, 18, 40, 21, 43, 10, 4, 31, 0, 11, 5, 1, 15, 33, 2, 30, 28, 47, 12, 42, 29, 23, 6, 32, 19, 39, 41, 25, 8, 13, 17, 35, 14, 3, 44, 24, 36, 7]
|
60
|
+
|
61
|
+
# Stats
|
62
|
+
|
63
|
+
Consider a random `value` using the `FixedLengthEncoder` default `LENGTH` of 8 and `ALPHABET` of 36
|
64
|
+
characters. If we encode `value` and `value + 1` and compute the difference between them in base 36
|
65
|
+
we get a `delta`. The table below compares the distribution of 10M pairs of two adjacent encoded values with 10M pairs of two random numbers.
|
66
|
+
Both sets are taken from the range `0` to `36**8 = 2,821,109,907,456`. As expected the number of
|
67
|
+
negative deltas is near `50%` with the encoded values `49.9860%` negative and random `49.9989%`.
|
68
|
+
It's interesting to note that there are no random occurances of two adjecent values, but in the
|
69
|
+
encoded values there are `674`.
|
70
|
+
|
71
|
+
| |: Encoded :|: Random :|
|
72
|
+
| ---------------:| -----------------:| -----------------:|
|
73
|
+
| Negative deltas| 4,998,610 | 4,999,894 |
|
74
|
+
| Delta equals one| 674 | 0 |
|
75
|
+
| Maximum Delta| 2,820,278,456,877 | 2,820,579,691,973 |
|
76
|
+
| Average Delta| 935,745,508,922 | 940,183,477,180 |
|
77
|
+
| Std Dev| 1,148,460,034,903 | 1,151,442,250,985 |
|
78
|
+
|
79
|
+
* Author :: Brett Pontarelli <brett@paperyfrog.com>
|
80
|
+
* Website :: http://brett.pontarelli.com
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'rake'
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
task :default => :spec
|
6
|
+
|
7
|
+
desc 'Run statistical tests'
|
8
|
+
task :stats do
|
9
|
+
load 'extra/stats.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'Build examples for README'
|
13
|
+
task :readme do
|
14
|
+
load 'extra/readme.rb'
|
15
|
+
end
|
data/extra/readme.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'fixed_length_encoder'
|
2
|
+
|
3
|
+
puts FixedLengthEncoder.encode(100)
|
4
|
+
puts FixedLengthEncoder.encode(101)
|
5
|
+
puts FixedLengthEncoder.encode(42, 3)
|
6
|
+
|
7
|
+
max = 7*7 - 1
|
8
|
+
ENCODE_MAP = (0..max).to_a.shuffle
|
9
|
+
DECODE_MAP = []
|
10
|
+
(0..max).each { |i| DECODE_MAP[ENCODE_MAP[i]] = i }
|
11
|
+
puts "[#{ENCODE_MAP.join(', ')}]"
|
12
|
+
puts "[#{DECODE_MAP.join(', ')}]"
|
data/extra/stats.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'fixed_length_encoder'
|
2
|
+
|
3
|
+
class Stats
|
4
|
+
def initialize
|
5
|
+
@delta_max = 0
|
6
|
+
@negative_count = 0
|
7
|
+
@one_count = 0
|
8
|
+
@iterations = 0
|
9
|
+
@average_cumm = 0
|
10
|
+
@stddev_cumm = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_delta(delta)
|
14
|
+
if (delta < 0)
|
15
|
+
delta = -delta
|
16
|
+
@negative_count += 1
|
17
|
+
end
|
18
|
+
@one_count += 1 if delta == 1
|
19
|
+
@iterations += 1
|
20
|
+
@average_cumm += delta
|
21
|
+
@stddev_cumm += delta*delta
|
22
|
+
@delta_max = delta if delta > @delta_max
|
23
|
+
end
|
24
|
+
|
25
|
+
def negative_count
|
26
|
+
@negative_count
|
27
|
+
end
|
28
|
+
|
29
|
+
def one_count
|
30
|
+
@one_count
|
31
|
+
end
|
32
|
+
|
33
|
+
def delta_max
|
34
|
+
@delta_max
|
35
|
+
end
|
36
|
+
|
37
|
+
def average
|
38
|
+
@average_cumm / @iterations
|
39
|
+
end
|
40
|
+
|
41
|
+
def stddev
|
42
|
+
Math.sqrt((@stddev_cumm - (average*average/@iterations))/@iterations)
|
43
|
+
end
|
44
|
+
|
45
|
+
def one_percent
|
46
|
+
100.0 * @one_count / @iterations
|
47
|
+
end
|
48
|
+
|
49
|
+
def negative_percent
|
50
|
+
100.0 * @negative_count / @iterations
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def comma(number)
|
55
|
+
number.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
56
|
+
end
|
57
|
+
|
58
|
+
max = 36**8
|
59
|
+
iter = 10**7
|
60
|
+
encoder = FixedLengthEncoder::Encoder.new(FixedLengthEncoder::ALPHABET, FixedLengthEncoder::ENCODE_MAP, FixedLengthEncoder::DECODE_MAP)
|
61
|
+
random_stats = Stats.new()
|
62
|
+
encoder_stats = Stats.new()
|
63
|
+
puts "36**8 = #{comma(max)}"
|
64
|
+
(0..iter).each do |i|
|
65
|
+
value = Random.rand(max - 2)
|
66
|
+
e1 = encoder.string_to_integer(encoder.encode(value, 8))
|
67
|
+
e2 = encoder.string_to_integer(encoder.encode(value+1, 8))
|
68
|
+
encoder_stats.add_delta(e1 - e2)
|
69
|
+
|
70
|
+
v1 = Random.rand(max - 1)
|
71
|
+
v2 = Random.rand(max - 1)
|
72
|
+
random_stats.add_delta(v1 - v2)
|
73
|
+
end
|
74
|
+
|
75
|
+
def report(title, encoder)
|
76
|
+
puts "===#{title}==="
|
77
|
+
puts 'Neg: ' + comma(encoder.negative_count) + " (#{encoder.negative_percent.to_s}%)"
|
78
|
+
puts 'One: ' + comma(encoder.one_count) + " (#{encoder.one_percent.to_s}%)"
|
79
|
+
puts 'Max: ' + comma(encoder.delta_max)
|
80
|
+
puts 'Avg: ' + comma(encoder.average)
|
81
|
+
puts 'Std: ' + comma(encoder.stddev)
|
82
|
+
end
|
83
|
+
|
84
|
+
report('Random', random_stats)
|
85
|
+
report('Encoder', encoder_stats)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'fixed_length_encoder'
|
8
|
+
spec.version = FixedLengthEncoder::VERSION
|
9
|
+
spec.authors = ['Brett Pontarelli']
|
10
|
+
spec.email = ['brett@paperyfrog.com']
|
11
|
+
spec.description = 'Integers are converted to strings using a complex mapping ' +
|
12
|
+
'and a base 36 alphabet. Strings with valid digits (0-9 a-z)' +
|
13
|
+
'are converted back to integers. The encoding is one-to-one but not sequential.' +
|
14
|
+
'This is useful for obfuscating user ids in urls.'
|
15
|
+
spec.summary = 'Two way conversion from integer to a fixed (default=8) digit string.'
|
16
|
+
spec.homepage = 'http://rubygems.org/gems/fixed_length_encoder'
|
17
|
+
spec.license = 'MIT'
|
18
|
+
|
19
|
+
spec.files = `git ls-files`.split($/)
|
20
|
+
# spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
25
|
+
spec.add_development_dependency 'rake'
|
26
|
+
end
|
data/lib/fixed_length_encoder.rb
CHANGED
@@ -63,30 +63,26 @@ module FixedLengthEncoder
|
|
63
63
|
|
64
64
|
def offset(value, direction)
|
65
65
|
offset = (@max_value/2).floor
|
66
|
-
(value
|
67
|
-
(value -= offset) if direction < 0
|
68
|
-
(value += @max_value) if value < 0
|
69
|
-
(value -= @max_value) if value >= @max_value
|
70
|
-
value
|
66
|
+
(value + direction * offset) % @max_value
|
71
67
|
end
|
72
68
|
|
73
69
|
def scramble_value(value, direction)
|
74
|
-
message = integer_to_array(value)
|
70
|
+
message = integer_to_array(value)
|
75
71
|
message = map_array(message, @encode_map, direction) if direction > 0
|
76
72
|
message = map_array(message, @decode_map, direction) if direction < 0
|
77
73
|
array_to_integer(message)
|
78
74
|
end
|
79
75
|
|
80
76
|
def map_array(message, map, direction)
|
81
|
-
indexes = (
|
77
|
+
indexes = (0..@message_length).to_a.map { |i| i % @message_length }
|
82
78
|
indexes = indexes.reverse if direction < 0
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
map_index =
|
79
|
+
(0...@message_length).each do |index|
|
80
|
+
low_index = indexes[index]
|
81
|
+
high_index = indexes[index + 1]
|
82
|
+
map_index = message[high_index] * @base + message[low_index]
|
87
83
|
map_value = map[map_index]
|
88
|
-
message[
|
89
|
-
message[
|
84
|
+
message[low_index] = map_value / @base
|
85
|
+
message[high_index] = map_value % @base
|
90
86
|
end
|
91
87
|
message
|
92
88
|
end
|
@@ -123,4 +119,4 @@ module FixedLengthEncoder
|
|
123
119
|
value
|
124
120
|
end
|
125
121
|
end
|
126
|
-
end
|
122
|
+
end
|
data/lib/version.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'fixed_length_encoder'
|
2
|
+
|
3
|
+
def suppress_warnings
|
4
|
+
original_verbosity = $VERBOSE
|
5
|
+
$VERBOSE = nil
|
6
|
+
result = yield
|
7
|
+
$VERBOSE = original_verbosity
|
8
|
+
return result
|
9
|
+
end
|
10
|
+
|
11
|
+
describe FixedLengthEncoder do
|
12
|
+
describe 'Invalid encodings' do
|
13
|
+
before (:each) do
|
14
|
+
suppress_warnings do
|
15
|
+
@origianl = FixedLengthEncoder::ALPHABET
|
16
|
+
FixedLengthEncoder::ALPHABET = '0123456789'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
after (:each) do
|
21
|
+
suppress_warnings do
|
22
|
+
FixedLengthEncoder::ALPHABET = @origianl
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should error on non-integers' do
|
27
|
+
expect { FixedLengthEncoder.encode('ERROR') }.to raise_error(ArgumentError)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'shouldn\'t encode values too big for message length' do
|
31
|
+
expect { FixedLengthEncoder.encode(100, 2) }.to raise_error(ArgumentError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'shouldn\'t encode negative values' do
|
35
|
+
expect { FixedLengthEncoder.encode(-1, 2) }.to raise_error(ArgumentError)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should error for non-strings' do
|
39
|
+
expect { FixedLengthEncoder.decode(0) }.to raise_error(ArgumentError)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should error for bad characters' do
|
43
|
+
expect { FixedLengthEncoder.decode('^') }.to raise_error(ArgumentError)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'Valid encodings' do
|
48
|
+
before (:each) do
|
49
|
+
suppress_warnings do
|
50
|
+
@origianl = FixedLengthEncoder::ALPHABET
|
51
|
+
@encode_map = FixedLengthEncoder::ENCODE_MAP
|
52
|
+
@decode_map = FixedLengthEncoder::DECODE_MAP
|
53
|
+
FixedLengthEncoder::ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
54
|
+
max = 62*62 - 1
|
55
|
+
encode_map = (0..max).to_a.shuffle
|
56
|
+
decode_map = []
|
57
|
+
(0..max).each { |i| decode_map[encode_map[i]] = i }
|
58
|
+
FixedLengthEncoder::ENCODE_MAP = encode_map
|
59
|
+
FixedLengthEncoder::DECODE_MAP = decode_map
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
after (:each) do
|
64
|
+
suppress_warnings do
|
65
|
+
FixedLengthEncoder::ALPHABET = @origianl
|
66
|
+
FixedLengthEncoder::ENCODE_MAP = @encode_map
|
67
|
+
FixedLengthEncoder::DECODE_MAP = @decode_map
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should be a valid alphabet' do
|
72
|
+
FixedLengthEncoder.isValidAlphabet().should be_true
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should be a valid map' do
|
76
|
+
FixedLengthEncoder.isValidMap().should be_true
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should be reversible for the min value' do
|
80
|
+
value = 0
|
81
|
+
message = FixedLengthEncoder.encode(value)
|
82
|
+
FixedLengthEncoder.decode(message).should eq(value)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should be reversible for the max value' do
|
86
|
+
value = (62**8)-1
|
87
|
+
message = FixedLengthEncoder.encode(value)
|
88
|
+
FixedLengthEncoder.decode(message).should eq(value)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should be reversible for the middle value' do
|
92
|
+
value = ((62**8)/2).floor
|
93
|
+
message = FixedLengthEncoder.encode(value)
|
94
|
+
FixedLengthEncoder.decode(message).should eq(value)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe 'Default encodings' do
|
99
|
+
it 'should be a valid alphabet' do
|
100
|
+
FixedLengthEncoder.isValidAlphabet().should be_true
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should be a valid map' do
|
104
|
+
FixedLengthEncoder.isValidMap().should be_true
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should be reversible for the min value' do
|
108
|
+
value = 0
|
109
|
+
message = FixedLengthEncoder.encode(value)
|
110
|
+
FixedLengthEncoder.decode(message).should eq(value)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should be reversible for the max value' do
|
114
|
+
value = (36**8)-1
|
115
|
+
message = FixedLengthEncoder.encode(value)
|
116
|
+
FixedLengthEncoder.decode(message).should eq(value)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should be reversible for the middle value' do
|
120
|
+
value = ((36**8)/2).floor
|
121
|
+
message = FixedLengthEncoder.encode(value)
|
122
|
+
FixedLengthEncoder.decode(message).should eq(value)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fixed_length_encoder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,20 +9,65 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
13
|
-
dependencies:
|
12
|
+
date: 2013-09-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
14
46
|
description: Integers are converted to strings using a complex mapping and a base
|
15
47
|
36 alphabet. Strings with valid digits (0-9 a-z)are converted back to integers. The
|
16
48
|
encoding is one-to-one but not sequential.This is useful for obfuscating user ids
|
17
49
|
in urls.
|
18
|
-
email:
|
50
|
+
email:
|
51
|
+
- brett@paperyfrog.com
|
19
52
|
executables: []
|
20
53
|
extensions: []
|
21
54
|
extra_rdoc_files: []
|
22
55
|
files:
|
56
|
+
- .gitignore
|
57
|
+
- .rspec
|
58
|
+
- Gemfile
|
59
|
+
- LICENSE
|
60
|
+
- README.md
|
61
|
+
- Rakefile
|
62
|
+
- extra/readme.rb
|
63
|
+
- extra/stats.rb
|
64
|
+
- fixed_length_encoder.gemspec
|
23
65
|
- lib/fixed_length_encoder.rb
|
66
|
+
- lib/version.rb
|
67
|
+
- spec/fixed_length_encoder_spec.rb
|
24
68
|
homepage: http://rubygems.org/gems/fixed_length_encoder
|
25
|
-
licenses:
|
69
|
+
licenses:
|
70
|
+
- MIT
|
26
71
|
post_install_message:
|
27
72
|
rdoc_options: []
|
28
73
|
require_paths:
|
@@ -33,16 +78,23 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
33
78
|
- - ! '>='
|
34
79
|
- !ruby/object:Gem::Version
|
35
80
|
version: '0'
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
hash: 2142083918509383952
|
36
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
37
85
|
none: false
|
38
86
|
requirements:
|
39
87
|
- - ! '>='
|
40
88
|
- !ruby/object:Gem::Version
|
41
89
|
version: '0'
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
hash: 2142083918509383952
|
42
93
|
requirements: []
|
43
94
|
rubyforge_project:
|
44
|
-
rubygems_version: 1.8.
|
95
|
+
rubygems_version: 1.8.25
|
45
96
|
signing_key:
|
46
97
|
specification_version: 3
|
47
98
|
summary: Two way conversion from integer to a fixed (default=8) digit string.
|
48
|
-
test_files:
|
99
|
+
test_files:
|
100
|
+
- spec/fixed_length_encoder_spec.rb
|