luhn 0.1.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/LICENSE +19 -0
- data/README.rdoc +44 -0
- data/lib/luhn.rb +40 -0
- data/lib/luhn/civic_number.rb +62 -0
- data/lib/luhn/extensions.rb +3 -0
- data/lib/luhn/extensions/numeric.rb +11 -0
- data/lib/luhn/extensions/string.rb +10 -0
- data/lib/luhn/version.rb +4 -0
- data/spec/helper.rb +7 -0
- data/spec/spec_civic_number.rb +53 -0
- data/spec/spec_extensions.rb +21 -0
- data/spec/spec_luhn.rb +31 -0
- metadata +76 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Copyright (c) 2010 Joel Junström, Oktavilla
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
= Ruby class for handling basic Luhn number generation and verification
|
2
|
+
|
3
|
+
Includes a class to handle Swedish civic numbers (Personnummer)
|
4
|
+
That interface supports checking validity (length, valid date and satisfies luhn),
|
5
|
+
returning the sex, the control digit and generating random civic numbers.
|
6
|
+
|
7
|
+
== Install
|
8
|
+
|
9
|
+
$ gem install luhn
|
10
|
+
|
11
|
+
== Usage:
|
12
|
+
|
13
|
+
=== Basic Luhn
|
14
|
+
|
15
|
+
Luhn.valid?('0465827879483596') # true
|
16
|
+
Luhn.control_digit('046582787948359') # 6
|
17
|
+
Luhn.generate(n) # returns a random number of n length that satisfies luhn
|
18
|
+
|
19
|
+
'0465827879483596'.valid_luhn? # true
|
20
|
+
0465827879483596.valid_luhn? # true
|
21
|
+
|
22
|
+
=== Swedish civic numbers
|
23
|
+
|
24
|
+
civic_number = Luhn::CivicNumber.new('19391030-3183')
|
25
|
+
civic_number.valid? # true
|
26
|
+
civic_number.sex # 'male'
|
27
|
+
civic_number.male? # true
|
28
|
+
civic_number.control_digit # 3
|
29
|
+
|
30
|
+
'391030-3183'.civic_number? # true
|
31
|
+
3910303183.civic_number? # true
|
32
|
+
|
33
|
+
== About the Luhn algorithm (from http://en.wikipedia.org/wiki/Luhn_formula)
|
34
|
+
|
35
|
+
The Luhn algorithm or Luhn formula, also known as the "modulus 10" or "mod 10"
|
36
|
+
algorithm, is a simple checksum formula used to validate a variety of
|
37
|
+
identification numbers, such as credit card numbers, IMEI numbers,
|
38
|
+
National Provider Identifier numbers in US and Canadian Social Insurance Numbers.
|
39
|
+
It was created by IBM scientist Hans Peter Luhn and described in U.S. Patent
|
40
|
+
No. 2,950,048, filed on January 6, 1954, and granted on August 23, 1960.
|
41
|
+
|
42
|
+
== Copyright
|
43
|
+
|
44
|
+
Copyright (c) 2010 Joel Junström. See LICENSE for details.
|
data/lib/luhn.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'luhn/extensions'
|
3
|
+
|
4
|
+
module Luhn
|
5
|
+
autoload :CivicNumber, 'luhn/civic_number'
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def valid?(value)
|
9
|
+
self.checksum(value, :odd) % 10 == 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def control_digit(value)
|
13
|
+
sum = self.checksum(value, :even)
|
14
|
+
(sum % 10 != 0) ? 10 - (sum % 10) : 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate(size, options = {})
|
18
|
+
generated = options[:prefix] || ''
|
19
|
+
(size - generated.size - 1).times { |i| generated += rand(10).to_s }
|
20
|
+
generated + self.control_digit(generated).to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def checksum(value, operation)
|
26
|
+
i = 0
|
27
|
+
value.reverse.split(//).inject(0) do |sum, c|
|
28
|
+
n = c.to_i
|
29
|
+
calc = if operation == :even
|
30
|
+
(i % 2 == 0) ? n * 2 : n
|
31
|
+
else
|
32
|
+
(i % 2 != 0) ? n * 2 : n
|
33
|
+
end
|
34
|
+
i += 1
|
35
|
+
sum += calc <= 9 ? calc : (calc % 10) + 1
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'ostruct'
|
3
|
+
module Luhn
|
4
|
+
class CivicNumber
|
5
|
+
attr_reader :civic_number
|
6
|
+
|
7
|
+
def initialize(string)
|
8
|
+
@civic_number = cleanup_string(string.to_s)
|
9
|
+
end
|
10
|
+
|
11
|
+
def valid?
|
12
|
+
@valid ||= civic_number.length == 10 && valid_date? && Luhn.valid?(civic_number)
|
13
|
+
end
|
14
|
+
|
15
|
+
def valid_date?
|
16
|
+
(1..12).include?(birth_date.month) && (1..31).include?(birth_date.day)
|
17
|
+
end
|
18
|
+
|
19
|
+
def control_digit
|
20
|
+
@control_digit ||= Luhn.control_digit(civic_number[0...9])
|
21
|
+
end
|
22
|
+
|
23
|
+
def sex
|
24
|
+
@sex ||= valid? ? (civic_number[8...9].to_i.even? ? 'female' : 'male') : 'unknown'
|
25
|
+
end
|
26
|
+
|
27
|
+
def female?
|
28
|
+
sex == 'female'
|
29
|
+
end
|
30
|
+
|
31
|
+
def male?
|
32
|
+
sex == 'male'
|
33
|
+
end
|
34
|
+
|
35
|
+
def birth_date
|
36
|
+
@date ||= OpenStruct.new({
|
37
|
+
:year => civic_number[0...2].to_i,
|
38
|
+
:month => civic_number[2...4].to_i,
|
39
|
+
:day => civic_number[4...6].to_i
|
40
|
+
})
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
civic_number
|
45
|
+
end
|
46
|
+
|
47
|
+
class << self
|
48
|
+
def generate
|
49
|
+
date = Time.local(Time.now.year - rand(100) - 1, rand(12) + 1, rand(31) + 1)
|
50
|
+
Luhn.generate(10, :prefix => date.strftime("%y%m%d"))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def cleanup_string(string)
|
57
|
+
string.gsub!(/\D/, '')
|
58
|
+
string.length == 12 ? string[2...12] : string
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
data/lib/luhn/version.rb
ADDED
data/spec/helper.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Luhn::CivicNumber do
|
4
|
+
it 'identifies if the civic number is valid' do
|
5
|
+
Luhn::CivicNumber.new('3910304298').valid?.must_equal true
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'identifies if the civic number is invalid' do
|
9
|
+
Luhn::CivicNumber.new('3910304290').valid?.must_equal false
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'requires a length of 10 to be valid' do
|
13
|
+
luhn_string = Luhn.generate(8, :prefix => '391030')
|
14
|
+
Luhn::CivicNumber.new(luhn_string).valid?.must_equal false
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'requires the civic number to be a valid date' do
|
18
|
+
luhn_string = Luhn.generate(10, :prefix => '3999')
|
19
|
+
Luhn::CivicNumber.new(luhn_string).valid?.must_equal false
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'calculates the control digit for a valid civic number' do
|
23
|
+
Luhn::CivicNumber.new('3910304298').control_digit.must_equal 8
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'calculates the correct control digit for an invalid civic number' do
|
27
|
+
Luhn::CivicNumber.new('3910304290').control_digit.must_equal 8
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'identifies if the civic number belongs to a male' do
|
31
|
+
Luhn::CivicNumber.new('3910304298').sex.must_equal 'male'
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'identifies if the civic number belongs to a female' do
|
35
|
+
Luhn::CivicNumber.new('3910303183').sex.must_equal 'female'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'cleans up civic number with the full year supplied' do
|
39
|
+
Luhn::CivicNumber.new('19391030-3183').civic_number.must_equal '3910303183'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'knows the date of birth' do
|
43
|
+
civic_number = Luhn::CivicNumber.new('3001018194')
|
44
|
+
civic_number.birth_date.year.must_equal 30
|
45
|
+
civic_number.birth_date.month.must_equal 1
|
46
|
+
civic_number.birth_date.day.must_equal 1
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'generates a valid random civic number' do
|
50
|
+
civic_number = Luhn::CivicNumber.generate
|
51
|
+
Luhn::CivicNumber.new(civic_number).valid?.must_equal true
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe String do
|
4
|
+
it 'knows if it is a valid luhn' do
|
5
|
+
'3910304298'.valid_luhn?.must_equal true
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'knows if it is an civic number' do
|
9
|
+
'3910304298'.civic_number?.must_equal true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Numeric do
|
14
|
+
it 'knows if it is a valid luhn' do
|
15
|
+
3910304298.valid_luhn?.must_equal true
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'knows if it is an civic number' do
|
19
|
+
3910304298.civic_number?.must_equal true
|
20
|
+
end
|
21
|
+
end
|
data/spec/spec_luhn.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
describe Luhn do
|
4
|
+
it "identifies if the number is valid" do
|
5
|
+
Luhn.valid?('1111111116').must_equal true
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'identifies if the number is invalid' do
|
9
|
+
Luhn.valid?('1111111111').must_equal false
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'generates a number string that satisfies luhn' do
|
13
|
+
luhn_string = Luhn.generate(rand(32)+1)
|
14
|
+
Luhn.valid?(luhn_string).must_equal true
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'generate a number string with the size of the argument' do
|
18
|
+
luhn_string = Luhn.generate(5)
|
19
|
+
luhn_string.size.must_equal 5
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'generate a a valid luhn with a given prefix' do
|
23
|
+
luhn_string = Luhn.generate(10, :prefix => '00001')
|
24
|
+
luhn_string.must_match /^00001/
|
25
|
+
Luhn.valid?(luhn_string).must_equal true
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'calculates the control digit' do
|
29
|
+
Luhn.control_digit('111111111').must_equal 6
|
30
|
+
end
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: luhn
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.1.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- "Joel Junstr\xC3\xB6m"
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-09-26 00:00:00 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: minitest
|
17
|
+
prerelease: false
|
18
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
19
|
+
none: false
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 2.6.0
|
24
|
+
type: :development
|
25
|
+
version_requirements: *id001
|
26
|
+
description:
|
27
|
+
email:
|
28
|
+
- joel.junstrom@gmail.com
|
29
|
+
executables: []
|
30
|
+
|
31
|
+
extensions: []
|
32
|
+
|
33
|
+
extra_rdoc_files: []
|
34
|
+
|
35
|
+
files:
|
36
|
+
- lib/luhn/civic_number.rb
|
37
|
+
- lib/luhn/extensions/numeric.rb
|
38
|
+
- lib/luhn/extensions/string.rb
|
39
|
+
- lib/luhn/extensions.rb
|
40
|
+
- lib/luhn/version.rb
|
41
|
+
- lib/luhn.rb
|
42
|
+
- spec/helper.rb
|
43
|
+
- spec/spec_civic_number.rb
|
44
|
+
- spec/spec_extensions.rb
|
45
|
+
- spec/spec_luhn.rb
|
46
|
+
- LICENSE
|
47
|
+
- README.rdoc
|
48
|
+
homepage: http://github.com/joeljunstrom/ruby_luhn
|
49
|
+
licenses: []
|
50
|
+
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
|
54
|
+
require_paths:
|
55
|
+
- lib
|
56
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
requirements: []
|
69
|
+
|
70
|
+
rubyforge_project:
|
71
|
+
rubygems_version: 1.8.8
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: A small implementation of the Luhn algorithm.
|
75
|
+
test_files: []
|
76
|
+
|