id_coder 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.
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'modalsupport'
4
+ gem 'modalsettings'
5
+
6
+ group :development do
7
+ gem "shoulda", ">= 0"
8
+ gem "rdoc", "~> 3.12"
9
+ gem "bundler", "~> 1"
10
+ gem "jeweler", "~> 1.8.3"
11
+ end
@@ -0,0 +1,32 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.8.3)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rdoc
10
+ json (1.6.6)
11
+ modalsettings (1.0.0)
12
+ modalsupport (>= 0.8.1)
13
+ modalsupport (0.8.3)
14
+ rake (0.9.2.2)
15
+ rdoc (3.12)
16
+ json (~> 1.4)
17
+ shoulda (3.0.1)
18
+ shoulda-context (~> 1.0.0)
19
+ shoulda-matchers (~> 1.0.0)
20
+ shoulda-context (1.0.0)
21
+ shoulda-matchers (1.0.0)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ bundler (~> 1)
28
+ jeweler (~> 1.8.3)
29
+ modalsettings
30
+ modalsupport
31
+ rdoc (~> 3.12)
32
+ shoulda
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Javier Goizueta
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,44 @@
1
+ = id_coder
2
+
3
+ Id-codes are relatively short codes to represent externally id internal numbers.
4
+
5
+ A bijection between ids and codes is used to assure uniqueness of codes and to avoid storing the
6
+ codes in the database (codes and ids are computed on-the-fly from each other).
7
+
8
+ Codes are user-visible; they're designed to be the user's identification of some element.
9
+
10
+ Only digits an capital letters are used for the codes, excluding I,O,1,0 to avoid confusion,
11
+ and a check digit can be used to detect most common transcription errors. This makes codes
12
+ viable to be transmitted orally, etc.
13
+
14
+ The number of digits used is scalable: the minimum number of digits and the increment-size can be
15
+ parameterized to render good-looking codes.
16
+
17
+ An IdCoder can be defined passing to it three optional parameters that define the lenght of the codes:
18
+
19
+ IdCoder[:num_digits=>4, :block_digits=>3, :check_digit=>false]
20
+
21
+ An additional parameter can be passed to define which characters will be used as digits for the codes
22
+ and its order; this must be a String of IdCoder::RADIX distinct characters:
23
+
24
+ IdCoder[:code_digits=>"0123456789ABCDEFGHIJKLMNOPQRSTUV"]
25
+
26
+ Alternatively, a seed parameter can be passed to generate a randomized string
27
+
28
+ IdCoder[:seed=>8734112]
29
+
30
+ Since this might produce different results in different Ruby versions, the code_digits produced can be accessed
31
+ and kept for portability:
32
+
33
+ IdCoder[:seed=>8734112].code_digits # -> "9GTFNJSZA6LQWXV3BEKHYMP75U8R24CD"
34
+
35
+ Ids (integers) can be coded and decoded into alphanumeric strings like this:
36
+
37
+ id_coder = IdCoder[]
38
+ id_coder.id_to_code(7) # => "JEQJTE3"
39
+ id_coder.code_to_id("JEQJTE3") # => 7
40
+
41
+ == Copyright
42
+
43
+ Copyright (c) 2012 Javier Goizueta. See LICENSE.txt for
44
+ further details.
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "id_coder"
18
+ gem.homepage = "http://github.com/jgoizueta/id_coder"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Alphanumeric code generator}
21
+ gem.description = %Q{Generates relatively short codes to represent id numbers externally}
22
+ gem.email = "jgoizueta@gmail.com"
23
+ gem.authors = ["Javier Goizueta"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ task :default => :test
36
+
37
+ require 'rdoc/task'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "id_coder #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,203 @@
1
+ require 'rubygems'
2
+ require 'modalsupport'
3
+ require 'modalsettings'
4
+
5
+ # Id-codes are relatively short codes to represent externally id internal numbers.
6
+ #
7
+ # A bijection between ids and codes is used to assure uniqueness of codes and to avoid storing the
8
+ # codes in the database (codes and ids are computed on-the-fly from each other).
9
+ #
10
+ # Codes are user-visible; they're designed to be the user's identification of some element.
11
+ #
12
+ # Only digits an capital letters are used for the codes, excluding I,O,1,0 to avoid confusion,
13
+ # and a check digit can be used to detect most common transcription errors. This makes codes
14
+ # viable to be transmitted orally, etc.
15
+ #
16
+ # The number of digits used is scalable: the minimum number of digits and the increment-size can be
17
+ # parameterized to render good-looking codes.
18
+ #
19
+ # An IdCoder can be defined passing to it three optional parameters that define the lenght of the codes:
20
+ # IdCoder[:num_digits=>4, :block_digits=>3, :check_digit=>false]
21
+ # An additional parameter can be passed to define which characters will be used as digits for the codes
22
+ # and its order; this must be a String of IdCoder::RADIX distinct characters:
23
+ # IdCoder[:code_digits=>"0123456789ABCDEFGHIJKLMNOPQRSTUV"]
24
+ # Alternatively, a seed parameter can be passed to generate a randomized string
25
+ # IdCoder[:seed=>8734112]
26
+ # Since this might produce different results in different Ruby versions, the code_digits produced can be accessed
27
+ # and kept for portability:
28
+ # IdCoder[:seed=>8734112].code_digits # -> "9GTFNJSZA6LQWXV3BEKHYMP75U8R24CD"
29
+ #
30
+ class IdCoder
31
+
32
+ # Internal parameters
33
+ RADIX = 32
34
+ DIGITS = "0123456789abcdefghijklmnopqrstuv" # RADIX-digits used by Integer#to_s(RADIX), String#to_i(RADIX)
35
+ CODE_DIGITS = "BAFTQ4EJCNVYZKDPG37H5S8WML692RXU" # RADIX-digits used for the codes (permutation of 2-9,A-Z except I,O)
36
+
37
+ class InvalidCode < RuntimeError; end
38
+ class InvalidNumber < RuntimeError; end
39
+ class InvalidCodeDigits < RuntimeError; end
40
+
41
+ def initialize(config={})
42
+ config = Settings[config]
43
+ if config.code_digits
44
+ @code_digits = config.code_digits
45
+ raise InvalidCodeDigits, "Invalid digits" if @code_digits.bytes.to_a.uniq.size!=RADIX
46
+ else
47
+ @code_digits = CODE_DIGITS
48
+ if config.seed
49
+ srand config.seed
50
+ @code_digits = @code_digits.bytes.to_a.shuffle.map(&:chr)*""
51
+ end
52
+ end
53
+ @num_digits = (config.num_digits || 6).to_i
54
+ @block_digits = (config.block_digits || 2).to_i
55
+ @check_digit = config.check_digit
56
+ @check_digit = true if @check_digit.nil?
57
+ end
58
+
59
+ include ModalSupport::BracketConstructor
60
+
61
+ include
62
+
63
+ # configurable parameters
64
+
65
+ # Minumum number of digits used; limits the number of valid ids before scaling up to RADIX**num_digits
66
+ def num_digits
67
+ @num_digits
68
+ end
69
+
70
+ # Use a check digit?: this increases the length of codes in one
71
+ def check_digit?
72
+ @check_digit
73
+ end
74
+
75
+ # Number of digits for incremental scaling-up blocks
76
+ def block_digits
77
+ @block_digits
78
+ end
79
+
80
+ # Properties of the codes
81
+
82
+ # mininum code_length
83
+ def code_length
84
+ num_digits + (check_digit? ? 1 : 0)
85
+ end
86
+
87
+ # Number of valid codes before scaling-up
88
+ def num_valid_codes
89
+ RADIX**num_digits
90
+ end
91
+
92
+ def num_digits_for(id)
93
+ if id<num_valid_codes
94
+ num_digits
95
+ else
96
+ # ((Math.log(id)/Math.log(RADIX)-code_length)/block_digits).ceil*block_digits + num_digits
97
+ nd = num_digits
98
+ loop do
99
+ nd += block_digits
100
+ nvc = RADIX**nd
101
+ break if id<nvc
102
+ end
103
+ # check = ((Math.log(id)/Math.log(RADIX)-code_length)/block_digits).ceil*block_digits + num_digits
104
+ # raise "nd=#{nd}; [#{check}] id=#{id}" unless nd==check
105
+ nd
106
+ end
107
+ end
108
+
109
+ def code_length_for(id)
110
+ num_digits_for(id) + (check_digit? ? 1 : 0)
111
+ end
112
+
113
+ def code_digits
114
+ @code_digits
115
+ end
116
+
117
+ def parameters
118
+ { :num_digits=>@num_digits, :block_digits=>@block_digits, :check_digit=>@check_digit, :code_digits=>@code_digits }
119
+ end
120
+
121
+ def inspect
122
+ "IdCoder[#{parameters.inspect.unwrap('[]')}]"
123
+ end
124
+
125
+ # Code generatioon
126
+
127
+ # Direct encoding: generate an Id-Code from an integer id.
128
+ def id_to_code(id)
129
+ raise InvalidNumber, "Numbers to be coded must be passed as integers" unless id.kind_of?(Integer)
130
+ raise InvalidNumber, "Negative numbers cannot be encoded: #{id}" if id<0
131
+ nd = num_digits_for(id)
132
+ v = id.to_s(RADIX)
133
+ v = "0"*(nd-v.size) + v
134
+ i = 0
135
+ code = ""
136
+ mask = 0
137
+ v.reverse.each_byte do |b|
138
+ d = DIGITS.index(b.chr)
139
+ code << @code_digits[mask = ((d+i)%RADIX ^ mask),1]
140
+ i += 1
141
+ end
142
+ code << check_digit(code) if check_digit?
143
+ code
144
+ end
145
+
146
+ # Inverse encoding: compute the integer id for a Id-Code
147
+ def code_to_id(code)
148
+ raise InvalidCode, "Codes must be strings" unless code.kind_of?(String)
149
+ code = code.strip.upcase # tolerate case differences & surrounding whitespace
150
+ raise InvalidCode, "Invalid code: #{code}" unless code =~ /\A[#{@code_digits}]+\Z/
151
+ # raise InvalidCode, "Invalid code length: #{code.size} for #{code} (must be #{code_length})" unless code.size==code_length
152
+ if check_digit?
153
+ cd = code[-1,1]
154
+ code = code[0...-1]
155
+ raise InvalidCode, "Invalid code: #{code+cd}" if cd!=check_digit(code)
156
+ end
157
+ nd = code.size
158
+ sx = nd-num_digits
159
+ raise InvalidCode, "Invalid code length: #{code.size} for #{code}" unless sx>=0 && (sx%block_digits)==0
160
+ i = 0
161
+ v = ""
162
+ mask = 0
163
+ code.each_byte do |b|
164
+ next_mask = @code_digits.index(b.chr)
165
+ d = ((next_mask ^ mask)-i)%RADIX
166
+ mask = next_mask
167
+ d = DIGITS[d,1]
168
+ v = d + v
169
+ i += 1
170
+ end
171
+ v.to_i(RADIX)
172
+ end
173
+
174
+ # Check code: returns true (valid) or false (invalid)
175
+ def valid_code?(code)
176
+ is_valid = true
177
+ begin
178
+ code_to_id(code)
179
+ rescue InvalidCode
180
+ is_valid = false
181
+ end
182
+ is_valid
183
+ end
184
+
185
+ private
186
+
187
+ # Check digits are computed using the Luhn Mod N Algorithm:
188
+ # http://en.wikipedia.org/wiki/Luhn_mod_N_algorithm
189
+ def check_digit(code)
190
+ factor = 2
191
+ sum = 0
192
+ code.each_byte do |b|
193
+ addend = factor * @code_digits.index(b.chr)
194
+ factor = (factor==2) ? 1 : 2
195
+ addend = (addend / RADIX) + (addend % RADIX)
196
+ sum += addend
197
+ end
198
+ remainder = sum % RADIX
199
+ @code_digits[(RADIX - remainder) % RADIX, 1]
200
+ end
201
+
202
+
203
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'id_coder'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,152 @@
1
+ require 'helper'
2
+
3
+ class TestIdCoder < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @id_coder = IdCoder[
7
+ :num_digits => 6,
8
+ :block_digits => 2,
9
+ :check_digit => true
10
+ ]
11
+ end
12
+
13
+ def test_bijectivity
14
+ # sanity check
15
+ assert@id_coder.check_digit?
16
+ assert_equal 6,@id_coder.num_digits
17
+ assert_equal 2,@id_coder.block_digits
18
+ assert_equal 7,@id_coder.code_length
19
+ assert_equal 32, IdCoder::RADIX
20
+ assert_equal 32**6,@id_coder.num_valid_codes
21
+
22
+ # Test numbers within the basic range (num_digits)
23
+ test_numbers = (0..100).to_a + (99999900..100000020).to_a + [232211,9898774,1002,343332,1023,1024,1025]
24
+ test_numbers.each do |number|
25
+ code =@id_coder.id_to_code(number)
26
+ assert_equal 7, code.size # test also code length here
27
+ assert_equal number,@id_coder.code_to_id(code),"#{number} is converted to a code and back"
28
+ end
29
+
30
+ # Test larger numbers
31
+ test_numbers = (1_999_999_990..2_000_000_010).to_a + (1_999_999_999_990..2_000_000_000_010).to_a
32
+ test_numbers.each do |number|
33
+ code =@id_coder.id_to_code(number)
34
+ assert code.size > 7
35
+ assert(
36
+ (code.size-(@id_coder.check_digit? ? 1 : 0)-@id_coder.num_digits)%@id_coder.block_digits == 0
37
+ )
38
+ assert_equal number,@id_coder.code_to_id(code),"#{number} is converted to a code and back"
39
+ end
40
+
41
+ end
42
+
43
+ def test_invalid_numbers
44
+ # sanity check
45
+ assert@id_coder.check_digit?
46
+ assert_equal 6,@id_coder.num_digits
47
+ assert_equal 2,@id_coder.block_digits
48
+ assert_equal 7,@id_coder.code_length
49
+ assert_equal 32, IdCoder::RADIX
50
+ assert_equal 32**6,@id_coder.num_valid_codes
51
+ # => ~1000000000 codes
52
+
53
+ assert_raise(IdCoder::InvalidNumber){@id_coder.id_to_code("1") }
54
+ assert_raise(IdCoder::InvalidNumber){@id_coder.id_to_code(-1) }
55
+ assert_nothing_raised(IdCoder::InvalidNumber){@id_coder.id_to_code(2000000000) }
56
+ assert_nothing_raised(IdCoder::InvalidNumber){@id_coder.id_to_code(0) }
57
+ assert_nothing_raised(IdCoder::InvalidNumber){@id_coder.id_to_code(1) }
58
+ assert_nothing_raised(IdCoder::InvalidNumber){@id_coder.id_to_code(100) }
59
+ assert_nothing_raised(IdCoder::InvalidNumber){@id_coder.id_to_code(1000000000) }
60
+ assert_nothing_raised(IdCoder::InvalidNumber){@id_coder.id_to_code(2000000000) }
61
+ assert_nothing_raised(IdCoder::InvalidNumber){@id_coder.id_to_code(100000000000000000000) }
62
+ end
63
+
64
+ def test_invalid_codes
65
+ @id_coder = IdCoder[:num_digits=>4, :block_digits=>3, :check_digit=>false]
66
+ # sanity check
67
+ assert !@id_coder.check_digit?
68
+ assert_equal 4,@id_coder.num_digits
69
+ assert_equal 3,@id_coder.block_digits
70
+ assert_equal 4,@id_coder.code_length
71
+ assert_equal 32, IdCoder::RADIX
72
+ assert_equal 32**4,@id_coder.num_valid_codes
73
+
74
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id(1023) }
75
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id(234) }
76
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id(2345) }
77
+ assert_nothing_raised(IdCoder::InvalidCode){@id_coder.code_to_id('2345') }
78
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('345') }
79
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('34567') }
80
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('3415') }
81
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('34I5') }
82
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('2A04') }
83
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('2AO4') }
84
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('2A-3') }
85
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('2A 3') }
86
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('2A)3') }
87
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('345678') }
88
+ assert_nothing_raised(IdCoder::InvalidCode){@id_coder.code_to_id('3456789') }
89
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('3456789A') }
90
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('3456789AB') }
91
+ assert_nothing_raised(IdCoder::InvalidCode){@id_coder.code_to_id('3456789ABC') }
92
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('3456789ABCD') }
93
+ assert_nothing_raised(IdCoder::InvalidCode){@id_coder.code_to_id('2A45') }
94
+ assert_nothing_raised(IdCoder::InvalidCode){@id_coder.code_to_id(' 2A43 ') }
95
+ assert_nothing_raised(IdCoder::InvalidCode){@id_coder.code_to_id('2a43') }
96
+ end
97
+
98
+ def test_check_digits
99
+ # sanity check
100
+ assert@id_coder.check_digit?
101
+ assert_equal 6,@id_coder.num_digits
102
+ assert_equal 2,@id_coder.block_digits
103
+ assert_equal 7,@id_coder.code_length
104
+ assert_equal 32, IdCoder::RADIX
105
+ assert_equal 32**6,@id_coder.num_valid_codes
106
+
107
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id(1023) }
108
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id(234) }
109
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id(2345938) }
110
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('2345938') }
111
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('345432') }
112
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id('3454321') }
113
+ valid_code =@id_coder.id_to_code(234331)
114
+ assert_equal 7, valid_code.size
115
+ digits = %w{2 3 4 5 6 7 8 9 A B C D E F G H J K L M N P Q R S T U V W X Y Z}
116
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id(valid_code[0...-1])}
117
+ # Check that changing the check digit makes the code invalid
118
+ digits.each do |digit|
119
+ next if digit == valid_code[-1,1]
120
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id(valid_code[0...-1]+digit)}
121
+ end
122
+ # Check that any single-digit change makes the code invalid
123
+ (0..5).each do |i|
124
+ digits.each do |digit|
125
+ next if digit == valid_code[i,1]
126
+ invalid_code = valid_code[0,i] + digit + valid_code[i+1..-1]
127
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id(invalid_code)}
128
+ end
129
+ end
130
+ # Check that adjacent digit swapping makes the code invalid
131
+ # (there are some exceptions though, see http://en.wikipedia.org/wiki/Luhn_mod_N_algorithm)
132
+ # TODO: sistematic check avoiding exceptions
133
+ invalid_code = valid_code[0,2] + valid_code[3,1] + valid_code[2,1] + valid_code[4..-1]
134
+ assert_raise(IdCoder::InvalidCode){@id_coder.code_to_id(invalid_code)}
135
+ end
136
+
137
+ def test_internal_sanity
138
+ # Check the coded digits used: must be exactly IdCoder::RADIX unique uppercase characters
139
+ chars = []
140
+ IdCoder::CODE_DIGITS.each_byte{|b| chars << b.chr}
141
+ assert_equal IdCoder::RADIX, chars.uniq.size
142
+ assert_equal IdCoder::CODE_DIGITS.upcase, IdCoder::CODE_DIGITS
143
+
144
+ # Check the internally-used radix-32 digits to be consistent with Integer#to_s
145
+ assert_equal IdCoder::RADIX, IdCoder::DIGITS.size
146
+ (0...IdCoder::RADIX).each do |i|
147
+ assert_equal i.to_s(IdCoder::RADIX), IdCoder::DIGITS[i,1]
148
+ end
149
+ end
150
+
151
+
152
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: id_coder
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Javier Goizueta
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: modalsupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: modalsettings
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: shoulda
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rdoc
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '3.12'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '3.12'
78
+ - !ruby/object:Gem::Dependency
79
+ name: bundler
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '1'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1'
94
+ - !ruby/object:Gem::Dependency
95
+ name: jeweler
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 1.8.3
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ version: 1.8.3
110
+ description: Generates relatively short codes to represent id numbers externally
111
+ email: jgoizueta@gmail.com
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files:
115
+ - LICENSE.txt
116
+ - README.rdoc
117
+ files:
118
+ - .document
119
+ - Gemfile
120
+ - Gemfile.lock
121
+ - LICENSE.txt
122
+ - README.rdoc
123
+ - Rakefile
124
+ - VERSION
125
+ - lib/id_coder.rb
126
+ - test/helper.rb
127
+ - test/test_id_coder.rb
128
+ homepage: http://github.com/jgoizueta/id_coder
129
+ licenses:
130
+ - MIT
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ segments:
142
+ - 0
143
+ hash: -1112580131298559240
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 1.8.21
153
+ signing_key:
154
+ specification_version: 3
155
+ summary: Alphanumeric code generator
156
+ test_files: []