This quiz reminded me of my days in credit card processing.  The
weighted checksum for routing numbers is more interesting, but the
sort-of-two-pass aspect of the Luhn algorithm is a great stumbling
block.  My solution follows.  You'll notice I liked your ARGV.join
trick for the input.


class Array
  def cycle!
    push(shift)
  end
end

class CCNum < String
  PATTERNS = {
    'AMEX'       => { :start => ['34', '37'], :length => 15 },
    'Discover'   => { :start => ['6011', '65'], :length => 16 },
    'MasterCard' => { :start => (51..55).to_a.collect { |n|
n.to_s }, :length => 16 },
    'Visa'       => { :start => '4', :length => [13, 16] },
  }.freeze

  def initialize(*args)
    super
    gsub!(/\D/, '')
    @factors = [1,2]
    @factors.cycle! if (length % 2) == 1
  end

  def type
    return @type if @type
    PATTERNS.each do |name, pat|
      @type = name if [pat[:start]].flatten.any? { |s|  match /
^#{s}/ } and [pat[:length]].flatten.any? { |l|  length == l }
    end
    @type ||= 'Unknown'
  end

  def luhn_sum
    @luhn_sum ||= split('').inject(0) do |sum, digit|
      @factors.cycle!
      sum += (digit.to_i * @factors.first).to_s.split('').inject(0) { |
s,d|  s += d.to_i }
    end
  end

  def luhn_valid?
    (luhn_sum % 10) == 0
  end
end

card_num = CCNum.new(ARGV.join)
puts "#{card_num} is a(n) #{card_num.luhn_valid? ? 'V' : 'Inv' }alid
#{card_num.type}"