Please Forward: Ruby Quiz Submission

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

ยทยทยท

Begin forwarded message: