bank_card 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -5,15 +5,25 @@
5
5
 
6
6
  == Description
7
7
 
8
- This gem contains validation of card number with Luhn method, card brand detection (Visa, MasterCard, etc.)
8
+ This gem contains
9
+ * validation methods to check card number with Luhn method, card expiration date
10
+ * brand detection method for Visa, MasterCard, JCB, Discover, Maestro, Diners Club, American Express.
9
11
 
10
- == Examples
12
+ == Example
11
13
 
12
14
  class CreditCard < ActiveRecord::Base
13
15
  include BankCard::Validations
16
+ include BankCard::Brands
14
17
 
15
18
  validates :number, :luhn => true
16
19
  validates_expiration_of :date
20
+
21
+ before_create :set_brand
22
+
23
+ private
24
+ def set_brand
25
+ self.brand = detect_brand(number)
26
+ end
17
27
  end
18
28
 
19
29
  You can get more info from specs
@@ -0,0 +1,76 @@
1
+ module BankCard
2
+ module Brands
3
+
4
+ def detect_brand(number)
5
+ brand = [:visa, :american_express, :master, :jcb, :diners_club, :discover, :maestro].
6
+ detect { |brand| send("#{brand}?", number.to_s) }
7
+
8
+ brand ? brand : :unknown
9
+ end
10
+
11
+ private
12
+ # begin with 4 length 13, 16 or more
13
+ def visa?(number)
14
+ number =~ /^
15
+ 4( # begin with 4
16
+ (\d{12})| # and length 13
17
+ (\d{15,}) # and length 16 or more
18
+ )
19
+ $/x
20
+ end
21
+
22
+ # begin with 34 or 37 and length 15
23
+ def american_express?(number)
24
+ number =~ /^3[47]\d{13}$/
25
+ end
26
+
27
+ # begin from 51 to 55 and length 16
28
+ def master?(number)
29
+ number =~ /^5[1-5]\d{14}$/
30
+ end
31
+
32
+ # begin from 3528 to 3589 and length 16
33
+ def jcb?(number)
34
+ number =~ /^ # begin with
35
+ 35( # 35
36
+ (2[89])| # 28 to 29
37
+ ([3-8][0-9]) # 30 to 89
38
+ )
39
+ \d{12} # and length 16
40
+ $/x
41
+ end
42
+
43
+ # begin from 300 to 305 and length 14
44
+ # or begin from 36 and length 14
45
+ def diners_club?(number)
46
+ number =~ /^ # begin
47
+ (30[0-5]\d{11})| # from 300 to 305 and length 14
48
+ (36\d{12}) # with 36 and length 14
49
+ $/x
50
+ end
51
+
52
+ # begin with 6011, 65, from 644 to 649, from 622126 to 622925 and length 16
53
+ def discover?(number)
54
+ number =~ /^ # begin with
55
+ (6011\d{12})| # 6011 and length 16
56
+ (65\d{14})| # 65 and length 16
57
+ (64[4-9]\d{13})| # 644 to 649 and length 16
58
+ (622 # 622
59
+ (
60
+ (12[6-9])| # 126 to 129
61
+ (1[3-9][0-9])| # 130 to 199
62
+ ([2-8]\d{2})| # 200 to 899
63
+ (9[01]\d)| # 900 to 919
64
+ (92[0-5]) # 920 to 925
65
+ )\d{10} # and length 16
66
+ )
67
+ $/x
68
+ end
69
+
70
+ # begin with 5018, 5020, 5038, 6304, 6759, 6761, 6762, 6763 and length from 12 to 19
71
+ def maestro?(number)
72
+ number =~ /^(5018|5020|5038|6304|6759|6761|6762|6763)\d{8,15}$/
73
+ end
74
+
75
+ end
76
+ end
@@ -1,3 +1,3 @@
1
1
  module BankCard
2
- VERSION = "0.0.4"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/bank_card.rb CHANGED
@@ -6,6 +6,7 @@ require 'active_support/core_ext/time/calculations'
6
6
  require 'active_support/i18n'
