Ruby Quiz wrote:
> This week's Ruby Quiz is to write a program that accepts a credit card
> number as a command-line argument.  The program should print the card's
> type (or Unknown) as well a Valid/Invalid indication of whether or not the
> card passes the Luhn algorithm.

I'm rather new to ruby and this is the first time I participated in this quiz.
If found this one to be rather easy and fun to work with.
Here's my solution:

#!/usr/bin/env ruby

class CreditCard
  attr_reader :number
  CardTypes = [
    { :name => "AMEX", :regex => /(34|37)\d{13}/, :luhn => true},
    { :name => "Bankcard", :regex => /5610\d{12}/, :luhn => true},
    { :name => "Bankcard", :regex => /56022[1-5]\d{10}/, :luhn => true},
    { :name => "China Union Pay", :regex => /622\d{13}/, :luhn => false},
    { :name => "DC-CB", :regex => /30[0-5]\d{11}/, :luhn => true},
    { :name => "DC-eR", :regex => /2(014|149)\d{11}/, :luhn => false},
    { :name => "DC-Int", :regex => /36\d{12}/, :luhn => true},
    { :name => "DC-UC or MasterCard", :regex => /55\d{14}/, :luhn => true},
    { :name => "Discover", :regex => /6011\d{12}/, :luhn => true},
    { :name => "MasterCard", :regex => /5[1-4]\d{14}/, :luhn => true},
    { :name =>"Maestro", :regex => /(5020|5038|6759)\d{12}/, :luhn => true},
    { :name => "Visa", :regex => /4(\d{13}|\d{16})/, :luhn => true},
    { :name => "Unknown", :regex => //, :luhn => true} ]
    # If the credit card is of unknown type, we'll just assume
    # that it can be verified using the Luhn algorithm.

  def initialize(num)
    self.number=num
  end

  def number=(num)
    raise ArgumentError, "Supplied argument is not a number" unless
                                                     num.to_s =~ /^[-_\s\d]+$/
    @number=num.to_s.gsub(/(\s|_|-)/,'')
    @type=nil
    @validity=nil
  end

  def card_type
    @type||=CardTypes.detect {|i| i[:regex].match @number}
  end

  def to_s
    "Number: #{@number}, Type: #{card_type[:name]}, Valid: #{valid?}"
  end

  def valid?
    return @validity unless @validity.nil?
    return @validity="unknown" unless card_type[:luhn]
    arr=@number.split(//).reverse.map {|x| x.to_i}
    arr.each_with_index{|v,i| arr[i]=v*2 if i%2==1}
    sum=arr.join.split(//).map do |x| x.to_i end.inject {|s,i| i+s}
    @validity = sum%10==0
  end
end

if __FILE__==$0
  card=CreditCard.new(if ARGV.empty?
                                  puts "Please enter your credit card number:"
                                  gets.chomp
                                else
                                  ARGV.join
                                end)
  puts card
end