kiq 0.1.6
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 +7 -0
- data/Gemfile +4 -0
- data/README.md +73 -0
- data/Rakefile +7 -0
- data/bin/kiq +11 -0
- data/kiq.gemspec +24 -0
- data/lib/kiq.rb +8 -0
- data/lib/kiq/backer.rb +157 -0
- data/lib/kiq/helpers/constants.rb +20 -0
- data/lib/kiq/helpers/display.rb +32 -0
- data/lib/kiq/helpers/version.rb +4 -0
- data/lib/kiq/project.rb +100 -0
- data/lib/kiq/run.rb +176 -0
- data/spec/kiq.rb +4 -0
- data/spec/kiq/backer_spec.rb +116 -0
- data/spec/kiq/project_spec.rb +82 -0
- data/spec/kiq/run_spec.rb +17 -0
- data/spec/spec_helper.rb +19 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8738b2a749f8c6959b25142fd004b6b4afd9a22d
|
4
|
+
data.tar.gz: b294d68b568e765289461654a299ea702fc6d8ee
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c1a743f005a75273f63e9547b7425a5db88984ec7ce0d75fe422de321ccda91cbe27ce20e817aebd0cb12ef67468dcc04e62c44ad44fb84f32c179271a758c91
|
7
|
+
data.tar.gz: 71c3e03e2c1ecf0d8386d828044b481d25aa7c65436f92b73547169b3cc1ecc7394d490036e7d97782e8cc584b7cf8a72ab73184d1e441338996fa88bab0aa20
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
Kiq [](https://travis-ci.org/loganmeetsworld/kiq) [](https://badge.fury.io/rb/kiq) [](http://inch-ci.org/github/loganmeetsworld/kiq) [](https://codeclimate.com/github/loganmeetsworld/kiq)
|
3
|
+
=========
|
4
|
+
|
5
|
+
## Summary
|
6
|
+
kiq is a crowd funding application that can be run from your command line. This project was a learning experience. Key learning points for me were:
|
7
|
+
* Building a Ruby gem.
|
8
|
+
* Struggling with deciding if I should use other gems and build in dependencies.
|
9
|
+
* Testing Ruby and making code more modular.
|
10
|
+
* Getting over my Rails addiction to lovely built in libraries and methods.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
This is a Ruby Gem! It can be found on the rubygems site [here](https://rubygems.org/gems/kiq) and easily implemented on any operating system that has Ruby installed with the command `gem install kiq`.
|
14
|
+
|
15
|
+
I tested this gem out on OSX El Capitan, Yosemite, Microsoft 10, and Linux. Just make sure you have [https://www.ruby-lang.org/en/documentation/installation/](Ruby installed on your OS). RubyGems comes with a Ruby installation.
|
16
|
+
|
17
|
+
This gem uses serialization to store the projects in a `.kiq` yml file. If you don't want this file in whatever folder you are currently in, I would suggest creating a new folder to use the gem:
|
18
|
+
`cd projects`
|
19
|
+
`mkdir kiq`
|
20
|
+
`gem install kiq`
|
21
|
+
And then you're off! If you ever want to remove the `.kiq` yaml file, delete it from the folder with:
|
22
|
+
`rm .kiq`
|
23
|
+
|
24
|
+
If you're interesting in developing with the code, use git to clone this repo:
|
25
|
+
`git clone https://github.com/loganmeetsworld/kiq.git`
|
26
|
+
|
27
|
+
## Sample Input and Output
|
28
|
+
```
|
29
|
+
> kiq project Awesome_Sauce 500
|
30
|
+
Added Project: Awesome_Sauce!
|
31
|
+
> kiq back John Awesome_Sauce 4111111111111111 50
|
32
|
+
John backed Awesome_Sauce for $50.
|
33
|
+
> kiq back Sally Awesome_Sauce 1234567890123456 10
|
34
|
+
ERROR: That card fails Luhn-10!
|
35
|
+
> kiq back Jane Awesome_Sauce 4111111111111111 50
|
36
|
+
ERROR: That card has already been added by another user!
|
37
|
+
> kiq back Jane Awesome_Sauce 5555555555554444 50
|
38
|
+
Jane backed Awesome_Sauce for $50.
|
39
|
+
> kiq list Awesome_Sauce
|
40
|
+
Project Name: Awesome_Sauce
|
41
|
+
Amount Remaining: $400.0
|
42
|
+
BACKERS:
|
43
|
+
Backer John, Amount: $50
|
44
|
+
Backer Jane, Amount: $50
|
45
|
+
> kiq back Mary Awesome_Sauce 5474942730093167 400
|
46
|
+
Reached goal!
|
47
|
+
Mary backed Awesome_Sauce for $400.
|
48
|
+
> kiq list Awesome_Sauce
|
49
|
+
Project Name: Awesome_Sauce
|
50
|
+
Amount Remaining: $0.0
|
51
|
+
Reached goal!
|
52
|
+
BACKERS:
|
53
|
+
Backer John, Amount: $50
|
54
|
+
Backer Jane, Amount: $50
|
55
|
+
Backer Mary, Amount: $400
|
56
|
+
> kiq backer John
|
57
|
+
Backed Awesome_Sauce for $50 dollars.
|
58
|
+
```
|
59
|
+
|
60
|
+
## Technology/Dependencies
|
61
|
+
### Let's talk about assumptions
|
62
|
+
* I want to recognize that I made a couple of assumptions in this process. The first assumption was that a user downloading the gem was using a Mac.
|
63
|
+
|
64
|
+
### Why ruby, why a gem (and the downsides to those choices)
|
65
|
+
### To depend, or not to depend? That is the question.
|
66
|
+
### Serialization For Storage
|
67
|
+
### Hash Useage
|
68
|
+
|
69
|
+
## To Do (if only there was more time)
|
70
|
+
A lot of things.
|
71
|
+
* Make it look prettier, research UI gems like `highline` and `colorize` and look for ways to implement them.
|
72
|
+
* Refactor the specs. There's a lot of repetition there.
|
73
|
+
* Add specs for handling user input.
|
data/Rakefile
ADDED
data/bin/kiq
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Loads the library path and all files in it
|
3
|
+
# requires the run file to start program
|
4
|
+
# calls the perform method in run
|
5
|
+
|
6
|
+
lib = File.expand_path('../lib', __FILE__)
|
7
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
8
|
+
|
9
|
+
require 'kiq/run'
|
10
|
+
|
11
|
+
Kiq::CLI::Run.new.perform
|
data/kiq.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'kiq/helpers/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |gem|
|
6
|
+
gem.name = 'kiq'
|
7
|
+
gem.version = Kiq::VERSION
|
8
|
+
gem.authors = ['Logan McDonald']
|
9
|
+
gem.email = ['logan@logancodes.it']
|
10
|
+
gem.description = %q{Use this command line tool to back Kickstarter campaigns.}
|
11
|
+
gem.summary = %q{Mini Kickstarter Command Line Interface}
|
12
|
+
gem.homepage = 'https://github.com/loganmeetsworld/kiq'
|
13
|
+
gem.license = "MIT"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.files -= gem.files.grep(%r{^\.})
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
|
+
gem.require_paths = ['lib']
|
20
|
+
|
21
|
+
gem.add_development_dependency 'rspec', '~> 2.14'
|
22
|
+
gem.add_development_dependency 'rake', '~> 10.1'
|
23
|
+
gem.add_development_dependency 'simplecov', '~> 0.12'
|
24
|
+
end
|
data/lib/kiq.rb
ADDED
data/lib/kiq/backer.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
module Kiq
|
2
|
+
module CLI
|
3
|
+
# Hosts logic for the backer object
|
4
|
+
class Backer
|
5
|
+
@@instance_collector = []
|
6
|
+
# Allows access to name, credit_card, and amount
|
7
|
+
attr_accessor :name, :credit_card, :amount
|
8
|
+
|
9
|
+
# @param name [String]
|
10
|
+
# @param amount [String]
|
11
|
+
# @credit_card [String]
|
12
|
+
# Initalizes a new Backer object
|
13
|
+
def initialize(name, credit_card, amount)
|
14
|
+
@name = name
|
15
|
+
@amount = amount
|
16
|
+
@credit_card = credit_card
|
17
|
+
end
|
18
|
+
|
19
|
+
# @param project [Project]
|
20
|
+
# Check if project exists
|
21
|
+
# @return [Boolean] warning if false
|
22
|
+
def self.validate_project_exists(project)
|
23
|
+
if !project.nil?
|
24
|
+
return true
|
25
|
+
else
|
26
|
+
puts "ERROR: Project doesn't exist."
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param project [Project]
|
32
|
+
# Checks all validation methods for a credit card
|
33
|
+
# @return [Boolean] warning if false
|
34
|
+
def self.validate_card(project, credit_card)
|
35
|
+
return self.check_card_uniqueness(project, credit_card) && self.check_card_luhn_10(credit_card) && self.check_card_length(credit_card) && self.check_card_numeric(credit_card) && self.check_name_length(name)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param backer_name [String]
|
39
|
+
# Checks all validation methods for a Backer's name
|
40
|
+
# @return [Boolean] warning if false
|
41
|
+
def self.validate_backer_name(backer_name)
|
42
|
+
return self.check_name_characters(backer_name) && self.check_name_length(backer_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param amount [String]
|
46
|
+
# Checks if the amount includes dollar signs
|
47
|
+
# @return [Boolean] warning if false
|
48
|
+
def self.check_amount_dollar_sign(amount)
|
49
|
+
if !amount.include?('$')
|
50
|
+
return true
|
51
|
+
else
|
52
|
+
puts "ERROR: Backing amount must not contain the '$' character or any other alphanumeric characters. Numbers only."
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param name [String]
|
58
|
+
# Checks if the name has non-alphanumeric characters besides underscores and dashes
|
59
|
+
# @return [Boolean] warning if false
|
60
|
+
def self.check_name_characters(name)
|
61
|
+
if name.scan(/[^\w-]/).empty?
|
62
|
+
return true
|
63
|
+
else
|
64
|
+
puts "ERROR: Backer name may only use alphanumeric characters, underscores, and dashes."
|
65
|
+
return false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @param name [String]
|
70
|
+
# Checks if the string is between 4 and 20 characters
|
71
|
+
# @return [Boolean] warning if false
|
72
|
+
def self.check_name_length(name)
|
73
|
+
if name.length >= 4 && name.length <= 20
|
74
|
+
return true
|
75
|
+
else
|
76
|
+
puts "ERROR: Backer name must be between 4 and 20 characters."
|
77
|
+
return false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param project [Project]
|
82
|
+
# @param credit_card [String]
|
83
|
+
# Checks if the credit card is unique, no other Backer exists with that card
|
84
|
+
# @return [Boolean] warning if false
|
85
|
+
def self.check_card_uniqueness(project, credit_card)
|
86
|
+
if project.backers[credit_card].nil?
|
87
|
+
return true
|
88
|
+
else
|
89
|
+
puts "ERROR: That card has already been added by another user!"
|
90
|
+
return false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# @param credit_card [String]
|
95
|
+
# Checks if the credit card passes the Luhn-10 algorithm
|
96
|
+
# @return [Boolean] warning if false
|
97
|
+
def self.check_card_luhn_10(credit_card)
|
98
|
+
if self.luhn_valid?(credit_card)
|
99
|
+
return true
|
100
|
+
else
|
101
|
+
puts "ERROR: That card fails Luhn-10!"
|
102
|
+
return false
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# @param credit_card [String]
|
107
|
+
# Checks if the credit card is under 19 numbers long
|
108
|
+
# @return [Boolean] warning if false
|
109
|
+
def self.check_card_length(credit_card)
|
110
|
+
if credit_card.length <= 19
|
111
|
+
return true
|
112
|
+
else
|
113
|
+
puts "ERROR: That card isn't less than or equal to 19 numbers!"
|
114
|
+
return false
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# @param credit_card [String]
|
119
|
+
# Checks if the credit card only contains numbers
|
120
|
+
# @return [Boolean] warning if false
|
121
|
+
def self.check_card_numeric(credit_card)
|
122
|
+
if credit_card.match(/^\d+$/)
|
123
|
+
return true
|
124
|
+
else
|
125
|
+
puts "ERROR: That card isn't purely numeric!"
|
126
|
+
return false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# @param credit_card [String]
|
131
|
+
# Computes Luhn-10 algorithm
|
132
|
+
# @return [Boolean]
|
133
|
+
def self.luhn_valid?(credit_card)
|
134
|
+
number = credit_card.reverse
|
135
|
+
sum = 0
|
136
|
+
count = 0
|
137
|
+
|
138
|
+
number.each_char do |char|
|
139
|
+
n = char.to_i
|
140
|
+
if count.odd?
|
141
|
+
n *= 2
|
142
|
+
end
|
143
|
+
|
144
|
+
if n >= 10
|
145
|
+
n = 1 + (n - 10)
|
146
|
+
end
|
147
|
+
|
148
|
+
sum += n
|
149
|
+
count += 1
|
150
|
+
end
|
151
|
+
|
152
|
+
(sum % 10) == 0
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'kiq/helpers/version'
|
2
|
+
|
3
|
+
# Contains all relevant constants for displaying information
|
4
|
+
module Kiq
|
5
|
+
TITLE_HASH = {
|
6
|
+
'NAME' => "kiq: a command line interface for backing Kickstarter projects.\n\n",
|
7
|
+
'SYNOPSIS' => "kiq [command] [arguments...]\n\n",
|
8
|
+
'VERSION' => "#{Kiq::VERSION}\n\n"
|
9
|
+
}
|
10
|
+
COMMAND_HASH = {
|
11
|
+
'list' => [' - Display a project including backers and backed amounts', '[project name]'],
|
12
|
+
'project' => [' - Create a new project', '[project name] [amount]'],
|
13
|
+
'back' => [' - Back a project', '[backer name] [project name] [credit card number] [backing amount]'],
|
14
|
+
'backers' => [' - Display a list of projects that a backer has backed and amounts backed', '[backer name]']
|
15
|
+
}
|
16
|
+
|
17
|
+
SPACES = ' '
|
18
|
+
|
19
|
+
GLOBAL_COMMAND = "--help - Get help for a command\n\n"
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Kiq
|
2
|
+
# Displays help to the user
|
3
|
+
class Display
|
4
|
+
# Returns the full help center
|
5
|
+
# @return [String]
|
6
|
+
def self.help
|
7
|
+
Kiq::TITLE_HASH.keys.each do |key|
|
8
|
+
puts key
|
9
|
+
puts Kiq::SPACES + Kiq::TITLE_HASH[key]
|
10
|
+
end
|
11
|
+
|
12
|
+
puts 'GLOBAL OPTIONS'
|
13
|
+
puts Kiq::SPACES + Kiq::GLOBAL_COMMAND
|
14
|
+
|
15
|
+
puts "COMMANDS"
|
16
|
+
Kiq::COMMAND_HASH.keys.each do |key|
|
17
|
+
puts Kiq::SPACES + key + Kiq::COMMAND_HASH[key][0]
|
18
|
+
end
|
19
|
+
puts "\n"
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param command [String]
|
23
|
+
# Returns the command help center
|
24
|
+
# @return [String]
|
25
|
+
def self.command_help(command)
|
26
|
+
puts 'NAME'
|
27
|
+
puts Kiq::SPACES + Kiq::TITLE_HASH['NAME']
|
28
|
+
puts 'SYNOPSIS'
|
29
|
+
puts Kiq::SPACES + command + Kiq::SPACES + Kiq::COMMAND_HASH[command][1]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/kiq/project.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
module Kiq
|
2
|
+
module CLI
|
3
|
+
# Hosts logic for the backer object
|
4
|
+
class Project
|
5
|
+
@@instance_collector = []
|
6
|
+
# Allows access to name, amount, and backers
|
7
|
+
attr_accessor :name, :amount, :backers
|
8
|
+
|
9
|
+
# @param name [String]
|
10
|
+
# @param amount [String]
|
11
|
+
# @param backers [Hash]
|
12
|
+
# @instance_collector [Array]
|
13
|
+
# Initialize a project object with name and amount
|
14
|
+
def initialize(name, amount, backers={})
|
15
|
+
@name = name
|
16
|
+
@amount = amount
|
17
|
+
@backers = backers
|
18
|
+
@@instance_collector << self
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sets up easy was to call all instances of an object
|
22
|
+
# @return instances
|
23
|
+
def self.all_offspring
|
24
|
+
@@instance_collector
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param project [Array] the user input for project
|
28
|
+
# @param projects [Array] all instances of Project
|
29
|
+
# Calls all validation methods for a given project
|
30
|
+
# @return [Boolean]
|
31
|
+
def self.validate_project(project, projects)
|
32
|
+
name = project[1]
|
33
|
+
amount = project[2]
|
34
|
+
|
35
|
+
return self.check_input_length(project) && self.project_does_not_exist?(project, projects) && self.check_amount_dollar_sign(amount) && self.check_name_characters(name) && self.check_name_length(name)
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param project [Array] the user input for project
|
39
|
+
# @param projects [Array] all instances of Project
|
40
|
+
# Checks if project already exists
|
41
|
+
# @return [Boolean] warning if false
|
42
|
+
def self.project_does_not_exist?(project, projects)
|
43
|
+
if projects[project[1]].nil?
|
44
|
+
return true
|
45
|
+
else
|
46
|
+
puts "ERROR: Project already exists."
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param amount [String]
|
52
|
+
# Checks if the amount includes dollar signs
|
53
|
+
# @return [Boolean] warning if false
|
54
|
+
def self.check_amount_dollar_sign(amount)
|
55
|
+
if !amount.include?('$')
|
56
|
+
return true
|
57
|
+
else
|
58
|
+
puts "ERROR: Project amount must not contain the '$' character or any other alphanumeric characters. Numbers only."
|
59
|
+
return false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# @param input [Array] full user input
|
64
|
+
# Checks if the array is the correct length for project input
|
65
|
+
# @return [Boolean] warning if false
|
66
|
+
def self.check_input_length(input)
|
67
|
+
if input.length == 3
|
68
|
+
return true
|
69
|
+
else
|
70
|
+
puts "ERROR: A project must have two arguments."
|
71
|
+
return false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param name [String]
|
76
|
+
# Checks if the name has non-alphanumeric characters besides underscores and dashes
|
77
|
+
# @return [Boolean] warning if false
|
78
|
+
def self.check_name_characters(name)
|
79
|
+
if name.scan(/[^\w-]/).empty?
|
80
|
+
return true
|
81
|
+
else
|
82
|
+
puts "ERROR: Project name may only use alphanumeric characters, underscores, and dashes."
|
83
|
+
return false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# @param name [String]
|
88
|
+
# Checks if the string is between 4 and 20 characters
|
89
|
+
# @return [Boolean] warning if false
|
90
|
+
def self.check_name_length(name)
|
91
|
+
if name.length >= 4 && name.length <= 20
|
92
|
+
return true
|
93
|
+
else
|
94
|
+
puts "ERROR: Project name must be between 4 and 20 characters."
|
95
|
+
return false
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
data/lib/kiq/run.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'kiq/project'
|
3
|
+
require 'kiq/backer'
|
4
|
+
require 'kiq/helpers/constants'
|
5
|
+
require 'kiq/helpers/display'
|
6
|
+
|
7
|
+
module Kiq
|
8
|
+
FILE = File.expand_path('.kiq')
|
9
|
+
module CLI
|
10
|
+
# Runs the program when the user types 'kiq'
|
11
|
+
class Run
|
12
|
+
# Initialize the Run with a projects hash and load the file
|
13
|
+
def initialize
|
14
|
+
@projects = {}
|
15
|
+
load_file
|
16
|
+
load_projects
|
17
|
+
end
|
18
|
+
|
19
|
+
# Uses user input
|
20
|
+
# @return [String] help documentation or handles valid input
|
21
|
+
def perform
|
22
|
+
user_input = ARGV
|
23
|
+
primary_command = user_input[0]
|
24
|
+
valid_commands = ['project', 'list', 'back', 'backer']
|
25
|
+
|
26
|
+
case primary_command
|
27
|
+
when *valid_commands
|
28
|
+
handle(user_input)
|
29
|
+
when '-v'
|
30
|
+
puts Kiq::VERSION
|
31
|
+
when nil
|
32
|
+
Display.help
|
33
|
+
when '--help'
|
34
|
+
Display.help
|
35
|
+
else
|
36
|
+
puts "Unknown command: '#{user_input}'." unless user_input == '-h'
|
37
|
+
Display.help
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Handles valid input
|
42
|
+
# Runs relevant method for command
|
43
|
+
# Returns help if user input indicates help is needed
|
44
|
+
def handle(input)
|
45
|
+
if input[-1] == '--help'
|
46
|
+
return Display.command_help(input[0])
|
47
|
+
end
|
48
|
+
|
49
|
+
case input[0]
|
50
|
+
when "project"
|
51
|
+
add(input)
|
52
|
+
when "back"
|
53
|
+
back(input[0], input[1], input[2], input[3], input[4])
|
54
|
+
when "list"
|
55
|
+
list(input[1])
|
56
|
+
when "backer"
|
57
|
+
backer(input[1])
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param project [Array]
|
62
|
+
# Returns successful message if project added
|
63
|
+
# Returns help if add is unsuccessful
|
64
|
+
def add(project)
|
65
|
+
type = project[0]
|
66
|
+
name = project[1]
|
67
|
+
amount = project[2]
|
68
|
+
|
69
|
+
if Project.validate_project(project, @projects)
|
70
|
+
@projects[name] = Project.new(name, amount)
|
71
|
+
save
|
72
|
+
puts "Added Project: #{name}!"
|
73
|
+
else
|
74
|
+
Display.command_help(type)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param type [String]
|
79
|
+
# @param backer_name [String]
|
80
|
+
# @param project_name [String]
|
81
|
+
# @param credit_card [String]
|
82
|
+
# @param amount [String]
|
83
|
+
# @return [String] successful message if user input is valid, help if back is unsuccessful
|
84
|
+
def back(type, backer_name, project_name, credit_card, amount)
|
85
|
+
project = @projects[project_name]
|
86
|
+
if Backer.validate_project_exists(project) && Backer.validate_card(project, credit_card) && Backer.validate_backer_name(backer_name) && Backer.check_amount_dollar_sign(amount)
|
87
|
+
backer = Backer.new(backer_name, credit_card, amount)
|
88
|
+
project.backers[credit_card] = backer
|
89
|
+
update_goal(project, amount)
|
90
|
+
save
|
91
|
+
puts "#{backer.name.capitalize} backed #{project_name} for $#{backer.amount}."
|
92
|
+
else
|
93
|
+
Display.command_help(type)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# @param project_name [String]
|
98
|
+
# If Project instance exists, lists all backers for a certain project
|
99
|
+
# @return [String] of backers, or error and help if project does not exist
|
100
|
+
def list(project_name)
|
101
|
+
if !Project.project_does_not_ exist?(['project', project_name], @projects)
|
102
|
+
project = Project.all_offspring.find { |p| p.name == project_name }
|
103
|
+
puts "Project Name: #{project.name}"
|
104
|
+
puts "Amount Remaining: $#{project.amount}"
|
105
|
+
check_goal(project)
|
106
|
+
puts "BACKERS:"
|
107
|
+
project.backers.each do |backer|
|
108
|
+
puts "Backer #{backer[1].name}, Amount: $#{backer[1].amount}"
|
109
|
+
end
|
110
|
+
else
|
111
|
+
puts "ERROR: Project does not exist.\n\n"
|
112
|
+
Display.help
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# @param backer_name [String]
|
117
|
+
# Lists projects backed by a certain backer
|
118
|
+
# @return [String] of projects, or nothing if none exist
|
119
|
+
def backer(backer_name)
|
120
|
+
@projects.each do |project|
|
121
|
+
project[1].backers.each do |backer|
|
122
|
+
if backer[1].name == backer_name
|
123
|
+
puts "Backed #{project[1].name} for $#{backer[1].amount} dollars"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
# Accessor for the project list file
|
132
|
+
# Returns the file path
|
133
|
+
def file
|
134
|
+
@file ||= File.exist?(FILE) ? FILE : '.kiq'
|
135
|
+
end
|
136
|
+
|
137
|
+
# Creates a new project file if none exists
|
138
|
+
# Returns nothing
|
139
|
+
def load_file
|
140
|
+
File.exist?(file) ? return : save
|
141
|
+
end
|
142
|
+
|
143
|
+
# Loads the yaml projects file and creates a projects hash
|
144
|
+
# Returns nothing
|
145
|
+
def load_projects
|
146
|
+
unless File.zero?(@file)
|
147
|
+
contents = YAML.load_file(@file)
|
148
|
+
contents.keys.each do |d|
|
149
|
+
@projects[d] = Project.new(contents[d].name, contents[d].amount, contents[d].backers)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# Saves the current list of projects to the yaml file (serialization)
|
155
|
+
# Returns nothing
|
156
|
+
def save
|
157
|
+
File.open(file, "w") {|f| f.write(@projects.to_yaml) }
|
158
|
+
end
|
159
|
+
|
160
|
+
# Updates project goal
|
161
|
+
# Returns check_goal if goal is met
|
162
|
+
def update_goal(project, amount)
|
163
|
+
project.amount = project.amount.to_f - amount.to_f
|
164
|
+
check_goal(project)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Checks if project amount has met the goal
|
168
|
+
# @return [String] success message if goal met, else returns nothing
|
169
|
+
def check_goal(project)
|
170
|
+
if project.amount.to_i <= 0
|
171
|
+
puts "Reached goal!"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
data/spec/kiq.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Kiq::CLI::Backer do
|
4
|
+
let(:backer) { Kiq::CLI::Backer }
|
5
|
+
context 'validate a project' do
|
6
|
+
describe '#self.validate_project_exists(project)' do
|
7
|
+
project = Kiq::CLI::Project.new('Test', '300', {})
|
8
|
+
non_existant_project = nil
|
9
|
+
it 'returns true if a project exists' do
|
10
|
+
expect(backer.validate_project_exists(project)).to eq true
|
11
|
+
end
|
12
|
+
it 'returns false if a project does not exist' do
|
13
|
+
expect(backer.validate_project_exists(non_existant_project)).to eq false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'validate a card' do
|
19
|
+
describe 'self.check_card_uniqueness(project, credit_card)' do
|
20
|
+
project = Kiq::CLI::Project.new('Test', '300', {})
|
21
|
+
project_backer = Kiq::CLI::Backer.new('Jane', '4000', '5555555555554444')
|
22
|
+
project.backers = {"5555555555554444"=> project_backer}
|
23
|
+
credit_card = "5555555555554444"
|
24
|
+
|
25
|
+
it 'returns false if the card is not new' do
|
26
|
+
expect(backer.check_card_uniqueness(project, credit_card)).to eq false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'self.self.check_card_luhn_10(credit_card)' do
|
31
|
+
valid_cards = ['4111111111111111', '378282246310005', '371449635398431', '5610591081018250']
|
32
|
+
invalid_cards = ['1234567890123456', ]
|
33
|
+
|
34
|
+
it 'returns true if the card is valid' do
|
35
|
+
valid_cards.each do |card|
|
36
|
+
expect(backer.check_card_luhn_10(card)).to eq true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns false if the card is not valid' do
|
41
|
+
invalid_cards.each do |card|
|
42
|
+
expect(backer.check_card_luhn_10(card)).to eq false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'self.check_card_length(credit_card)' do
|
48
|
+
valid_card = '123456789'
|
49
|
+
invalid_card = '123456789098765432123456'
|
50
|
+
it 'returns true if the card is under 19 characters' do
|
51
|
+
expect(backer.check_card_length(valid_card)).to eq true
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'returns false if the card is not under 19 characters' do
|
55
|
+
expect(backer.check_card_length(invalid_card)).to eq false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe 'self.check_card_numeric(credit_card)' do
|
60
|
+
valid_card = '123456789'
|
61
|
+
invalid_cards = ['123fds45678', '3948-3929-34', '_394_']
|
62
|
+
it 'returns true if the card is under 19 characters' do
|
63
|
+
expect(backer.check_card_numeric(valid_card)).to eq true
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'returns false if the card is not under 19 characters' do
|
67
|
+
invalid_cards.each do |card|
|
68
|
+
expect(backer.check_card_numeric(card)).to eq false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'validate a backer name' do
|
75
|
+
describe 'self.check_name_characters(name)' do
|
76
|
+
good_name = 'good_name-'
|
77
|
+
bad_name = 'bad name'
|
78
|
+
it 'returns true if name has correct chacters' do
|
79
|
+
expect(backer.check_name_characters(good_name)).to eq true
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'returns false if name does not have correct chacters' do
|
83
|
+
expect(backer.check_name_characters(bad_name)).to eq false
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'self.check_name_length(name)' do
|
88
|
+
long_name = 'abcdefghijklmnopqrstuvwxyz'
|
89
|
+
short_name = 'abc'
|
90
|
+
good_name = 'justright'
|
91
|
+
|
92
|
+
it 'returns true if name is between 4 and 20 characters long' do
|
93
|
+
expect(backer.check_name_length(good_name)).to eq true
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'returns false if name is not between 4 and 20 characters long' do
|
97
|
+
expect(backer.check_name_length(short_name)).to eq false
|
98
|
+
expect(backer.check_name_length(long_name)).to eq false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'validate an amount' do
|
104
|
+
describe 'self.check_amount_dollar_sign(amount)' do
|
105
|
+
good_amount = '400'
|
106
|
+
bad_amount = '$400'
|
107
|
+
it 'returns true if amount has no dollar sign' do
|
108
|
+
expect(backer.check_amount_dollar_sign(good_amount)).to eq true
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'returns false if amount has dollar sign' do
|
112
|
+
expect(backer.check_amount_dollar_sign(bad_amount)).to eq false
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Kiq::CLI::Project do
|
4
|
+
|
5
|
+
describe '#self.all_offspring' do
|
6
|
+
it 'returns all the instances of project class' do
|
7
|
+
Kiq::CLI::Project.new('Test', '300', {})
|
8
|
+
expect(Kiq::CLI::Project.all_offspring.length).to_not eq 0
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'validating a project' do
|
13
|
+
describe 'self.project_does_not_exist?(project, projects)' do
|
14
|
+
Kiq::CLI::Project.new('Test', '300', {})
|
15
|
+
projects = {'Test' => Kiq::CLI::Project.all_offspring.first}
|
16
|
+
good_project = ['project', 'NotTest']
|
17
|
+
bad_project = ['project', 'Test']
|
18
|
+
|
19
|
+
it 'returns true if a project does not exist' do
|
20
|
+
expect(Kiq::CLI::Project.project_does_not_exist?(good_project, projects)).to eq true
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'returns false if a project does not exist' do
|
24
|
+
expect(Kiq::CLI::Project.project_does_not_exist?(bad_project, projects)).to eq false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe 'self.check_amount_dollar_sign(amount)' do
|
29
|
+
good_amount = '400'
|
30
|
+
bad_amount = '$400'
|
31
|
+
it 'returns true if amount has no dollar sign' do
|
32
|
+
expect(Kiq::CLI::Project.check_amount_dollar_sign(good_amount)).to eq true
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'returns false if amount has dollar sign' do
|
36
|
+
expect(Kiq::CLI::Project.check_amount_dollar_sign(bad_amount)).to eq false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe 'self.check_input_length(input)' do
|
41
|
+
short_input = ['Test', 'One']
|
42
|
+
long_input = ['Test', 'One', 'Two', 'Three']
|
43
|
+
good_input = ['Test', 'One', 'Two']
|
44
|
+
|
45
|
+
it 'returns true if correct number of args' do
|
46
|
+
expect(Kiq::CLI::Project.check_input_length(good_input)).to eq true
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'returns false if incorrect number of args' do
|
50
|
+
expect(Kiq::CLI::Project.check_input_length(short_input)).to eq false
|
51
|
+
expect(Kiq::CLI::Project.check_input_length(long_input)).to eq false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'self.check_name_characters(name)' do
|
56
|
+
good_name = 'good_name-'
|
57
|
+
bad_name = 'bad name'
|
58
|
+
it 'returns true if name has correct chacters' do
|
59
|
+
expect(Kiq::CLI::Project.check_name_characters(good_name)).to eq true
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'returns false if name does not have correct chacters' do
|
63
|
+
expect(Kiq::CLI::Project.check_name_characters(bad_name)).to eq false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'self.check_name_length(name)' do
|
68
|
+
long_name = 'abcdefghijklmnopqrstuvwxyz'
|
69
|
+
short_name = 'abc'
|
70
|
+
good_name = 'justright'
|
71
|
+
|
72
|
+
it 'returns true if name is between 4 and 20 characters long' do
|
73
|
+
expect(Kiq::CLI::Project.check_name_length(good_name)).to eq true
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'returns false if name is not between 4 and 20 characters long' do
|
77
|
+
expect(Kiq::CLI::Project.check_name_length(short_name)).to eq false
|
78
|
+
expect(Kiq::CLI::Project.check_name_length(long_name)).to eq false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Kiq::CLI::Run do
|
4
|
+
it 'can initialize a run' do
|
5
|
+
expect( Kiq::CLI::Run.new).to_not eq nil
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '#add(project)' do
|
9
|
+
project = ['project', 'RandomProject', '300']
|
10
|
+
Kiq::CLI::Run.new.add(project)
|
11
|
+
projects = { 'RandomProject' => Kiq::CLI::Project.all_offspring.first }
|
12
|
+
|
13
|
+
it 'adds a project' do
|
14
|
+
expect(Kiq::CLI::Project.project_does_not_exist?('RandomProject', projects)).to eq true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
|
+
require 'kiq'
|
3
|
+
|
4
|
+
require 'simplecov'
|
5
|
+
SimpleCov.start
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
original_stderr = $stderr
|
9
|
+
original_stdout = $stdout
|
10
|
+
config.before(:all) do
|
11
|
+
# Redirect stderr and stdout
|
12
|
+
$stderr = File.open(File::NULL, "w")
|
13
|
+
$stdout = File.open(File::NULL, "w")
|
14
|
+
end
|
15
|
+
config.after(:all) do
|
16
|
+
$stderr = original_stderr
|
17
|
+
$stdout = original_stdout
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kiq
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Logan McDonald
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.14'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.14'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.1'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: simplecov
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.12'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.12'
|
55
|
+
description: Use this command line tool to back Kickstarter campaigns.
|
56
|
+
email:
|
57
|
+
- logan@logancodes.it
|
58
|
+
executables:
|
59
|
+
- kiq
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- Gemfile
|
64
|
+
- README.md
|
65
|
+
- Rakefile
|
66
|
+
- bin/kiq
|
67
|
+
- kiq.gemspec
|
68
|
+
- lib/kiq.rb
|
69
|
+
- lib/kiq/backer.rb
|
70
|
+
- lib/kiq/helpers/constants.rb
|
71
|
+
- lib/kiq/helpers/display.rb
|
72
|
+
- lib/kiq/helpers/version.rb
|
73
|
+
- lib/kiq/project.rb
|
74
|
+
- lib/kiq/run.rb
|
75
|
+
- spec/kiq.rb
|
76
|
+
- spec/kiq/backer_spec.rb
|
77
|
+
- spec/kiq/project_spec.rb
|
78
|
+
- spec/kiq/run_spec.rb
|
79
|
+
- spec/spec_helper.rb
|
80
|
+
homepage: https://github.com/loganmeetsworld/kiq
|
81
|
+
licenses:
|
82
|
+
- MIT
|
83
|
+
metadata: {}
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
requirements: []
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 2.6.6
|
101
|
+
signing_key:
|
102
|
+
specification_version: 4
|
103
|
+
summary: Mini Kickstarter Command Line Interface
|
104
|
+
test_files:
|
105
|
+
- spec/kiq.rb
|
106
|
+
- spec/kiq/backer_spec.rb
|
107
|
+
- spec/kiq/project_spec.rb
|
108
|
+
- spec/kiq/run_spec.rb
|
109
|
+
- spec/spec_helper.rb
|