7
7
 
8
8
  require "bank_card/version"
9
+ require "bank_card/brands"
9
10
 
10
11
  module BankCard
11
12
  module Validations
@@ -0,0 +1,154 @@
1
+ require 'spec_helper'
2
+
3
+ class CreditCard
4
+ include BankCard::Brands
5
+
6
+ attr_accessor :number
7
+ end
8
+
9
+ describe BankCard::Brands do
10
+ describe "detected brand" do
11
+ let(:card) { CreditCard.new }
12
+ subject { card.detect_brand(number) }
13
+
14
+ context "when card number begins with 4" do
15
+ context "and its length 13 digits" do
16
+ let(:number) { '4234567890123' }
17
+ it { should == :visa }
18
+ end
19
+
20
+ context "and its length 16 digits" do
21
+ let(:number) { '4200000000000000' }
22
+ it { should == :visa }
23
+ end
24
+
25
+ context "and its length more than 16 digits" do
26
+ let(:number) { '420000000000000012' }
27
+ it { should == :visa }
28
+ end
29
+ end
30
+
31
+ [34, 37].each do |card_begin|
32
+ context "when card number begins with #{card_begin} and its length 15" do
33
+ let(:number) { "#{card_begin}3456789012345" }
34
+ it { should == :american_express }
35
+ end
36
+ end
37
+
38
+ (51..55).each do |card_begin|
39
+ context "when card number begins with #{card_begin} and its length 16" do
40
+ let(:number) { "#{card_begin}34567890123456" }
41
+ it { should == :master }
42
+ end
43
+ end
44
+
45
+ (3528..3589).each do |card_begin|
46
+ context "when card number begins with #{card_begin} and its length 16" do
47
+ let(:number) { "#{card_begin}567890123456" }
48
+ it { should == :jcb }
49
+ end
50
+ end
51
+
52
+ (300..305).each do |card_begin|
53
+ context "when card number begins with #{card_begin} and its length 14" do
54
+ let(:number) { "#{card_begin}45678901234" }
55
+ it { should == :diners_club }
56
+ end
57
+ end
58
+
59
+ context "when card number begins with 36 and its length 14" do
60
+ let(:number) { "36345678901234" }
61
+ it { should == :diners_club }
62
+ end
63
+
64
+ # 6011, 622126-622925, 644-649, 65 discover
65
+ context "when card number begins with 6011 and its length 16" do
66
+ let(:number) { "6011567890123456" }
67
+ it { should == :discover }
68
+ end
69
+
70
+ context "when card number begins with 65 and its length 16" do
71
+ let(:number) { "6534567890123456" }
72
+ it { should == :discover }
73
+ end
74
+
75
+ (644..649).each do |card_begin|
76
+ context "when card number begins with #{card_begin} and its length 16" do
77
+ let(:number) { "#{card_begin}4567890123456" }
78
+ it { should == :discover }
79
+ end
80
+ end
81
+
82
+ # discover begin from 622126 to 622925 and its length 16 - a lot of tests
83
+ # tested only the boundary cases
84
+ context "when card number begins with 622126 and its length 16" do
85
+ let(:number) { "6221267890123456" }
86
+ it { should == :discover }
87
+ end
88
+ context "when card number begins with 622129 and its length 16" do
89
+ let(:number) { "6221297890123456" }
90
+ it { should == :discover }
91
+ end
92
+ context "when card number begins with 622130 and its length 16" do
93
+ let(:number) { "6221307890123456" }
94
+ it { should == :discover }
95
+ end
96
+ context "when card number begins with 622199 and its length 16" do
97
+ let(:number) { "6221997890123456" }
98
+ it { should == :discover }
99
+ end
100
+ context "when card number begins with 622200 and its length 16" do
101
+ let(:number) { "6222007890123456" }
102
+ it { should == :discover }
103
+ end
104
+ context "when card number begins with 622899 and its length 16" do
105
+ let(:number) { "6228997890123456" }
106
+ it { should == :discover }
107
+ end
108
+ context "when card number begins with 622900 and its length 16" do
109
+ let(:number) { "6229007890123456" }
110
+ it { should == :discover }
111
+ end
112
+ context "when card number begins with 622919 and its length 16" do
113
+ let(:number) { "6229197890123456" }
114
+ it { should == :discover }
115
+ end
116
+ context "when card number begins with 622920 and its length 16" do
117
+ let(:number) { "6229207890123456" }
118
+ it { should == :discover }
119
+ end
120
+ context "when card number begins with 622925 and its length 16" do
121
+ let(:number) { "6229257890123456" }
122
+ it { should == :discover }
123
+ end
124
+
125
+ shared_examples_for "card length" do
126
+ context "and its length 12" do
127
+ let(:number) { "#{card_begin}#{'1'*(12 - card_begin.length)}" }
128
+ it { should == :maestro }
129
+ end
130
+ context "and its length greate than 12 and less then 19" do
131
+ let(:number) { "#{card_begin}#{'1'*(15 - card_begin.length)}" }
132
+ it { should == :maestro }
133
+ end
134
+ context "and its length 19" do
135
+ let(:number) { "#{card_begin}#{'1'*(19 - card_begin.length)}" }
136
+ it { should == :maestro }
137
+ end
138
+ end
139
+
140
+ [5018, 5020, 5038, 6304, 6759, 6761, 6762, 6763].each do |card_begin|
141
+ context "when card number begins with #{card_begin}" do
142
+ let(:card_begin) { card_begin.to_s }
143
+ it_should_behave_like "card length"
144
+ end
145
+ end
146
+
147
+ context "when not detect brand" do
148
+ let(:number) { "111213131290899" }
149
+ it { should == :unknown }
150
+ end
151
+
152
+ end
153
+
154
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bank_card
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-10-03 00:00:00.000000000 +03:00
13
- default_executable:
12
+ date: 2012-02-22 00:00:00.000000000Z
14
13
  dependencies:
