je_crowdfund 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: eecf1a23692aa22c3ec570eea372ddecf925e591ccc681419f5d8edb8fc12711
4
+ data.tar.gz: 9019a82ce5ea00ed98d256b3fa29619f51132ec41596401e4a67059eec12e9f7
5
+ SHA512:
6
+ metadata.gz: 8ad2b8173657f52c6bc140d5fd44a6b0970501d1d00f1feea5bc719d2e29080fc15b4d9bccf020efe0426d4548b6bc3bdc4b006712041da73e36691058b43d42
7
+ data.tar.gz: 4b1a273469f6bb1972a11ac46183a1fb866cf0ea1a19532f9d5b7dcbe5a1349de57336c4e273d177ccdf908dee97559350b5ab6b685717990515ec52956b6969
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2021 Jonathan Eccles
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ The [Pragmatic Studios](https://pragmaticstudio.com) Crowd Fund bonus game created in their Learn Ruby course.
2
+
3
+ Projects can be added, funded, have funds removed and received pledges. There may also be some special types of project lurking...
data/bin/crowdfund ADDED
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/crowdfund/fundrequest'
4
+
5
+ # project1 = Project.new("wooden whistle", 600, 500, 1)
6
+ # project2 = Project.new("paper notebook", 150)
7
+ # project3 = Project.new("brush", 100, 0)
8
+
9
+ crowd_list = CrowdFund::FundRequest.new("crowd funding")
10
+ # crowd_list.add_project(project1)
11
+ # crowd_list.add_project(project2)
12
+ # crowd_list.add_project(project3)
13
+
14
+ crowd_list.load_projects(File.join(File.dirname(__FILE__), 'project_list.csv'))
15
+
16
+ rounds = ARGV.shift || "1"
17
+ crowd_list.request_funding(rounds.to_i)
18
+
19
+ loop do
20
+ puts("\nRun another funding roung? (y/n): ")
21
+ answer = gets.chomp.downcase
22
+ case answer
23
+ when 'y', 'yes'
24
+ crowd_list.request_funding(1)
25
+ when 'n', 'no', 'x', 'q', 'quit', 'exit'
26
+ crowd_list.print_stats
27
+ break
28
+ else
29
+ puts "Enter 'y' or 'n' only"
30
+ end
31
+ end
32
+
33
+ crowd_list.save_unfunded_projects("#{crowd_list.name.downcase.gsub(' ', '_')}_unfunded_projects.txt")
@@ -0,0 +1,3 @@
1
+ Wooden whistle, 100, 0, 0
2
+ Paper notebook, 500, 50, 1
3
+ Brush, 1000, 100, 2
@@ -0,0 +1,13 @@
1
+ module CrowdFund
2
+ class Die
3
+ attr_reader :number
4
+
5
+ def initialize
6
+ roll
7
+ end
8
+
9
+ def roll
10
+ @number = Random.rand(1..6)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module CrowdFund
2
+ module Fundable
3
+ def funded?
4
+ funding_gap <= 0
5
+ end
6
+
7
+ def add_funds(amount = 25)
8
+ self.funding += amount
9
+ self.backers += 1
10
+ puts "Project #{name} got an addtional £#{amount} in funding!"
11
+ end
12
+
13
+ def remove_funds(amount = 15)
14
+ self.funding -= amount
15
+ self.backers -= 1
16
+ puts "Project #{name} lost £#{amount} in funding!"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'die'
2
+ require_relative 'pledge_pool'
3
+
4
+ module CrowdFund
5
+ module FundingRound
6
+ def self.one_round(project)
7
+ die = Die.new
8
+ @number_rolled = die.roll
9
+ if @number_rolled.even?
10
+ project.add_funds
11
+ else
12
+ project.remove_funds
13
+ end
14
+ project.record_pledge(PledgePool.random)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,65 @@
1
+ require 'csv'
2
+ require_relative 'project'
3
+ require_relative 'funding_round'
4
+
5
+ module CrowdFund
6
+ class FundRequest
7
+ attr_reader :name
8
+
9
+ def initialize(name)
10
+ @name = name.capitalize
11
+ @projects = []
12
+ end
13
+
14
+ def load_projects(filename)
15
+ CSV.foreach(filename) do |line|
16
+ add_project(Project.from_csv(line))
17
+ end
18
+ end
19
+
20
+ def save_unfunded_projects(to_file = "unfunded_projects.txt")
21
+ File.open(to_file, "w") do |file|
22
+ file.puts "#{@name} Unfunded Projects:"
23
+ under_funded = @projects.filter { |p| p.funded? == false }
24
+ under_funded.sort.each { |uf| file.puts uf.name_and_funding }
25
+ end
26
+ end
27
+
28
+ def add_project(project)
29
+ @projects << project
30
+ end
31
+
32
+ def request_funding(rounds)
33
+ puts "There are #{@projects.size} projects in the #{@name} project list:"
34
+ puts @projects
35
+
36
+ 1.upto(rounds) do |round|
37
+ puts "\nRound: #{round}"
38
+ @projects.each do |project|
39
+ FundingRound.one_round(project)
40
+ end
41
+ end
42
+
43
+ print_stats
44
+ end
45
+
46
+ def print_stats
47
+ puts "\nFunding Results"
48
+
49
+ fully_funded, under_funded = @projects.partition { |p| p.funded? }
50
+ puts "\n#{fully_funded.size} projects funded"
51
+ fully_funded.each {|ff| puts ff.name_and_funding}
52
+
53
+ puts "\n#{under_funded.size} projects under funded"
54
+ under_funded.sort.each {|uf| puts uf.name_and_funding}
55
+
56
+ @projects.sort.each do |project|
57
+ puts "\nProject #{project.name}'s pledges:"
58
+ project.each_donated_pledge do |pledge|
59
+ puts "£#{pledge.amount} in #{pledge.name} pledges"
60
+ end
61
+ puts "£#{project.pledges} in total pledges"
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'project'
2
+
3
+ module CrowdFund
4
+ class GovernmentMatchProject < Project
5
+ def initialize(name, funding_goal, funding = 0, backers = 0)
6
+ super(name, funding_goal, funding, backers)
7
+ @name = "[GOV MATCH] #{name.capitalize}"
8
+ end
9
+
10
+ def add_funds(amount = 25)
11
+ super(amount) if funding_gap <= @funding_goal / 2
12
+ super(amount)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'project'
2
+
3
+ module CrowdFund
4
+ class GrantProject < Project
5
+ def initialize(name, funding_goal, funding = 0, backers = 0)
6
+ super(name, funding_goal, funding, backers)
7
+ @name = "[GRANT] #{name.capitalize}"
8
+ end
9
+
10
+ def remove_funds(amount = 0)
11
+ puts "Project #{@name} is a Grant Project and cannot have funds removed!"
12
+ end
13
+ end
14
+
15
+ if __FILE__ == $0
16
+ grant = GrantProject.new("battery", 150)
17
+ puts grant.name_and_funding
18
+ grant.add_funds(100)
19
+ 2.times { grant.add_funds }
20
+ puts grant.name_and_funding
21
+ 8.times { grant.remove_funds }
22
+ puts grant.name_and_funding
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ module CrowdFund
2
+ Pledge = Struct.new(:name, :amount)
3
+
4
+ module PledgePool
5
+
6
+ PLEDGES = [
7
+ Pledge.new(:base, 25),
8
+ Pledge.new(:bronze, 50),
9
+ Pledge.new(:silver, 75),
10
+ Pledge.new(:gold, 100)
11
+ ]
12
+
13
+ def self.random
14
+ PLEDGES.sample
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ module CrowdFund
2
+ module Pledgeable
3
+ def record_pledge(pledge)
4
+ self.pledges_received[pledge.name] += pledge.amount
5
+ puts "Project #{name} received a #{pledge.name} pledge worth £#{pledge.amount}."
6
+ puts "Project #{name}\s pledges #{pledges_received}"
7
+ end
8
+
9
+ def pledges
10
+ pledges_received.values.reduce(0, :+)
11
+ end
12
+
13
+ def each_donated_pledge
14
+ pledges_received.each do |name, points|
15
+ yield(Pledge.new(name, points))
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,45 @@
1
+ require_relative 'fundable'
2
+ require_relative 'pledgeable'
3
+
4
+ module CrowdFund
5
+ class Project
6
+ include Fundable, Pledgeable
7
+
8
+ attr_reader :funding_goal
9
+ attr_accessor :name, :funding, :backers, :pledges_received
10
+
11
+ def initialize(name, funding_goal, funding = 0, backers = 0)
12
+ @name = name.capitalize
13
+ @funding_goal = funding_goal
14
+ @funding = funding
15
+ @backers = backers
16
+ @pledges_received = Hash.new(0)
17
+ end
18
+
19
+ def self.from_csv(csv_line)
20
+ Project.new(csv_line[0], Integer(csv_line[1]), Integer(csv_line[2]), Integer(csv_line[3]))
21
+ end
22
+
23
+ def funding_gap
24
+ funding_goal - funding - pledges
25
+ end
26
+
27
+ def name_and_funding
28
+ "#{@name.ljust(20, '.')} £#{@funding + pledges} of £#{@funding_goal}"
29
+ end
30
+
31
+ def <=>(project)
32
+ project.funding_gap <=> self.funding_gap
33
+ end
34
+
35
+ def to_s
36
+ "Project #{@name} has £#{@funding + pledges} in funding and pledges from #{@backers} backer(s) towards a goal of £#{@funding_goal} leaving a gap of £#{funding_gap} as of #{Time.now.strftime("%F %T")}"
37
+ end
38
+ end
39
+
40
+
41
+ if __FILE__ == $0
42
+ project1 = Project.new("testbed", 1000, 500, 10)
43
+ puts project1
44
+ end
45
+ end
@@ -0,0 +1,29 @@
1
+ require 'crowdfund/fundrequest'
2
+
3
+ module CrowdFund
4
+ describe FundRequest do
5
+ before do
6
+ $stdout = StringIO.new
7
+ @crowd_list = FundRequest.new("crowd funding")
8
+ @project1 = Project.new("wooden whistle", 600, 500, 1)
9
+ @crowd_list.add_project(@project1)
10
+ end
11
+
12
+ it "adds funds if an even number is rolled" do
13
+ allow_any_instance_of(Die).to receive(:roll).and_return(2)
14
+
15
+ expect{ @crowd_list.request_funding(2) }.to change(@project1, :funding).by(25 * 2)
16
+ end
17
+
18
+ it "removes funds if an odd number is rolled" do
19
+ allow_any_instance_of(Die).to receive(:roll).and_return(5)
20
+
21
+ expect{ @crowd_list.request_funding(2) }.to change(@project1, :funding).by(-15 * 2)
22
+ end
23
+
24
+ it "assigns a pledge for funding during a project funding round" do
25
+ @crowd_list.request_funding(1)
26
+ expect(@project1.pledges).to_not be_zero
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ require 'crowdfund/government_match_project'
2
+
3
+ module CrowdFund
4
+ describe GovernmentMatchProject do
5
+ before do
6
+ $stdout = StringIO.new
7
+ @initial_funding = 150
8
+ @initial_backers = 1
9
+ @funding_goal = 1000
10
+ @name = "hover car"
11
+ @match = GovernmentMatchProject.new(@name, @funding_goal, @initial_funding, @initial_backers)
12
+ end
13
+
14
+ it "has a capitalized name" do
15
+ expect(@match.name).to eq("[GOV MATCH] #{@name.capitalize}")
16
+ end
17
+
18
+ it "does not match funds while under half funded" do
19
+ amount = 150
20
+ expect{ @match.add_funds(amount) }.to change(@match, :funding).by(150)
21
+ end
22
+
23
+ it "matches additional funds when projects are at least half funded" do
24
+ amount = 100
25
+ @match.add_funds(350)
26
+ expect{ @match.add_funds(amount) }.to change(@match, :funding).by(200)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,23 @@
1
+ require 'crowdfund/grant_project'
2
+
3
+ module CrowdFund
4
+ describe GrantProject do
5
+ before do
6
+ $stdout = StringIO.new
7
+ @initial_funding = 150
8
+ @initial_backers = 1
9
+ @funding_goal = 10000
10
+ @name = "hover car"
11
+ @grant = GrantProject.new(@name, @funding_goal, @initial_funding, @initial_backers)
12
+ end
13
+
14
+ it "has a capitalized name" do
15
+ expect(@grant.name).to eq("[GRANT] #{@name.capitalize}")
16
+ end
17
+
18
+ it "does not decrease funds when funds are removed" do
19
+ amount = 15
20
+ expect{ @grant.remove_funds(amount) }.to change(@grant, :funding).by(0)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ require 'crowdfund/pledge_pool'
2
+
3
+ module CrowdFund
4
+ describe Pledge do
5
+ before do
6
+ @pledge = Pledge.new(:silver, 75)
7
+ end
8
+
9
+ it "has a name attribute" do
10
+ expect(@pledge.name).to eq(:silver)
11
+ end
12
+
13
+ it "has a points attribute" do
14
+ expect(@pledge.amount).to eq(75)
15
+ end
16
+ end
17
+
18
+ describe PledgePool do
19
+ it "has four pledge levels" do
20
+ expect(PledgePool::PLEDGES.size).to eq(4)
21
+ end
22
+
23
+ it "has a base level worth 25" do
24
+ expect(PledgePool::PLEDGES[0]).to eq(Pledge.new(:base, 25))
25
+ end
26
+
27
+ it "has a bronze level worth £50" do
28
+ expect(PledgePool::PLEDGES[1]).to eq(Pledge.new(:bronze, 50))
29
+ end
30
+
31
+ it "has a silver level worth £50" do
32
+ expect(PledgePool::PLEDGES[2]).to eq(Pledge.new(:silver, 75))
33
+ end
34
+
35
+ it "has a gold level worth £100" do
36
+ expect(PledgePool::PLEDGES[3]).to eq(Pledge.new(:gold, 100))
37
+ end
38
+
39
+ it "returns a random pledge" do
40
+ pledge = PledgePool.random
41
+
42
+ expect(PledgePool::PLEDGES).to include(pledge)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,107 @@
1
+ require 'crowdfund/project'
2
+ require 'crowdfund/pledge_pool'
3
+
4
+ module CrowdFund
5
+ describe Project do
6
+ before do
7
+ $stdout = StringIO.new
8
+ @initial_funding = 150
9
+ @initial_backers = 1
10
+ @funding_goal = 10000
11
+ @name = "hover car"
12
+ @project = Project.new(@name, @funding_goal, @initial_funding, @initial_backers)
13
+ end
14
+
15
+ it "has a capitalized name" do
16
+ expect(@project.name).to eq(@name.capitalize)
17
+ end
18
+
19
+ it "has an initial target funding goal" do
20
+ expect(@project.funding_goal).not_to eq(0)
21
+ end
22
+
23
+ it "computes the total funding outstanding as the target funding amount minus the funding amount" do
24
+ expect(@project.funding_gap).to eq(@project.funding_goal - @initial_funding)
25
+ end
26
+
27
+ it "increases funds by 25 when funds are added" do
28
+ amount = 25
29
+ expect{ @project.add_funds(amount) }.to change(@project, :funding).by(amount)
30
+ end
31
+
32
+ it "increases backers by 1 when funds are added" do
33
+ amount = 25
34
+ expect{ @project.add_funds(amount) }.to change(@project, :backers).by(1)
35
+ end
36
+
37
+ it "decreases funds by 15 when funds are removed" do
38
+ amount = 15
39
+ expect{ @project.remove_funds(amount) }.to change(@project, :funding).by(-amount)
40
+ end
41
+
42
+ it "decreases backers by 1 when funds are removed" do
43
+ amount = 15
44
+ expect{ @project.remove_funds(amount) }.to change(@project, :backers).by(-1)
45
+ end
46
+
47
+ it "is not fully funded when funding < funding_goal" do
48
+ expect(@project.funded?).to eq(false)
49
+ end
50
+
51
+ it "yields each pledge level and its total value" do
52
+ @project.record_pledge(Pledge.new(:gold, 100))
53
+ @project.record_pledge(Pledge.new(:gold, 100))
54
+ @project.record_pledge(Pledge.new(:silver, 75))
55
+ @project.record_pledge(Pledge.new(:bronze, 50))
56
+ @project.record_pledge(Pledge.new(:bronze, 50))
57
+ @project.record_pledge(Pledge.new(:bronze, 50))
58
+ @project.record_pledge(Pledge.new(:base, 25))
59
+ @project.record_pledge(Pledge.new(:base, 25))
60
+
61
+ pledged = []
62
+ @project.each_donated_pledge do |pledge|
63
+ pledged << pledge
64
+ end
65
+
66
+ expect(pledged).to eq([
67
+ Pledge.new(:gold, 200),
68
+ Pledge.new(:silver, 75),
69
+ Pledge.new(:bronze, 150),
70
+ Pledge.new(:base, 50)
71
+ ])
72
+ end
73
+
74
+ it "can be created from a CSV string" do
75
+ aggregate_failures "from_csv record" do
76
+ project = Project.from_csv(["jon", "200", "0", "0"])
77
+
78
+ expect(project.name).to eq("Jon")
79
+ expect(project.funding_goal).to eq(200)
80
+ expect(project.funding).to eq(0)
81
+ expect(project.backers).to eq(0)
82
+ end
83
+ end
84
+
85
+
86
+ context "created with a default funding amount" do
87
+ before do
88
+ @project = Project.new(@name, @funding_goal)
89
+ end
90
+
91
+ it "has a default value of 0 for funding amount" do
92
+ expect(@project.funding).to eq(0)
93
+ end
94
+ end
95
+
96
+ context "fully funded" do
97
+ before do
98
+ @initial_funding = @funding_goal
99
+ @project = Project.new(@name, @funding_goal, @initial_funding, @intial_backers)
100
+ end
101
+
102
+ it "is fully funded when funding gap <= 0" do
103
+ expect(@project.funded?).to eq(true)
104
+ end
105
+ end
106
+ end
107
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: je_crowdfund
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Eccles
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-02-05 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: '3.10'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 3.10.0
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '3.10'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 3.10.0
33
+ description: "The [Pragmatic Studios](https://pragmaticstudio.com) Crowd Fund bonus
34
+ game created in their Learn Ruby course. \n\nProjects can be added, funded, have
35
+ funds removed and received pledges. There may also be some special types of project
36
+ lurking..."
37
+ email: getjon@me.com
38
+ executables:
39
+ - crowdfund
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - LICENSE.txt
44
+ - README.md
45
+ - bin/crowdfund
46
+ - bin/project_list.csv
47
+ - lib/crowdfund/die.rb
48
+ - lib/crowdfund/fundable.rb
49
+ - lib/crowdfund/funding_round.rb
50
+ - lib/crowdfund/fundrequest.rb
51
+ - lib/crowdfund/government_match_project.rb
52
+ - lib/crowdfund/grant_project.rb
53
+ - lib/crowdfund/pledge_pool.rb
54
+ - lib/crowdfund/pledgeable.rb
55
+ - lib/crowdfund/project.rb
56
+ - spec/crowdfund/fundrequest_spec.rb
57
+ - spec/crowdfund/government_match_project_spec.rb
58
+ - spec/crowdfund/grant_project_spec.rb
59
+ - spec/crowdfund/pledge_pool_spec.rb
60
+ - spec/crowdfund/project_spec.rb
61
+ homepage: https://pragmaticstudio.com/courses/ruby
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '1.9'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubygems_version: 3.1.4
81
+ signing_key:
82
+ specification_version: 4
83
+ summary: Pragmatic Studio Ruby course Crowd Funding bonus game
84
+ test_files:
85
+ - spec/crowdfund/project_spec.rb
86
+ - spec/crowdfund/pledge_pool_spec.rb
87
+ - spec/crowdfund/fundrequest_spec.rb
88
+ - spec/crowdfund/government_match_project_spec.rb
89
+ - spec/crowdfund/grant_project_spec.rb