Begin forwarded message: > From: Alex Wayne <alex / beautifulpixel.com> > Date: May 1, 2007 8:08:59 PM CDT > To: submission / rubyquiz.com > Subject: Please Forward: Ruby Quiz Submission > > A simple approach. It has 2 classes. One class parses the card > number, the other provides a command line interface to the first > class. The harder part (the Luhn calculation) is heavily commented > for clarity. > > For brevity on the command line, I named the program ccinfo.rb. > The long winded "cc_validator_class.rb" is for the library that > does allthe heavily lifting but is not called directly. To use it > stick both files in a directory and type: > > ruby ccinfo.rb 4111111111111111 > > Thanks for the quizzes! > > -Alex Wayne > http://beautifulpixel.com > class CCValidator > > attr_reader :number, :card_type > > def initialize(number) > @number = number.to_s > @card_type = parse_type(number) > @valid = parse_valid(number) > end > > def valid? > @valid > end > > private > > # A simple Regex case statement returns the card type > def parse_type(number) > case number > when /^3(4|7)\d{13}$/ > :amex > when /^6011\d{12}$/ > :discover > when /^5[1-5]\d{14}$/ > :mastercard > when /^4(\d{12}|\d{15})$/ > :visa > else > :unknown > end > end > > # Calculate number validity > def parse_valid(number) > # Rather than starting to double on the second to last > number, we decide wether or > # not we can start on the first number by how many digits the > number has. If it's > # even the first number gets double and then every other > number after. If it's odd > # start double on the second digit, thereby "offset"-ing the > progression. > offset = (number.size % 2 == 0) ? 0 : 1 > > # covert number string to an array of integer digits. > digits = number.split('').collect { |digit| digit.to_i } > > # Iterate through the digit array, double every other digit. > Each is not used here > # because we need to keep track of the index and stuff the > result back in the original > # array. > digits.size.times do |i| > if (i + offset) % 2 == 0 > digits[i] = digits[i] * 2 > end > end > > # convert array with doubled integer digits, into strings. > Then split all elements > # back into single digits. > digits = digits.collect { |digit| digit.to_s.split('') } > > # Flatten the potentialilly nested arrays and convert all > digits back to integers for > # addition. > digits = digits.flatten.collect { |digit| digit.to_i } > > # Add up the digits and see if the result is a multiple of > 10, proving that the card > # number is valid. > digits.inject(0) { |sum, digit| sum + digit } % 10 == 0 > end > > end > require "cc_validator_class" > > class CCValidator > class CommandLine > def initialize > if @number = ARGV.shift > write_output > else > write_usage > end > end > > def write_output > result = CCValidator.new(@number) > puts <<-OUTPUT > =============================== > Credit Card Validator > > Card Number: #{result.number} > > Type: #{result.card_type.to_s.capitalize} > Valid: #{result.valid? ? 'YES' : 'NO'} > > OUTPUT > end > > def write_usage > puts <<-USAGE > =============================== > Credit Card Validator > > This program will tell the type of credit card and if the number is > valid. To use, simply > provide a credit card number on the command line, and the results > will be immediately > displayed. > > EXAMPLE: > ruby ccinfo.rb 4111111111111111 > > USAGE > end > end > end > > CCValidator::CommandLine.new