15
14
  - !ruby/object:Gem::Dependency
16
15
  name: rspec
17
- requirement: &2153678240 !ruby/object:Gem::Requirement
16
+ requirement: &2153585680 !ruby/object:Gem::Requirement
18
17
  none: false
19
18
  requirements:
20
19
  - - ! '>='
@@ -22,10 +21,10 @@ dependencies:
22
21
  version: '0'
23
22
  type: :development
24
23
  prerelease: false
25
- version_requirements: *2153678240
24
+ version_requirements: *2153585680
26
25
  - !ruby/object:Gem::Dependency
27
26
  name: guard
28
- requirement: &2153677820 !ruby/object:Gem::Requirement
27
+ requirement: &2153585220 !ruby/object:Gem::Requirement
29
28
  none: false
30
29
  requirements:
31
30
  - - ! '>='
@@ -33,10 +32,10 @@ dependencies:
33
32
  version: '0'
34
33
  type: :development
35
34
  prerelease: false
36
- version_requirements: *2153677820
35
+ version_requirements: *2153585220
37
36
  - !ruby/object:Gem::Dependency
38
37
  name: guard-rspec
39
- requirement: &2153677400 !ruby/object:Gem::Requirement
38
+ requirement: &2153584680 !ruby/object:Gem::Requirement
40
39
  none: false
41
40
  requirements:
42
41
  - - ! '>='
@@ -44,10 +43,10 @@ dependencies:
44
43
  version: '0'
45
44
  type: :development
46
45
  prerelease: false
47
- version_requirements: *2153677400
46
+ version_requirements: *2153584680
48
47
  - !ruby/object:Gem::Dependency
49
48
  name: guard-bundler
50
- requirement: &2153676980 !ruby/object:Gem::Requirement
49
+ requirement: &2153584140 !ruby/object:Gem::Requirement
51
50
  none: false
52
51
  requirements:
53
52
  - - ! '>='
@@ -55,10 +54,10 @@ dependencies:
55
54
  version: '0'
56
55
  type: :development
57
56
  prerelease: false
58
- version_requirements: *2153676980
57
+ version_requirements: *2153584140
59
58
  - !ruby/object:Gem::Dependency
