kiq 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://travis-ci.org/loganmeetsworld/kiq.svg?branch=master)](https://travis-ci.org/loganmeetsworld/kiq) [![Gem Version](https://badge.fury.io/rb/kiq.svg)](https://badge.fury.io/rb/kiq) [![Inline docs](http://inch-ci.org/github/loganmeetsworld/kiq.svg?branch=master)](http://inch-ci.org/github/loganmeetsworld/kiq) [![Code Climate](https://codeclimate.com/github/loganmeetsworld/kiq/badges/gpa.svg)](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
|