credit_card_processor 0.0.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.
- checksums.yaml +15 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +32 -0
- data/Rakefile +1 -0
- data/credit_card_processor.gemspec +26 -0
- data/lib/credit_card_processor/version.rb +3 -0
- data/lib/credit_card_processor.rb +101 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZjdmNTZmYmYzODhhMDZlYmIwZTg3NGIyMTZiN2NkZDI3NmQzM2VjYg==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTM4OWYyYTlkMmMxNzg5NmY3MTcxYTUwNmUwNTc4MDc2ZDUzNzNiYw==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OTE5MjQ2OWIzN2VhMzNkNTBlNDFiMjNjZjgwYTExOTMxNjY0MWNjZWRhNjYw
|
10
|
+
NTJiY2ViZTkyODhlYzBiNjZlZGMxM2IzOThhMjhhNGJkYmNhNzBiYTVmODcx
|
11
|
+
ZmZhOWUwNGIxMDY0OGJkM2YwYjg0OTY4ZmU0MjQ5N2Y0ZDA3MTU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZTg0ZTA5OTdkM2FkMGMzNjNjMTQyODIwYjkwMWI2ZThjY2Y5MmEwNDkyMzQy
|
14
|
+
MTBlYjYwNjM5ZWQwYmZhNjBhYTAyOTNiOGNlOTAxMzYyNDQyYTgyOGNlYzY5
|
15
|
+
MDIxM2RhMTZlMDg1OWFjMDI5MjkxM2IyMWVlZDU0YTY0YzgyY2Y=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 TODO: Write your name
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# CreditCardProcessor
|
2
|
+
|
3
|
+
Program to add new credit card accounts, process charges and credits against them, and display summary information.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
In the credit_card_processor repository run:
|
7
|
+
|
8
|
+
```
|
9
|
+
bundle install
|
10
|
+
```
|
11
|
+
|
12
|
+
to get dependencies.
|
13
|
+
|
14
|
+
Then run the executable from bin:
|
15
|
+
|
16
|
+
```
|
17
|
+
./bin/credit_card_processor -f path_to_myfile.txt
|
18
|
+
```
|
19
|
+
|
20
|
+
## Design Decisions
|
21
|
+
|
22
|
+
This was written in ruby 1.9.3. I decided to use sqlite with sequel in order to load the accounts into a database structure for processing. This can be easily transitioned from a sqlite db to a real db if needed. Although it's outside the scope of this assignment it would probably be useful to store all charges and credits in a ledger table, along with the transaction date and time. This is money so it's good to have a paper trail.
|
23
|
+
|
24
|
+
I'm using trollop as a command line parser here, which may be overkill considering there is only one option. But it's easy to implement and includes a nice help screen.
|
25
|
+
|
26
|
+
The code first creates a table called accounts. It then parses an input file and takes any "Add" lines and inserts them into the account table along with the credit limit (called credit_available) and a balance of $0. It strips the dollar signs for integer processing and later math. If the credit card number does not pass luhn validation, it is inserted as "error". I used an existing gem for luhn validation rather than write one myself.
|
27
|
+
|
28
|
+
When it encounters a "Charge" line, it first checks that it does not exceed the credit available for that name. If not, it increases the balance and decreases the credit available. A charge is not subject to credit available check, but when processed increases the credit available and decreases the balance. Both charge and credit lines only processed if the name listed exists in the accounts table.
|
29
|
+
|
30
|
+
After processing all lines, it outputs either "name: $balance" or "name: error" depending on whether or not the credit card number can be validated. This does not prevent charge/credit processing; it just displays the error at the end.
|
31
|
+
|
32
|
+
Finally I didn't push this to rubygems because I wasn't sure Braintree wanted this code out in the wild. Therefore it cannot be installed as a gem, although bundle install can be used to get dependencies.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'credit_card_processor/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'credit_card_processor'
|
8
|
+
spec.version = CreditCardProcessor::VERSION
|
9
|
+
spec.authors = ['meggiebonner']
|
10
|
+
spec.email = ['meggie.bonner@gmail.com']
|
11
|
+
spec.summary = 'Gem for credit card processing.'
|
12
|
+
|
13
|
+
spec.files = `git ls-files -z`.split("\x0")
|
14
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
15
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
16
|
+
spec.require_paths = ['lib']
|
17
|
+
|
18
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
19
|
+
spec.add_development_dependency 'rake'
|
20
|
+
spec.add_development_dependency 'rspec'
|
21
|
+
|
22
|
+
spec.add_runtime_dependency 'sequel', '~> 4.1'
|
23
|
+
spec.add_runtime_dependency 'trollop'
|
24
|
+
spec.add_runtime_dependency 'sqlite3'
|
25
|
+
spec.add_runtime_dependency 'luhn-ruby'
|
26
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'credit_card_processor/version'
|
2
|
+
require 'trollop'
|
3
|
+
require 'sequel'
|
4
|
+
require 'luhn'
|
5
|
+
|
6
|
+
DB = Sequel.sqlite
|
7
|
+
|
8
|
+
module CreditCardProcessor
|
9
|
+
class CCProcessor
|
10
|
+
def initialize(argv = ARGV)
|
11
|
+
@parser = Trollop::Parser.new
|
12
|
+
Trollop.with_standard_exception_handling(@parser) do
|
13
|
+
define_options(argv)
|
14
|
+
@options = @parser.parse(argv.clone)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def define_options(_argv)
|
19
|
+
@parser.banner <<-EOS.gsub(/^ {8}/, '')
|
20
|
+
Processes credit card info from an input file
|
21
|
+
Options:
|
22
|
+
EOS
|
23
|
+
@parser.opt :file, 'Full path to input file', type: :string, short: 'f', required: true
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_table
|
27
|
+
DB.create_table? :accounts do
|
28
|
+
primary_key :id
|
29
|
+
String :name
|
30
|
+
String :cc_number
|
31
|
+
Integer :credit_available
|
32
|
+
Integer :balance
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def cc_valid?(cc_number)
|
37
|
+
Luhn.valid? cc_number
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_account(name, cc_number, credit_available)
|
41
|
+
if cc_valid?(cc_number)
|
42
|
+
DB[:accounts].insert(name: name, cc_number: cc_number, credit_available: credit_available, balance: 0)
|
43
|
+
else
|
44
|
+
DB[:accounts].insert(name: name, cc_number: 'error', credit_available: credit_available, balance: 0)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def name_exists?(name)
|
49
|
+
DB[:accounts].where(name: name)
|
50
|
+
end
|
51
|
+
|
52
|
+
def charge(name, charge)
|
53
|
+
credit_available = DB[:accounts].select(:credit_available).where(name: name).first
|
54
|
+
if charge.to_i <= credit_available[:credit_available].to_i && name_exists?(name)
|
55
|
+
DB[:accounts].where(name: name).update(balance: Sequel.expr(:balance) + charge.to_i)
|
56
|
+
DB[:accounts].where(name: name).update(credit_available: Sequel.expr(:credit_available) - charge.to_i)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def credit(name, credit)
|
61
|
+
if name_exists?(name)
|
62
|
+
DB[:accounts].where(name: name).update(balance: Sequel.expr(:balance) - credit.to_i)
|
63
|
+
DB[:accounts].where(name: name).update(credit_available: Sequel.expr(:credit_available) + credit.to_i)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse(input_file)
|
68
|
+
File.open(input_file).each do |line|
|
69
|
+
line = line.split(' ')
|
70
|
+
function = line[0]
|
71
|
+
if function == 'Add'
|
72
|
+
add_account(line[1], line[2], line[3].split('$').last.to_i)
|
73
|
+
elsif function == 'Charge'
|
74
|
+
charge(line[1], line[2].split('$').last.to_i)
|
75
|
+
elsif function == 'Credit'
|
76
|
+
credit(line[1], line[2].split('$').last.to_i)
|
77
|
+
else
|
78
|
+
puts 'Function is not valid'
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def output
|
84
|
+
output_hash = DB[:accounts].select(:name, :cc_number, :balance).all
|
85
|
+
output_hash.sort_by { |output_hash| output_hash[:name] }.each do |output_hash|
|
86
|
+
if output_hash[:cc_number] == 'error'
|
87
|
+
puts "#{output_hash[:name]}: error"
|
88
|
+
else
|
89
|
+
puts "#{output_hash[:name]}: $#{output_hash[:balance]}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def main
|
95
|
+
input_file = @options[:file]
|
96
|
+
create_table
|
97
|
+
parse(input_file)
|
98
|
+
output
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: credit_card_processor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- meggiebonner
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.5'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.5'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sequel
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '4.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '4.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: trollop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sqlite3
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: luhn-ruby
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description:
|
112
|
+
email:
|
113
|
+
- meggie.bonner@gmail.com
|
114
|
+
executables: []
|
115
|
+
extensions: []
|
116
|
+
extra_rdoc_files: []
|
117
|
+
files:
|
118
|
+
- .gitignore
|
119
|
+
- Gemfile
|
120
|
+
- LICENSE.txt
|
121
|
+
- README.md
|
122
|
+
- Rakefile
|
123
|
+
- credit_card_processor.gemspec
|
124
|
+
- lib/credit_card_processor.rb
|
125
|
+
- lib/credit_card_processor/version.rb
|
126
|
+
homepage:
|
127
|
+
licenses: []
|
128
|
+
metadata: {}
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ! '>='
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: '0'
|
138
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - ! '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
requirements: []
|
144
|
+
rubyforge_project:
|
145
|
+
rubygems_version: 2.2.2
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: Gem for credit card processing.
|
149
|
+
test_files: []
|
150
|
+
has_rdoc:
|