60
59
  name: ruby-debug19
61
- requirement: &2153676560 !ruby/object:Gem::Requirement
60
+ requirement: &2153583720 !ruby/object:Gem::Requirement
62
61
  none: false
63
62
  requirements:
64
63
  - - ! '>='
@@ -66,10 +65,10 @@ dependencies:
66
65
  version: '0'
67
66
  type: :development
68
67
  prerelease: false
69
- version_requirements: *2153676560
68
+ version_requirements: *2153583720
70
69
  - !ruby/object:Gem::Dependency
71
70
  name: shoulda-matchers
72
- requirement: &2153676140 !ruby/object:Gem::Requirement
71
+ requirement: &2153582420 !ruby/object:Gem::Requirement
73
72
  none: false
74
73
  requirements:
75
74
  - - ! '>='
@@ -77,10 +76,10 @@ dependencies:
77
76
  version: '0'
78
77
  type: :development
79
78
  prerelease: false
80
- version_requirements: *2153676140
79
+ version_requirements: *2153582420
81
80
  - !ruby/object:Gem::Dependency
82
81
  name: timecop
83
- requirement: &2153675720 !ruby/object:Gem::Requirement
82
+ requirement: &2153562380 !ruby/object:Gem::Requirement
84
83
  none: false
85
84
  requirements:
86
85
  - - ! '>='
@@ -88,10 +87,10 @@ dependencies:
88
87
  version: '0'
89
88
  type: :development
90
89
  prerelease: false
91
- version_requirements: *2153675720
90
+ version_requirements: *2153562380
92
91
  - !ruby/object:Gem::Dependency
93
92
  name: activemodel
94
- requirement: &2153675300 !ruby/object:Gem::Requirement
93
+ requirement: &2153561820 !ruby/object:Gem::Requirement
95
94
  none: false
96
95
  requirements:
97
96
  - - ! '>='
@@ -99,10 +98,10 @@ dependencies:
99
98
  version: '0'
100
99
  type: :runtime
101
100
  prerelease: false
102
- version_requirements: *2153675300
101
+ version_requirements: *2153561820
103
102
  - !ruby/object:Gem::Dependency
104
103
  name: activesupport
105
- requirement: &2155970660 !ruby/object:Gem::Requirement
104
+ requirement: &2153561340 !ruby/object:Gem::Requirement
106
105
  none: false
107
106
  requirements:
108
107
  - - ! '>='
@@ -110,7 +109,7 @@ dependencies:
110
109
  version: '0'
111
110
  type: :runtime
112
111
  prerelease: false
113
- version_requirements: *2155970660
112
+ version_requirements: *2153561340
114
113
  description: Gem includes validations, card brand detection method, etc.
115
114
  email:
116
115
  - alovak@gmail.com
@@ -125,13 +124,14 @@ files:
125
124
  - Rakefile
126
125
  - bank_card.gemspec
127
126
  - lib/bank_card.rb
127
+ - lib/bank_card/brands.rb
128
128
  - lib/bank_card/locale/en.yml
129
129
  - lib/bank_card/validations/expiration_validator.rb
130
130
  - lib/bank_card/validations/luhn_validator.rb
131
131
  - lib/bank_card/version.rb
132
+ - spec/lib/brands_spec.rb
132
133
  - spec/lib/validations_spec.rb
133
134
  - spec/spec_helper.rb
134
- has_rdoc: true
135
135
  homepage: ''
136
136
  licenses: []
137
137
  post_install_message:
@@ -152,10 +152,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
152
  version: '0'
153
153
  requirements: []
154
154
  rubyforge_project: bank_card
155
- rubygems_version: 1.6.2
155
+ rubygems_version: 1.8.12
156
156
  signing_key:
157
157
  specification_version: 3
158
158
  summary: Bunch of methods to work with credit cards
159
159
  test_files:
160
+ - spec/lib/brands_spec.rb
160
161
  - spec/lib/validations_spec.rb
161
162
  - spec/spec_helper.rb