bank_card 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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