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