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 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.
@@ -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.
@@ -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
@@ -0,0 +1,3 @@
1
+ # encoding: UTF-8
2
+ require 'luhn/extensions/string'
3
+ require 'luhn/extensions/numeric'
@@ -0,0 +1,11 @@
1
+ # encoding: UTF-8
2
+ class Numeric
3
+
4
+ def valid_luhn?
5
+ self.to_s.valid_luhn?
6
+ end
7
+
8
+ def civic_number?
9
+ self.to_s.civic_number?
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ # encoding: UTF-8
2
+ class String
3
+ def valid_luhn?
4
+ Luhn.valid?(self)
5
+ end
6
+
7
+ def civic_number?
8
+ Luhn::CivicNumber.new(self).valid?
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ # encoding: UTF-8
2
+ module Luhn
3
+ Version = '0.1.1'
4
+ end
@@ -0,0 +1,7 @@
1
+ $:.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
2
+ require 'rubygems'
3
+ gem 'minitest'
4
+ require 'luhn'
5
+ require 'minitest/benchmark' if ENV["BENCH"]
6
+ require 'minitest/spec'
7
+ require 'minitest/autorun'
@@ -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
@@ -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
+