[QUIZ] Checking Credit Cards (#122)

Here is my submission. My first version was a bit shorter and more
concise, where I had embedded the allowed lengths into the regex. I
already had it written before the topic of 12 digits starting with 4
came up. So, upon James’ first decision to call that an invalid Visa, I
refactored and left it that way.

cheers,
brian

if ARGV.size == 0
puts “You must supply a credit card number to be checked.”
abort
end

input = ARGV.join

class CardCompany
class << self
def is_issuer_of?(data)
data =~ self::ValidRegex
end

def valid_card?(data)
  return false if invalid_length?(data)

  reversed = data.reverse
  sum = 0

  1.step(reversed.length - 1, 2) do |i|
    digit = reversed[i..i].to_i
    if digit < 5
      sum += digit * 2
    else
      sum += ((digit * 2) % 10) + 1
    end
  end

  0.step(reversed.length - 1, 2) do |i|
    sum += reversed[i..i].to_i
  end

  sum % 10 == 0
end

def invalid_length?(data)
  return false if self::ValidLengths.empty?
  for length in self::ValidLengths
    return false if data.size == length
  end
  true
end

end
end

class AmexCardCo < CardCompany
ValidRegex = /^3[47]\d+$/
ValidLengths = [15]
Name = ‘AMEX’
end

class DiscoCardCo < CardCompany
ValidRegex = /^6011\d+$/
ValidLengths = [16]
Name = ‘Discover’
end

class MasterCardCo < CardCompany
ValidRegex = /^5[1-5]\d+$/
ValidLengths = [16]
Name = ‘MasterCard’
end

class VisaCardCo < CardCompany
ValidRegex = /^4\d+$/
ValidLengths = [13, 16]
Name = ‘Visa’
end

class UnknownCardCo < CardCompany
ValidRegex = /^\d+$/
ValidLengths = []
Name = ‘Unknown’
end

SYSTEMS = [ AmexCardCo, DiscoCardCo, MasterCardCo, VisaCardCo,
UnknownCardCo ]

for system in SYSTEMS
if system.is_issuer_of?(input)
found = true
valid = system.valid_card?(input)
puts “#{system::Name} #{valid ? ‘Valid’ : ‘Invalid’}”
break
end
end

puts “Invalid input” if !found