From: Ryan Leavengood Date: 2007-04-29T23:42:26+09:00 Subject: Re: [SPAM] Re: [QUIZ] Checking Credit Cards (#122) Here is my solution, including unit tests. I'm pretty pleased with my luhn check. require 'enumerator' class CardType < Struct.new(:name, :pattern, :lengths) def match(cc) (cc =~ pattern) and lengths.include?(cc.length) end def to_s name end end class CardValidator @types = [ CardType.new('AMEX', /^(34|37)/, [15]), CardType.new('Discover', /^6011/, [16]), CardType.new('MasterCard', /^5[1-5]/, [16]), CardType.new('Visa', /^4/, [13,16]) ] def self.card_type(cc) @types.find {|type| type.match(cc) } end def self.luhn_check(cc) # I like functional-style code (though this may be a bit over the top) (cc.split('').reverse.enum_for(:each_slice, 2).inject('') do |s, (a, b)| s << a + (b.to_i * 2).to_s end.split('').inject(0) {|sum, n| sum + n.to_i}) % 10 == 0 end end require 'test/unit' class CardValidatorTest < Test::Unit::TestCase def test_card_type assert_equal('AMEX', CardValidator.card_type('341122567979797').name) assert_equal('AMEX', CardValidator.card_type('371122567979797').name) assert_equal('Discover', CardValidator.card_type('6011123456781122').name) assert_equal('MasterCard', CardValidator.card_type('5115666677779999').name) assert_equal('MasterCard', CardValidator.card_type('5315666677779999').name) assert_equal('Visa', CardValidator.card_type('4408041234567893').name) assert_equal('Visa', CardValidator.card_type('4417123456789112').name) assert_equal('Visa', CardValidator.card_type('4417123456789').name) assert_nil(CardValidator.card_type('3411225679797973')) assert_nil(CardValidator.card_type('601112345678112')) assert_nil(CardValidator.card_type('51156666777799')) assert_nil(CardValidator.card_type('5615666677779989')) assert_nil(CardValidator.card_type('1111222233334444')) assert_nil(CardValidator.card_type('44171234567898')) end def test_luhn_check assert(CardValidator.luhn_check('1111222233334444')) assert(CardValidator.luhn_check('4408041234567893')) assert(!CardValidator.luhn_check('4417123456789112')) assert(!CardValidator.luhn_check('6011484800032882')) end end if $0 == __FILE__ abort("Usage: #$0 or -t to run unit tests") if ARGV.length < 1 if not ARGV.delete('-t') Test::Unit.run = true cc = ARGV.join.gsub(/\s*/, '') type = CardValidator.card_type(cc) puts "Card type is: #{type ? type : 'Unknown'}" puts "The card is #{CardValidator.luhn_check(cc) ? 'Valid' : 'Invalid'}" end end