credy 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in credy.gemspec
4
+ gemspec
@@ -0,0 +1,38 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ credy (0.1.1)
5
+ thor
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ columnize (0.3.6)
11
+ debugger (1.3.0)
12
+ columnize (>= 0.3.1)
13
+ debugger-linecache (~> 1.1.1)
14
+ debugger-ruby_core_source (~> 1.1.7)
15
+ debugger-linecache (1.1.2)
16
+ debugger-ruby_core_source (>= 1.1.1)
17
+ debugger-ruby_core_source (1.1.7)
18
+ diff-lcs (1.1.3)
19
+ rake (10.0.3)
20
+ rspec (2.12.0)
21
+ rspec-core (~> 2.12.0)
22
+ rspec-expectations (~> 2.12.0)
23
+ rspec-mocks (~> 2.12.0)
24
+ rspec-core (2.12.2)
25
+ rspec-expectations (2.12.1)
26
+ diff-lcs (~> 1.1.3)
27
+ rspec-mocks (2.12.2)
28
+ thor (0.17.0)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ credy!
35
+ debugger
36
+ rake
37
+ rspec
38
+ rspec-mocks
@@ -0,0 +1,5 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new(:spec)
4
+
5
+ task :default => :spec
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'thor'
4
+ require 'credy'
5
+
6
+ module Credy
7
+
8
+ class CLI < Thor
9
+
10
+ desc "generate", "Generate a credit card number"
11
+ method_option :details, aliases: '-d', desc: "Show card details"
12
+ method_option :country, aliases: '-c', type: :string, default: 'all', desc: "Card's issuing country (au, fr, us, ca, ...)"
13
+ method_option :type, aliases: '-t', type: :string, default: 'all', desc: "Card's type (visa, mastercard, americanexpress)"
14
+ method_option :number, aliases: '-n', type: :numeric, default: 1, desc: "Number of cards"
15
+ def generate
16
+ details = options[:details]
17
+
18
+ args = {}
19
+ args[:country] = options[:country] unless options[:country] == 'all'
20
+ args[:type] = options[:type] unless options[:type] == 'all'
21
+
22
+ options[:number].times do
23
+ number = CreditCard.generate args
24
+
25
+ unless number
26
+ puts 'No rule found for those criteria.'
27
+ break
28
+ end
29
+
30
+ if details
31
+ d = [number[:type], number[:country]].compact
32
+ puts "#{number[:number]} (#{d.join(', ')})"
33
+ else
34
+ puts number[:number]
35
+ end
36
+ end
37
+ end
38
+ map 'g' => :generate
39
+
40
+ desc 'infos NUMBER', 'Show information about a credit card number'
41
+ def infos(number)
42
+ infos = CreditCard.infos number
43
+ if infos
44
+ puts "Type: #{infos[:type]}"
45
+ puts "Country: #{infos[:country]}" if infos[:country]
46
+ if CreditCard.validate(number)[:valid]
47
+ puts "Valid".green
48
+ else
49
+ puts "Not valid".red
50
+ end
51
+ else
52
+ puts 'No information available for this number.'
53
+ end
54
+ end
55
+ map 'i' => :infos
56
+
57
+ desc 'validate NUMBER', 'Check if a credit card number is valid or not'
58
+ def validate(number)
59
+ validity = CreditCard.validate number
60
+
61
+ if validity[:valid]
62
+ puts 'This number is valid.'.green
63
+ else
64
+ puts 'This number is not valid.'.red
65
+ end
66
+
67
+ details = []
68
+ validity[:details].each do |criteria, value|
69
+ value = value ? 'v'.green : 'x'.red
70
+ details << "#{criteria}: #{value}"
71
+ end
72
+ puts "(#{details.join(', ')})"
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+
79
+ Credy::CLI.start
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path '../lib/credy/version', __FILE__
4
+
5
+ Gem::Specification.new do |s|
6
+ s.authors = ['Tim Petricola']
7
+ s.email = ['hi@timpetricola.com']
8
+ s.summary = %q{A simple (but powerful) credit card number generator}
9
+ s.homepage = ''
10
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
11
+ s.files = `git ls-files`.split "\n"
12
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split "\n"
13
+ s.name = 'credy'
14
+ s.require_paths = ['lib']
15
+ s.version = Credy::VERSION
16
+
17
+ s.add_development_dependency 'rspec'
18
+ s.add_development_dependency 'rspec-mocks'
19
+ s.add_development_dependency 'rake'
20
+ s.add_development_dependency 'debugger'
21
+ s.add_runtime_dependency 'thor'
22
+ end
@@ -0,0 +1,8 @@
1
+ americanexpress:
2
+ length: 15
3
+ prefix: ['34', '37']
4
+ countries:
5
+ ca: '372301'
6
+ fr: '374622'
7
+ pl: '337941'
8
+ uk: ['374283', '374288']
@@ -0,0 +1,3 @@
1
+ bankcard:
2
+ length: 16
3
+ prefix: ['5610', '560221', '560222', '560223', '560224', '560225']
@@ -0,0 +1,3 @@
1
+ china-unionpay:
2
+ length: [16, 17, 18, 19]
3
+ prefix: ['5610', '560221', '560222', '560223', '560224', '560225']
@@ -0,0 +1,3 @@
1
+ diners-club-carte-blanche:
2
+ length: 14
3
+ prefix: ['300', '301', '302', '303', '304', '305']
@@ -0,0 +1,3 @@
1
+ diners-club-enroute:
2
+ length: 15
3
+ prefix: ['2014', '2015', '2016', '2017', '2018', '2019']
@@ -0,0 +1,3 @@
1
+ diners-club-international:
2
+ length: 14
3
+ prefix: '36'
@@ -0,0 +1,3 @@
1
+ diners-club-us-ca:
2
+ length: 16
3
+ prefix: ['54', '55']
@@ -0,0 +1,3 @@
1
+ instapayment:
2
+ length: 16
3
+ prefix: ['637', '638', '639']
@@ -0,0 +1,3 @@
1
+ jcb:
2
+ length: 16
3
+ prefix: ['3528', '3529', '3530', '3531', '3532', '3533', '3534', '3535', '3536', '3537', '3538', '3539', '3540', '3541', '3542', '3543', '3544', '3545', '3546', '3547', '3548', '3549', '3550', '3551', '3552', '3553', '3554', '3555', '3556', '3557', '3558', '3559', '3560', '3561', '3562', '3563', '3564', '3565', '3566', '3567', '3568', '3569', '3570', '3571', '3572', '3573', '3574', '3575', '3576', '3577', '3578', '3579', '3580', '3581', '3582', '3583', '3584', '3585', '3586', '3587', '3588', '3589']
@@ -0,0 +1,3 @@
1
+ laser:
2
+ length: [16, 17, 18, 19]
3
+ prefix: ['6304', '6706', '6771', '6709']
@@ -0,0 +1,3 @@
1
+ maestro:
2
+ length: [16, 17, 18, 19]
3
+ prefix: ['5018', '5020', '5038', '5893', '6304', '6759', '6761', '6762', '6763', '0604']
@@ -0,0 +1,7 @@
1
+ mastercard:
2
+ length: 16
3
+ prefix: ['51', '52', '53', '54', '55']
4
+ countries:
5
+ ca: ['500235', '500766', '501012']
6
+ es: ['510013', '510014', '510017']
7
+ us: ['504837', '510875']
@@ -0,0 +1,3 @@
1
+ solo:
2
+ length: [16, 18, 19]
3
+ prefix: ['6334', '6767']
@@ -0,0 +1,3 @@
1
+ switch:
2
+ length: [16, 18, 19]
3
+ prefix: ['4903', '4905', '4911', '4936', '564182', '633110', '6333', '6759']
@@ -0,0 +1,3 @@
1
+ visa-electron:
2
+ length: 16
3
+ prefix: ['4026', '417500', '4405', '4508', '4844', '4913', '4917']
@@ -0,0 +1,6 @@
1
+ visa:
2
+ length: [13, 16]
3
+ prefix: '4'
4
+ countries:
5
+ ch: '404159'
6
+ au: ['401795', '404137']
@@ -0,0 +1,84 @@
1
+ require 'credy/version'
2
+ require 'credy/string'
3
+ require 'credy/rules'
4
+ require 'credy/check'
5
+
6
+ module Credy
7
+
8
+ def self.root
9
+ File.expand_path '../..', __FILE__
10
+ end
11
+
12
+ class CreditCard
13
+
14
+ # Generate a credit card number
15
+ def self.generate(options = {})
16
+
17
+ # Include global rules (not based on country)
18
+ include_global_rules = options[:country].nil?
19
+ rule = Rules.filter(options, include_global_rules).sample
20
+
21
+ return nil unless rule
22
+
23
+ length = rule[:length].is_a?(Array) ? rule[:length].sample : rule[:length]
24
+ number = rule[:prefix]
25
+
26
+ # Generates n-1 digits
27
+ (length - number.length - 1).times do
28
+ number = number + rand(10).to_s
29
+ end
30
+
31
+ # Generates the last digit according to luhn algorithm
32
+ l = nil
33
+ digits = (0..9).to_a.map(&:to_s)
34
+ begin
35
+ l = digits.delete digits.sample
36
+ end while !Check.luhn number+l
37
+
38
+ number = number+l
39
+
40
+ {
41
+ number: number,
42
+ type: rule[:type],
43
+ country: rule[:country]
44
+ }
45
+ end
46
+
47
+ # Returns information about a number
48
+ def self.infos(number)
49
+ rules = Rules.flatten(true).select do |rule|
50
+ valid = true
51
+
52
+ # Check number of digits
53
+ lengths = rule[:length].is_a?(Array) ? rule[:length] : [rule[:length]]
54
+ valid = false unless lengths.include? number.length
55
+
56
+ # Check prefix
57
+ valid = false unless !(number =~ Regexp.new("^#{rule[:prefix]}")).nil?
58
+ valid
59
+ end
60
+
61
+ if rules
62
+ rules[0]
63
+ else
64
+ nil
65
+ end
66
+ end
67
+
68
+ # Validates a number
69
+ def self.validate(number)
70
+ criterii = {}
71
+ criterii[:luhn] = Check.luhn number
72
+ criterii[:type] = !!self.infos(number)
73
+
74
+ valid = criterii.all? { |_, v| v == true }
75
+
76
+ {
77
+ valid: valid,
78
+ details: criterii
79
+ }
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,19 @@
1
+ module Credy
2
+
3
+ class Check
4
+
5
+ def self.luhn(code)
6
+ s1 = s2 = 0
7
+ code.to_s.reverse.chars.each_slice(2) do |odd, even|
8
+ s1 += odd.to_i
9
+
10
+ double = even.to_i * 2
11
+ double -= 9 if double >= 10
12
+ s2 += double
13
+ end
14
+ (s1 + s2) % 10 == 0
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,78 @@
1
+ require 'yaml'
2
+
3
+ module Credy
4
+
5
+ class Rules
6
+
7
+ # Return all the rules from yml files
8
+ def self.all
9
+ @rules ||= begin
10
+ rules = {}
11
+ Dir.glob "#{Credy.root}/data/*.yml" do |filename|
12
+ r = YAML::load IO.read(filename)
13
+ rules.merge! r
14
+ end
15
+ rules
16
+ end
17
+ end
18
+
19
+ # Change hash format to process rules
20
+ def self.flatten(global_rules = false)
21
+ rules = []
22
+
23
+ all.each do |type, details|
24
+
25
+ if global_rules
26
+ # Add general rules
27
+ global_prefixes = details['prefix']
28
+ if global_prefixes
29
+ global_prefixes = [global_prefixes] unless global_prefixes.is_a? Array
30
+
31
+ global_prefixes.each do |prefix|
32
+ rules.push({
33
+ prefix: prefix.to_s,
34
+ length: details['length'],
35
+ type: type
36
+ })
37
+ end
38
+ end
39
+ end
40
+
41
+ # Process each country
42
+ if details['countries']
43
+ details['countries'].each do |country, prefixes|
44
+ prefixes = [prefixes] unless prefixes.is_a? Array
45
+
46
+ # Add a rule for each prefix
47
+ prefixes.each do |prefix|
48
+ rules.push({
49
+ prefix: prefix.to_s,
50
+ length: details['length'],
51
+ type: type,
52
+ country: country,
53
+ })
54
+ end
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ # Sort rules by prefix length
61
+ rules.sort! { |x, y| y[:prefix].length <=> x[:prefix].length }
62
+
63
+ rules
64
+ end
65
+
66
+ # Returns rules according to given filters
67
+ def self.filter(filters = {}, global_rules = false)
68
+ flatten(global_rules).select do |rule|
69
+ [:country, :type].each do |condition|
70
+ break false if filters[condition] && filters[condition] != rule[condition]
71
+ true
72
+ end
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ end
@@ -0,0 +1,13 @@
1
+ class String
2
+ def colorize(color_code)
3
+ "\e[#{color_code}m#{self}\e[0m"
4
+ end
5
+
6
+ def red
7
+ colorize 31
8
+ end
9
+
10
+ def green
11
+ colorize 32
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module Credy
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,92 @@
1
+ # Credy
2
+
3
+ A simple credit card generator.
4
+
5
+ ## Functionnalities
6
+ * Generates a valid number (per country/type)
7
+ * Get information for a number
8
+ * Check validity of a number
9
+
10
+ ## Installation
11
+
12
+ `gem install credy`
13
+
14
+ ## CLI usage
15
+
16
+ ### Generate
17
+ ```
18
+ credy generate --country au --type visa
19
+ ```
20
+
21
+ ### Get informations
22
+ ```
23
+ credy infos 5108756163954799
24
+ ```
25
+
26
+ ### Validate
27
+ ```
28
+ credy validate 5108756163954799
29
+ ```
30
+
31
+ ## Ruby usage
32
+
33
+ ### Generate
34
+ ``` ruby
35
+ options {
36
+ :country => 'au',
37
+ :type => 'mastercard'
38
+ }
39
+ Credy::CreditCard.generate options
40
+ ```
41
+
42
+ ### Get informations
43
+ ``` ruby
44
+ Credy::CreditCard.infos '5108756163954799'
45
+ ```
46
+
47
+ ### Validate
48
+ ``` ruby
49
+ Credy::CreditCard.validate '5108756163954799'
50
+ ```
51
+
52
+ ## Supported cards
53
+
54
+ At the moment, only a few types and countries are (partially) supported.
55
+
56
+ ### Types
57
+ * americanexpress (American Express)
58
+ * bankcard (Bankcard)
59
+ * china-unionpay (China UnionPay)
60
+ * diners-club-carte-blanche (Diners Club Carte Blanche)
61
+ * diners-club-enroute (Diners Club enRoute)
62
+ * diners-club-international (Diners Club International)
63
+ * diners-club-us-ca (Diners Club United States & Canada)
64
+ * instapayment (InstaPayment)
65
+ * jcb (JCB)
66
+ * laser (Laser)
67
+ * maestro (Maestro)
68
+ * mastercard (Mastercard)
69
+ * solo (Solo)
70
+ * switch (Switch)
71
+ * visa (Visa)
72
+ * visa-electron (Visa Electron)
73
+
74
+ ### Countries
75
+ * au (Australia)
76
+ * ca (Canada)
77
+ * fr (France)
78
+ * pl (Poland)
79
+ * es (Spain)
80
+ * ch (Switzerland)
81
+ * uk (United Kingdom)
82
+ * us (United States)
83
+
84
+ ## Todo
85
+ * Add more data (see the Data source section)
86
+ * Remove luhn validation for *China UnionPay* and *Diners Club enRoute*
87
+
88
+ ## Data source
89
+ All data is coming from the [Bank card number](http://en.wikipedia.org/wiki/Bank_card_number) page and the [List of Issuer Identification Numbers](http://en.wikipedia.org/wiki/List_of_Issuer_Identification_Numbers) on [Wikipedia](http://wikipedia.org). I do not assume the responsibility for wrong data.
90
+
91
+ ## License
92
+ Credy is released under the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,131 @@
1
+ require_relative '../spec_helper'
2
+
3
+ silent { load 'bin/credy' }
4
+
5
+ describe Credy::CLI do
6
+
7
+ before :all do
8
+ @stdout = $stdout
9
+ $stdout = StringIO.new
10
+ end
11
+
12
+ describe 'generate' do
13
+
14
+ it 'works without options' do
15
+ Credy::CreditCard.should_receive(:generate).with({})
16
+ r = Credy::CLI.start ['generate']
17
+ end
18
+
19
+ it '--country' do
20
+ Credy::CreditCard.should_receive(:generate).with(country: 'au').twice
21
+ Credy::CLI.start ['generate', '--country', 'au']
22
+ Credy::CLI.start ['generate', '-c', 'au']
23
+ end
24
+
25
+ it '--type' do
26
+ Credy::CreditCard.should_receive(:generate).with(type: 'visa').twice
27
+ Credy::CLI.start ['generate', '--type', 'visa']
28
+ Credy::CLI.start ['generate', '-t', 'visa']
29
+ end
30
+
31
+ it '--number' do
32
+ Credy::CreditCard.should_receive(:generate).exactly(20).and_return({number: '50076645747856835'})
33
+ Credy::CLI.start ['generate', '--number', 10]
34
+ Credy::CLI.start ['generate', '-n', 10]
35
+ end
36
+
37
+ describe 'result' do
38
+
39
+ before :all do
40
+ $stdout = @stdout
41
+ end
42
+
43
+ it 'stops if no number is found' do
44
+ Credy::CreditCard.should_receive(:generate).once.and_return(nil)
45
+ STDOUT.should_receive(:puts).with('No rule found for those criteria.').once
46
+ Credy::CLI.start ['generate', '-n', 10]
47
+ end
48
+
49
+ describe '--details' do
50
+ before do
51
+ number = {number: '50076645747856835', type: 'visa', country: 'au'}
52
+ Credy::CreditCard.should_receive(:generate).any_number_of_times.and_return number
53
+ end
54
+
55
+ it 'only returns the number' do
56
+ STDOUT.should_receive(:puts).with('50076645747856835').once
57
+ Credy::CLI.start ['generate']
58
+ end
59
+
60
+ it 'accepts to return more details' do
61
+ STDOUT.should_receive(:puts).with('50076645747856835 (visa, au)').twice
62
+ Credy::CLI.start ['generate', '--details']
63
+ Credy::CLI.start ['generate', '-d']
64
+ end
65
+ end
66
+
67
+ end
68
+
69
+ end
70
+
71
+ describe 'infos' do
72
+
73
+ it 'calls the infos function' do
74
+ Credy::CreditCard.should_receive(:infos).with '5108756163954799'
75
+ Credy::CLI.start ['infos', '5108756163954799']
76
+ end
77
+
78
+ describe 'result' do
79
+ before :all do
80
+ $stdout = @stdout
81
+ end
82
+
83
+ it 'shows error if nothing found' do
84
+ Credy::CreditCard.should_receive(:infos).and_return nil
85
+ STDOUT.should_receive(:puts).with 'No information available for this number.'
86
+ Credy::CLI.start ['infos', '5108756163954799']
87
+ end
88
+
89
+ it 'shows the card informations' do
90
+ number = {number: '50076645747856835', type: 'visa', country: 'au'}
91
+ Credy::CreditCard.should_receive(:infos).at_least(1).times.and_return number
92
+ STDOUT.should_receive(:puts).with 'Type: visa'
93
+ STDOUT.should_receive(:puts).with 'Country: au'
94
+ STDOUT.should_receive(:puts).with 'Valid'
95
+ Credy::CLI.start ['infos', '50076645747856835']
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+ describe 'validate' do
102
+ it 'calls the validate function' do
103
+ Credy::CreditCard.should_receive(:validate).with('5108756163954799').and_return({valid: false, details: {}})
104
+ Credy::CLI.start ['validate', '5108756163954799']
105
+ end
106
+
107
+ describe 'result' do
108
+ before :all do
109
+ $stdout = @stdout
110
+ end
111
+
112
+ it 'shows card validity and details' do
113
+ validity = { valid: true, details: { luhn: true, type: true} }
114
+ Credy::CreditCard.should_receive(:validate).and_return validity
115
+ STDOUT.should_receive(:puts).with 'This number is valid.'
116
+ STDOUT.should_receive(:puts).with '(luhn: v, type: v)'
117
+ Credy::CLI.start ['validate', '50076645747856835']
118
+ end
119
+
120
+ it 'shows card validity and details for invalid number' do
121
+ validity = { valid: false, details: { luhn: false, type: false} }
122
+ Credy::CreditCard.should_receive(:validate).and_return validity
123
+ STDOUT.should_receive(:puts).with 'This number is not valid.'
124
+ STDOUT.should_receive(:puts).with '(luhn: x, type: x)'
125
+ Credy::CLI.start ['validate', '5108756163954799']
126
+ end
127
+
128
+ end
129
+ end
130
+
131
+ end
@@ -0,0 +1,19 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Credy::Check do
4
+
5
+ describe '.luhn' do
6
+
7
+ it 'returns true for correct data' do
8
+ Credy::Check.luhn(49927398716).should be_true
9
+ Credy::Check.luhn(1234567812345670).should be_true
10
+ end
11
+
12
+ it 'returns false for non correct data' do
13
+ Credy::Check.luhn(49927398717).should be_false
14
+ Credy::Check.luhn(1234567812345678).should be_false
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,167 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Credy::Rules do
4
+
5
+ subject { Credy::Rules }
6
+
7
+ describe '.all' do
8
+
9
+ it { should respond_to :all }
10
+ its(:all) { should be_a Hash }
11
+
12
+ it 'should be tested more'
13
+
14
+ end
15
+
16
+ describe '.flatten' do
17
+
18
+ it { should respond_to :flatten }
19
+ its(:flatten) { should be_a Array }
20
+
21
+ it 'flattens a rules hash' do
22
+ subject.stub(:all).and_return({
23
+ 'visa' => {
24
+ 'length' => [13, 16],
25
+ 'countries' => {
26
+ 'ch' => '404159',
27
+ 'au' => '401795'
28
+ }
29
+ },
30
+ 'mastercard' => {
31
+ 'length' => 16,
32
+ 'countries' => {
33
+ 'us' => '504837'
34
+ }
35
+ },
36
+ })
37
+
38
+ subject.flatten.should == [
39
+ {:prefix=>"404159", :length=>[13, 16], :type=>"visa", :country=>"ch"},
40
+ {:prefix=>"401795", :length=>[13, 16], :type=>"visa", :country=>"au"},
41
+ {:prefix=>"504837", :length=>16, :type=>"mastercard", :country=>"us"}
42
+ ]
43
+ end
44
+
45
+ it 'works with string prefixes' do
46
+ subject.stub(:all).and_return({
47
+ 'visa' => {
48
+ 'length' => [13, 16],
49
+ 'countries' => {
50
+ 'au' => '401795'
51
+ }
52
+ }
53
+ })
54
+
55
+ subject.flatten.should == [
56
+ {:prefix=>"401795", :length=>[13, 16], :type=>"visa", :country=>"au"}
57
+ ]
58
+ end
59
+
60
+ it 'works with integer prefixes' do
61
+ subject.stub(:all).and_return({
62
+ 'visa' => {
63
+ 'length' => [13, 16],
64
+ 'countries' => {
65
+ 'au' => 401795
66
+ }
67
+ }
68
+ })
69
+
70
+ subject.flatten.should == [
71
+ {:prefix=>"401795", :length=>[13, 16], :type=>"visa", :country=>"au"}
72
+ ]
73
+ end
74
+
75
+ it 'works with an array of prefixes' do
76
+ subject.stub(:all).and_return({
77
+ 'visa' => {
78
+ 'length' => [13, 16],
79
+ 'countries' => {
80
+ 'au' => ['401795', '404137']
81
+ }
82
+ }
83
+ })
84
+
85
+ subject.flatten.should == [
86
+ {:prefix=>"401795", :length=>[13, 16], :type=>"visa", :country=>"au"},
87
+ {:prefix=>"404137", :length=>[13, 16], :type=>"visa", :country=>"au"}
88
+ ]
89
+ end
90
+
91
+ it 'includes global rules' do
92
+ subject.stub(:all).and_return({
93
+ 'visa' => {
94
+ 'length' => [13, 16],
95
+ 'prefix' => '4',
96
+ 'countries' => {
97
+ 'ch' => '404159',
98
+ 'au' => '401'
99
+ }
100
+ },
101
+ 'mastercard' => {
102
+ 'length' => 16,
103
+ 'prefix' => '51'
104
+ },
105
+ })
106
+
107
+ subject.flatten(true).should == [
108
+ {:prefix=>"404159", :length=>[13, 16], :type=>"visa", :country=>"ch"},
109
+ {:prefix=>"401", :length=>[13, 16], :type=>"visa", :country=>"au"},
110
+ {:prefix=>"51", :length=>16, :type=>"mastercard"},
111
+ {:prefix=>"4", :length=>[13, 16], :type=>"visa"}
112
+ ]
113
+ end
114
+
115
+ end
116
+
117
+ describe '.filter' do
118
+
119
+ before do
120
+ subject.stub(:all).and_return({
121
+ 'visa' => {
122
+ 'length' => [13, 16],
123
+ 'countries' => {
124
+ 'ch' => '404159',
125
+ 'au' => '401795'
126
+ }
127
+ },
128
+ 'mastercard' => {
129
+ 'length' => 16,
130
+ 'countries' => {
131
+ 'au' => '401795'
132
+ }
133
+ },
134
+ })
135
+ end
136
+
137
+ it { should respond_to :filter }
138
+
139
+ it 'returns everything if no filter is provided' do
140
+ subject.filter.should be_a Array
141
+ subject.filter.should == subject.flatten
142
+ end
143
+
144
+ it 'filters by type' do
145
+ subject.filter(type: 'visa').length.should == 2
146
+ subject.filter(type: 'mastercard').length.should == 1
147
+ end
148
+
149
+ it 'accepts the :country option' do
150
+ subject.filter(country: 'ch').length.should == 1
151
+ subject.filter(country: 'au').length.should == 2
152
+ end
153
+
154
+ it 'accepts several options at the same time' do
155
+ rules = subject.filter type: 'visa', country: 'au'
156
+ rules.length.should == 1
157
+ end
158
+
159
+ it 'returns an empty array if nothing is found' do
160
+ rules = subject.filter type: 'foo', country: 'bar'
161
+ rules.should be_a Array
162
+ rules.should be_empty
163
+ end
164
+
165
+ end
166
+
167
+ end
@@ -0,0 +1,7 @@
1
+ describe Credy::VERSION do
2
+
3
+ it 'is defined' do
4
+ Credy::VERSION.should_not be_nil
5
+ end
6
+
7
+ end
@@ -0,0 +1,104 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Credy::CreditCard do
4
+
5
+ subject { Credy::CreditCard }
6
+
7
+ let(:rules) do
8
+ {
9
+ 'visa' => {
10
+ 'length' => [13, 16],
11
+ 'countries' => {
12
+ 'ch' => '404159',
13
+ 'au' => ['401795', '404137']
14
+ }
15
+ },
16
+ 'mastercard' => {
17
+ 'length' => 16,
18
+ 'countries' => {
19
+ 'au' => '401795',
20
+ 'us' => ['504837', '510875']
21
+ }
22
+ },
23
+ }
24
+ end
25
+
26
+ before(:each) do
27
+ Credy::Rules.stub(:all).and_return rules
28
+ end
29
+
30
+ describe '.generate' do
31
+
32
+ it 'returns a number, type and country' do
33
+ subject.generate[:number].should_not be_nil
34
+ subject.generate[:type].should_not be_nil
35
+ subject.generate[:country].should_not be_nil
36
+ end
37
+
38
+ it 'accepts the :type option' do
39
+ number = subject.generate type: 'visa'
40
+ number[:type].should == 'visa'
41
+ end
42
+
43
+ it 'accepts the :country option' do
44
+ number = subject.generate country: 'ch'
45
+ number[:country].should == 'ch'
46
+ end
47
+
48
+ it 'accepts several options at the same time' do
49
+ number = subject.generate type: 'mastercard', country: 'au'
50
+ number[:type].should == 'mastercard'
51
+ number[:country].should == 'au'
52
+ end
53
+
54
+ it 'generates the right number of digits' do
55
+ number = subject.generate type: 'mastercard'
56
+ number[:number].length.should == 16
57
+ end
58
+
59
+ it 'generate a number with the right prefix' do
60
+ number = subject.generate type: 'mastercard', country: 'au'
61
+ number[:number].should =~ /^401795/
62
+ end
63
+
64
+ it 'returns nil if nothing is found' do
65
+ number = subject.generate type: 'foo', country: 'bar'
66
+ number.should be_nil
67
+ end
68
+
69
+ end
70
+
71
+ describe '.infos' do
72
+
73
+ it 'returns the correct information according to the card number' do
74
+ infos = subject.infos '5108756163954792'
75
+ infos[:type].should == 'mastercard'
76
+ infos[:country].should == 'us'
77
+ end
78
+
79
+ end
80
+
81
+ describe '.validate' do
82
+
83
+ it 'returns a hash' do
84
+ r = subject.validate '5108756163954792'
85
+ r[:valid].should be_true
86
+ r[:details][:luhn].should be_true
87
+ r[:details][:type].should be_true
88
+ end
89
+
90
+ it 'checks against luhn algorithm' do
91
+ r = subject.validate '5108756163954791'
92
+ r[:valid].should be_false
93
+ r[:details][:luhn].should be_false
94
+ end
95
+
96
+ it 'checks against card type' do
97
+ r = subject.validate '99999999999999999999992'
98
+ r[:valid].should be_false
99
+ r[:details][:type].should be_false
100
+ end
101
+
102
+ end
103
+
104
+ end
@@ -0,0 +1,24 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'credy'
5
+
6
+ def silent
7
+ _stdout = $stdout
8
+ $stdout = StringIO.new
9
+ begin
10
+ yield
11
+ ensure
12
+ $stdout = _stdout
13
+ end
14
+ end
15
+
16
+ # Don't colorize strings in test environment
17
+ class String
18
+ def colorize(color_code)
19
+ self
20
+ end
21
+ end
22
+
23
+ RSpec.configure do |config|
24
+ end
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - jruby-18mode
6
+ - jruby-19mode
7
+ - ruby-head
8
+ - jruby-head
9
+ - 1.8.7
10
+ - ree
metadata ADDED
@@ -0,0 +1,168 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: credy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tim Petricola
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec-mocks
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: debugger
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: thor
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description:
95
+ email:
96
+ - hi@timpetricola.com
97
+ executables:
98
+ - credy
99
+ extensions: []
100
+ extra_rdoc_files: []
101
+ files:
102
+ - .gitignore
103
+ - Gemfile
104
+ - Gemfile.lock
105
+ - Rakefile
106
+ - bin/credy
107
+ - credy.gemspec
108
+ - data/americanexpress.yml
109
+ - data/bankcard.yml
110
+ - data/china-unionpay.yml
111
+ - data/diners-club-carte-blanche.yml
112
+ - data/diners-club-enroute.yml
113
+ - data/diners-club-international.yml
114
+ - data/diners-club-us-ca.yml
115
+ - data/instapayment.yml
116
+ - data/jcb.yml
117
+ - data/laser.yml
118
+ - data/maestro.yml
119
+ - data/mastercard.yml
120
+ - data/solo.yml
121
+ - data/switch.yml
122
+ - data/visa-electron.yml
123
+ - data/visa.yml
124
+ - lib/credy.rb
125
+ - lib/credy/check.rb
126
+ - lib/credy/rules.rb
127
+ - lib/credy/string.rb
128
+ - lib/credy/version.rb
129
+ - readme.md
130
+ - spec/bin/credy_spec.rb
131
+ - spec/lib/credy/check_spec.rb
132
+ - spec/lib/credy/rules_spec.rb
133
+ - spec/lib/credy/version_spec.rb
134
+ - spec/lib/credy_spec.rb
135
+ - spec/spec_helper.rb
136
+ - travis.yml
137
+ homepage: ''
138
+ licenses: []
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ! '>='
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ! '>='
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 1.8.24
158
+ signing_key:
159
+ specification_version: 3
160
+ summary: A simple (but powerful) credit card number generator
161
+ test_files:
162
+ - spec/bin/credy_spec.rb
163
+ - spec/lib/credy/check_spec.rb
164
+ - spec/lib/credy/rules_spec.rb
165
+ - spec/lib/credy/version_spec.rb
166
+ - spec/lib/credy_spec.rb
167
+ - spec/spec_helper.rb
168
+ has_rdoc: