Avatar

Code for checking swedish "personnummer".

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
  def validate_ssn
    # Kontrollera antal siffror i personnummret.
    if ssn.size != 10
      errors.add_to_base "Personnummret innehåller fel antal siffror. (ÅÅMMDDXXXX)"
    end
    
    # Räkna ut kontrollsiffran
    sum = tmp = 0

    (0..8).each do |i|
      if i % 2 == 0 
        if (tmp = 2 * ssn[i,1].to_i) > 9
          sum = sum + tmp - 9
        else
          sum = sum + tmp
        end
      else
        sum = sum + ssn[i,1].to_i
      end
    end

    if (sum % 10) != 0
      if (10 - (sum % 10)) != ssn[9,1].to_i
        errors.add_to_base 'Personnummret måste vara riktigt.'
      end
    else
      if ssn[9,1] != 0
        errors.add_to_base 'Personnummret måste vara riktigt.'
      end
    end
    
  end

Refactorings

No refactoring yet !

7f00244a6387413b37ee449f234ec045

Marc-Andre

April 7, 2009, April 07, 2009 18:26, permalink

No rating. Login to rate!

My Swedish is a bit rusty, but isn't that simply Luhn's check?
You can add all the numbers and check the sum (mod 10) is 0.
You might want to add a check that only numbers have been passed.
I didn't check the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def passes_luhn_check?(str)
  sum = 0
  numbers = str.split(//).map(&:to_i)
  numbers.each_with_index do |n, i|
    n *= 2 if i.even?
    n -= 9 if n >= 10
    sum += n
  end
  sum % 10 == 0
end

def validate_ssn
  # Kontrollera antal siffror i personnummret.
  if ssn.size != 10
    errors.add_to_base "Personnummret innehåller fel antal siffror. (ÅÅMMDDXXXX)"
  end
  
  # Räkna ut kontrollsiffran
  errors.add_to_base 'Personnummret måste vara riktigt.' unless passes_luhn_check?(ssn)
end
Avatar

Kratshi

June 23, 2009, June 23, 2009 08:48, permalink

No rating. Login to rate!

19730401

0acd844648005ae0cdd0cbb5a4a28b06

Canotooge-tool

December 5, 2009, December 05, 2009 08:23, permalink

No rating. Login to rate!

lart mycket

E0a752b8c2233d4879d614ed221125b3

Henry

July 18, 2010, July 18, 2010 12:19, permalink

No rating. Login to rate!
1
Henry
Ce5a35c350f4da824b7495f1408c9aaa

Joel Junström

August 18, 2010, August 18, 2010 07:58, permalink

No rating. Login to rate!

I'd wrap it in a class, it's a bit more verbose but you can reuse it instead of just having it as a validation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
require 'enumerator'
module Luhn
  class CivicNumber

    attr_reader :civic_number

    def initialize(civic_number)
      @civic_number = civic_number.to_s.gsub(/\D/, '')
    end

    def valid?
      civic_number.length == 10 && checksum(civic_number) % 10 == 0
    end

    def control_digit
      10 - checksum(civic_number[0...9]) % 10
    end

    def sex
      valid? ? (civic_number[8...9].to_i.even? ? 'female' : 'male') : 'unknown'
    end

    def female?
      sex == 'female'
    end

    def male?
      sex == 'male'
    end

  private

    def checksum(value)
      sum = 0
      value.split(//).enum_for(:each_with_index).map do |c, i|
        n = c.to_i
        n *= 2 if i.even?
        n -= 9 if n >= 10
        sum += n
      end
      sum
    end

  end
end

class String
  def as_civic_number
    Luhn::CivicNumber.new(self)
  end
end

class Numeric
  def as_civic_number
    Luhn::CivicNumber.new(self.to_s)
  end
end
Ce5a35c350f4da824b7495f1408c9aaa

Joel Junström

August 18, 2010, August 18, 2010 08:13, permalink

No rating. Login to rate!

Err, "slight" bug in previous example. Just remembered that the algorithm for the swedish civic number does not reduce the factor by 9 if it's >= 10 but instead adds each number (ie if first is 8 then 8 * 2 = 16 then the final checksum is calculated 1 + 6)

1
2
3
4
5
    def checksum(value)
       value.split(//).enum_for(:each_with_index).map do |n, i|
        n.to_i * (i.even? ? 2 : 1)
      end.to_s.split(//).inject(0) { |sum, c| sum += c.to_i }
    end

Your refactoring





Format Copy from initial code

or Cancel