# \[QUIZ\] Checking Credit Cards (#122)

**URL:** <https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93249>\
**Category:** Ruby\
**Created:** [April 30, 2007, 1:23am UTC](https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93249 "2007-04-30T01:23:49Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [April 30, 2007, 1:23am UTC](https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93249/1 "2007-04-30T01:23:49Z")

</div>

I was trying to go for “most compact and obfuscated version of the  
Luhn algorithm”, but unfortunately the complications of the algorithm  
meant that I had to debug my implementation, which left it slightly  
more readable. Oh well. It’s still pretty dense and while my type  
method might be readable it’s as dense as I care to make it.

#!ruby -x  
def type(s)  
case s.gsub(/\D/,’’)  
when /^(?=34).{15}$/; “AMEX”  
when /^(?=37).{15}$/; “AMEX”  
when /^(?=6011).{16}$/; “Discover”  
when /^(?=5[1-5]).{16}$/; “MasterCard”  
when /^(?=4).{13}(…)?$/; “Visa”  
else ; “Unknown”  
end  
end

def luhn(s)  
s.scan(/\d/).inject([0,0]){|(a,b),c|[b+c.to\_i, a+c.to\_i\*2%9+(c==‘9’ ? 9 : 0)]}[0]%10 == 0  
end

s = ARGV.join  
puts “#{type(s)} #{luhn(s)?‘V’:‘Inv’}alid”

**END**

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [April 30, 2007, 1:30am UTC](https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93249/2 "2007-04-30T01:30:50Z")

</div>

My submission is pastied here:

[http://pastie.caboo.se/57536](http://pastie.caboo.se/57536)

and pasted below. I tried to come up with a solution that would allow  
easy manipulation of the list of credit card providers.

- donald

# Ruby Q. 123

# Donald B.

# version 1.0

require ‘enumerator’

class Integer  
def digits  
self.to\_s.scan(/\d/).map{|n| n.to\_i}  
end

def sum\_digits  
digits.inject{|sum, n| sum + n}  
end

def luhn  
digits.reverse.enum\_slice(2).inject(0) do |sum, sl|  
sum + sl[0].sum\_digits + (sl.length == 2 ? (2\*sl[1]).sum\_digits :  
0)  
end  
end

def luhn?  
luhn % 10 == 0  
end  
end

module Credit  
class Provider  
attr\_reader :name

```
def initialize(name, pattern)
  @name = name
  @pattern = pattern
end

def valid?(number)
  @pattern.match(number)
end

def to_s
  @name
end

```

end

Providers = []  
Providers \<\< Provider.new(‘AMEX’, /^(34|37)\d{13}$/)  
Providers \<\< Provider.new(‘Discover’, /^6011\d{12}$/)  
Providers \<\< Provider.new(‘MasterCard’, /^5(1|2|3|4|5)\d{14}$/)  
Providers \<\< Provider.new(‘Visa’, /^4(\d{12}|\d{15})$/)

class Card  
attr\_reader :number

```
def initialize(number)
  if number.is_a? Integer
    @number = number
  elsif number.is_a? String
    @number = number.gsub(/\s/, '').to_i
  else
    raise InvalidArgument, number
  end
end
def provider
  Providers.each do |provider|
    return provider if provider.valid?(@number.to_s)
  end
  return 'Unknown'
end
def valid?
  @number.luhn?
end
def to_s
  @number.to_s
end

```

end  
end

card = Credit::Card.new(ARGV[0])  
puts card.provider.to\_s \<\< ’ ’ \<\< (card.valid? ? ‘valid’ : ‘invalid’)

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [April 30, 2007, 5:02am UTC](https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93249/3 "2007-04-30T05:02:01Z")

</div>

On Thursday 30 May 2002, Jeff Gray wrote:

> to the ‘method=’ style of method naming. For example, you could define a  
> (Simply removing the ‘=’ from the method name allows the second case to  
> work.) I couldn’t find anything in the pickaxe book or in ruby-talk  
> archives about this restriction. I’m on Ruby version 1.6.5 – has this  
> behavior changed in later versions, or is it a permanent trait of Ruby  
> syntax?
> 
> Thanks,
> 
> - jeff

Also consider the test case below, where a method with = in its name  
always returns the value passed to it. This is really frustrating, as I  
want to use = in method names as syntactic sugar but can’t because the  
methods don’t behave like they’re supposed to.  
Is this a bug or a “feature”?

This script:

class TestClass  
def test=(number)  
# 42 is the answer to life the universe and everything.  
42  
end  
def test(number)  
# 42 is the answer to life the universe and everything.  
42  
end  
end

test = TestClass.new

puts ‘With the = in the method name:’  
puts test.test=7  
puts test.test=(“Doesn’t work with parentheses either.”)  
puts test.test=[1,2,3,“Arrays are cool!”]

puts ‘And without:’  
puts test.test(7)  
puts test.test(“Doesn’t work with parentheses either.”)  
puts test.test([1,2,3,“Arrays are cool!”])

Produces output:

With the = in the method name:  
7  
Doesn’t work with parentheses either.  
1  
2  
3  
Arrays are cool!  
And without:  
42  
42  
42

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [April 30, 2007, 1:31pm UTC](https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93249/4 "2007-04-30T13:31:31Z")

</div>

Daniel M. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) writes:

> I was trying to go for “most compact and obfuscated version of the  
> Luhn algorithm”, but unfortunately the complications of the algorithm  
> meant that I had to debug my implementation, which left it slightly  
> more readable. Oh well. It’s still pretty dense and while my type  
> method might be readable it’s as dense as I care to make it.

You know, it’s longer, but I think I like this definition of the luhn  
algorithm better, in terms of its conceptual conciseness:

def luhn(s)  
s.scan(/\d/).map{|x|x.to\_i}.inject([0,0]){  
|(a,b),c|[b+c%9,a+2\*c%9]}[0]%10 == s.scan(/9/).size%10  
end

(Now - the challenge is to figure out why that works)

An interesting observation based on this representation is that the  
Luhn algorithm will fail to catch the transposition of an adjacent “0”  
and “9”:

both ‘446-667-6097’ and ‘446-667-6907’ pass.

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [May 1, 2007, 3:39pm UTC](https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93249/5 "2007-05-01T15:39:06Z")

</div>

The only reason I’m submitting this is because I added a  
inject\_with\_index method to Array. I liked Daniel M.'s use of the  
inject([0,0]), I didn’t even think of doing that.

class Array  
def inject\_with\_index(injected)  
each\_with\_index{|obj, index| injected = yield(injected, obj, index)  
}  
injected  
end  
end

class CreditCard  
@@types = {  
‘AMEX’ =\> { :start =\> [34, 37], :length =\> [15] },  
‘Discover’ =\> { :start =\> [6011], :length =\> [16] },  
‘MasterCard’ =\> { :start =\> (51…55).to\_a, :length =\> [16] },  
‘Vista’ =\> { :start =\> [4], :length =\> [13, 16] }  
}  
attr :type, true  
attr :valid, true  
def initialize(number)  
@type = (@@types.find do |card\_type, card\_st|  
card\_st[:start].any?{|st| /^#{st}/ =~ number } and  
card\_st[:length].any?{|le| number.length == le }  
end || [‘Unknown’]).first  
@valid = number.reverse.split(’’).inject\_with\_index(0) do  
|acc,num,ind|  
acc + ( ind%2 == 0 ? num.to\_i :  
(num.to\_i\*2).to\_s.split(’’).inject(0){|a,e|a.to\_i+e.to\_i} )  
end % 10 == 0  
puts " Type: #{@type}"  
puts “Valid: #{@valid}”  
end  
end

---

<div class="post-metadata">

**Author:** ![danmcb](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/d/13edae/32.png) [@danmcb](https://www.ruby-forum.com/u/danmcb)\
**Post date:** [April 30, 2007, 2:45pm UTC](https://www.ruby-forum.com/t/quiz-checking-credit-cards-122/93249/6 "2007-04-30T14:45:36Z")

</div>

This was my first rubyquiz. I took a meta programming approach, since  
it seemed like an easy enough example for it, and I haven’t done  
enough meta programming yet. The only one of the solutions above that  
I saw using some type of dynamic programming was the one using  
method\_missing above. Critiques welcome. For those interested, I did  
manage to write a more details description on my blog:

> **[Ruby Quiz #122 Solution: Checking Credit Cards using meta-programming](http://webjazz.blogspot.com/2007/04/ruby-quiz-122-solution-checking-credit.html)**
>
> So this is the first time I actually did a RubyQuiz for real. I spent probably 3 or 4 hours on it. Not too shabby. And, I got to do a li...

Metaprogramming is probably overkill for this, since you don’t get a  
new type of credit card company all the time, but it’s probably a nice  
technique for when you have an object that needs lots of different  
combinations of things each time.

Here is my solution:

require ‘enumerator’

class CreditCardChecker

def self.metaclass; class \<\< self; self; end; end

class \<\< self  
attr\_reader :cards

```
# writes a method with the card_name? as a method name. The

```

method created would  
# check what type of credit card a number is, based on the rules  
given in the block.  
# Use this function in the subclass  
#  
# class MyChecker \< CreditCardChecker  
# credit\_card(:amex) { |cc| (cc =~ /^34._/ or cc =~ /^37._/)  
and (cc.length == 15) }  
# end  
def credit\_card(card\_name, &rules)  
@cards ||= []  
@cards \<\< card\_name

```
  metaclass.instance_eval do
    define_method("#{card_name}?") do |cc_num|
      return rules.call(cc_num) ? true : false
    end
  end
end

```

end

def cctype(cc\_num)  
self.class.cards.each do |card\_name|  
return card\_name if self.class.send(“#{card\_name}?”,  
normalize(cc\_num))  
end  
return :unknown  
end

def valid?(cc\_num)  
rev\_num = []  
normalize(cc\_num).split(‘’).reverse.each\_slice(2) do |pair|  
rev\_num \<\< pair.first.to\_i \<\< pair.last.to\_i \* 2  
end  
rev\_num = rev\_num.to\_s.split(‘’)  
sum = rev\_num.inject(0) { |t, digit| t += digit.to\_i }  
(sum % 10) == 0 ? true : false  
end

private  
def normalize(cc\_num)  
cc\_num.gsub(/\s+/, ‘’)  
end  
end

class CreditCard \< CreditCardChecker  
credit\_card(:amex) { |cc| (cc =~ /^34._/ or cc =~ /^37._/) and  
(cc.length == 15) }  
credit\_card(:discover) { |cc| (cc =~ /^6011._/) and (cc.length ==  
16) }  
credit\_card(:mastercard) { |cc| cc =~ /^5[1-5]._/ and (cc.length ==  
16) }  
credit\_card(:visa) { |cc| (cc =~ /^4.\*/) and (cc.length == 13 or  
cc.length == 16) }  
end

CCnum = ARGV[0]

cccheck = CreditCard.new  
puts cccheck.cctype(CCnum)  
puts cccheck.valid?(CCnum)
