[http://d.hatena.ne.jp/nappa_zzz/20070429:title]

こんなもんでよいのかな。

# Ruby Quiz #122
# checking credit card
#
class CreditCard

   def initialize(number)
      @number = number
      nomalize = number.gsub(/\s/,"")
      @type  = check_type(nomalize)
      @valid = check_valid(nomalize)
   end

   def valid?
      @valid
   end
   attr_reader :type
   attr_reader :number

   private

   def check_valid(number)
      flag = false
      number.unpack('C*').reverse.inject(0) do |r,n|
         i = n.chr.to_i
         i = (i * 2 / 10) + (i * 2 % 10) if flag
         flag = !flag
         r + i
      end % 10 == 0
   end

   def check_type(number)
      @@type_re ||= %r/
         (\A3[47])  # $1 amex
       | (\A6011)   # $2 discover
       | (\A5[1-5]) # $3 mastercard
       | (\A4)      # $4 visa
      /x
      len = number.length
      number =~ @@type_re
      case
      when ($1 and len == 15) then :amex
      when ($2 and len == 16) then :discover
      when ($3 and len == 16) then :mastercard
      when ($4 and (len == 13 or
                    len == 16)) then :visa
      else                         :unknown
      end
   end
end

if __FILE__ == $0
   card = CreditCard.new(ARGV.join())
   puts "#{card.number} is #{card.type} card. " +
        "card number is #{card.valid? ? "valid" : "invalid"}."
end
> ruby ccard.rb 4408041234567893
4408041234567893 is visa card. card number is valid.
> ruby ccard.rb 4417 1234 5678 9112
4417123456789112 is visa card. card number is invalid.

21時すぎたから

回答がpostされてる。見比べると、injectが汚い。each_with_indexで実装したほうがすこしきれいかなぁ。あとsplit使えばよかった。chrがへる。