credy 0.1.1

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.
@@ -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: