crowd_fund_dubesoftware 1.0.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7e34d49648882cce1048ea25e0c28fb65c2e7c98256f6a57204f7fbee4fb0b51
4
+ data.tar.gz: 44d07ca6daaf98b1c91662dbe7744acddc47543d2872701f390ae7c8f60c8546
5
+ SHA512:
6
+ metadata.gz: aa38f2cf8e014867cf28f0ab836c46c7608590defcf3809c727bc328673aac031a6a89dfbbef62fb9811699cd440af5a93586bd1c1a4a2f91d3f366548f71462
7
+ data.tar.gz: 6ad0e4f280950ce21993a4bd4ec88c2c2a6d8f5b4156cf32b965421bcbc9e9bf7aca1e9d1d754edca340981c6d911632f8869c1eb102da129531d84f05875358
data/LICENSE ADDED
@@ -0,0 +1,16 @@
1
+ MIT No Attribution
2
+
3
+ Copyright 2022 @dubesoftware
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this
6
+ software and associated documentation files (the "Software"), to deal in the Software
7
+ without restriction, including without limitation the rights to use, copy, modify,
8
+ merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
12
+ INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
13
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
14
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
15
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
16
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,3 @@
1
+ A console-based funding program written in Ruby.
2
+
3
+ This program is the outcome of bonus exercise work for the excellent Ruby course by Mike and Nicole Clark of The Pragmatic Studio.
data/bin/crowd_fund ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/crowd_fund/project'
4
+ require_relative '../lib/crowd_fund/project_manager'
5
+
6
+ project_manager = CrowdFund::ProjectManager.new("VC-Friendly Start-up Projects")
7
+ default_project_file = File.join(File.dirname(__FILE__), 'projects.csv')
8
+ project_manager.load_projects(ARGV.shift || default_project_file)
9
+
10
+ loop do
11
+ puts("How many funding rounds? ('quit' to exit)")
12
+ answer = gets.chomp.downcase
13
+
14
+ case answer
15
+ when /^\d+$/
16
+ project_manager.request_funding(Integer(answer))
17
+ when 'quit', 'exit'
18
+ project_manager.print_stats
19
+ break
20
+ else
21
+ puts("Please enter a number or 'quit'")
22
+ end
23
+ end
24
+
25
+ project_manager.save_needed_funding
data/bin/projects.csv ADDED
@@ -0,0 +1,3 @@
1
+ Project1,100,0
2
+ Project2,500,50
3
+ Project3,1000,100
@@ -0,0 +1,13 @@
1
+ module CrowdFund
2
+ class Die
3
+ attr_accessor :number
4
+
5
+ def initialize
6
+ @number = roll
7
+ end
8
+
9
+ def roll
10
+ rand(1..6)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,28 @@
1
+ module CrowdFund
2
+ module Fundable
3
+
4
+ def add_funds
5
+ self.current_funding_amount += 25
6
+ puts "\nProject #{name} got $25 more!"
7
+ end
8
+
9
+ def remove_funds
10
+ self.current_funding_amount -= 15
11
+ puts "\nProject #{name} lost $15!"
12
+ end
13
+
14
+ def add_to_funds(amount)
15
+ self.current_funding_amount += amount
16
+ puts "\nProject #{name} got #{amount} funds!"
17
+ end
18
+
19
+ def remove_from_funds(amount)
20
+ self.current_funding_amount -= amount
21
+ puts "\nProject #{name} lost #{amount} funds!"
22
+ end
23
+
24
+ def fully_funded?
25
+ total_funding_received >= target_funding_amount
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ require_relative 'die'
2
+ require_relative 'pledge_pool'
3
+
4
+ module CrowdFund
5
+ module FundingRound
6
+ def self.fund(project)
7
+ die = Die.new
8
+
9
+ case die.roll % 2
10
+ when 0
11
+ project.add_funds
12
+ when 1
13
+ project.remove_funds
14
+ end
15
+
16
+ pledge = PledgePool.random
17
+ project.received_pledge(pledge)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ require_relative 'project'
2
+
3
+ module CrowdFund
4
+ class GrantProject < Project
5
+
6
+ def remove_funds
7
+ "Grant projects do not allow removal of funds."
8
+ end
9
+
10
+ def remove_from_funds(amount)
11
+ "Grant projects do not allow deduction from funds."
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ require_relative 'project'
2
+
3
+ module CrowdFund
4
+ class MatchedFundsProject < Project
5
+
6
+ def initialize(name, initial_funding_amount, target_funding_amount = 0, matching_ratio = 0.5)
7
+ super(name, initial_funding_amount, target_funding_amount)
8
+ @matching_ratio = matching_ratio
9
+ end
10
+
11
+ def match_funds
12
+ if @current_funding_amount >= (target_funding_amount * 0.5)
13
+ add_to_funds(target_funding_amount * 0.5)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module CrowdFund
2
+ Pledge = Struct.new(:name, :amount)
3
+
4
+ module PledgePool
5
+
6
+ PLEDGES = [
7
+ Pledge.new(:bronze, 50),
8
+ Pledge.new(:silver, 75),
9
+ Pledge.new(:gold, 100)
10
+ ]
11
+
12
+ def self.random
13
+ PLEDGES.sample
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,57 @@
1
+ require_relative 'die'
2
+ require_relative 'pledge_pool'
3
+ require_relative 'fundable'
4
+
5
+ module CrowdFund
6
+ class Project
7
+ include Fundable
8
+
9
+ attr_accessor :name, :current_funding_amount
10
+ attr_reader :initial_funding_amount, :target_funding_amount
11
+
12
+ def initialize(name, initial_funding_amount, target_funding_amount = 0)
13
+ @name = name
14
+ @current_funding_amount = @initial_funding_amount = initial_funding_amount
15
+ @target_funding_amount = target_funding_amount
16
+ @received_pledges = Hash.new(0)
17
+ end
18
+
19
+ def to_s
20
+ "Project #{@name} has $#{total_funding_received} towards a goal of $#{@target_funding_amount}."
21
+ end
22
+
23
+ def total_funding_still_needed
24
+ @target_funding_amount - total_funding_received
25
+ end
26
+
27
+ def received_pledge(pledge)
28
+ @received_pledges[pledge.name] += pledge.amount
29
+ puts "#{@name} received a #{pledge.name} pledge worth $#{pledge.amount}."
30
+ puts "#{@name}'s pledges: #{@received_pledges}"
31
+ end
32
+
33
+ def pledges
34
+ @received_pledges.values.reduce(0, :+)
35
+ end
36
+
37
+ def total_funding_received
38
+ @current_funding_amount + pledges
39
+ end
40
+
41
+ def each_received_pledge
42
+ @received_pledges.each do |name, amount|
43
+ yield Pledge.new(name, amount)
44
+ end
45
+ end
46
+
47
+ def funding_needed_entry
48
+ formatted_name = @name.ljust(20, '.')
49
+ "#{formatted_name} #{total_funding_still_needed}"
50
+ end
51
+
52
+ def self.from_csv(string)
53
+ name, initial_funding_amount = string.split(',')
54
+ Project.new(name, Integer(initial_funding_amount))
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,91 @@
1
+ require 'csv'
2
+ require_relative 'funding_round'
3
+ require_relative 'pledge_pool'
4
+
5
+ module CrowdFund
6
+ class ProjectManager
7
+ attr_reader :title
8
+
9
+ def initialize(title)
10
+ @title = title
11
+ @projects = []
12
+ end
13
+
14
+ def add_project(project)
15
+ @projects << project
16
+ end
17
+
18
+ def load_projects(from_file)
19
+ CSV.foreach(from_file) do |row|
20
+ project = Project.new(row[0], row[1].to_i)
21
+ add_project(project)
22
+ end
23
+ end
24
+
25
+ def print_pledge_amounts
26
+ pledges = PledgePool::PLEDGES
27
+
28
+ puts "\nThere are #{pledges.size} possible pledge amounts:"
29
+ pledges.each do |pledge|
30
+ puts "\tA #{pledge.name} pledge is worth $#{pledge.amount}."
31
+ end
32
+ end
33
+
34
+ def request_funding(rounds)
35
+ puts "There are #{@projects.size} projects in #{title}:"
36
+ puts @projects
37
+
38
+ print_pledge_amounts
39
+ puts "\n"
40
+
41
+ 1.upto(rounds) do
42
+ @projects.each do |project|
43
+ FundingRound.fund(project)
44
+ puts project
45
+ end
46
+ end
47
+ end
48
+
49
+ def print_name_and_total_funding_received(project)
50
+ puts "#{project.name} (#{project.total_funding_received})"
51
+ end
52
+
53
+ def print_stats
54
+ puts "\n#{@title} Statistics:"
55
+
56
+ fully_funded_projects, under_funded_projects = @projects.partition { |p| p.fully_funded? }
57
+
58
+ puts "\n#{fully_funded_projects.size} fully-funded projects:"
59
+ fully_funded_projects.each do |p|
60
+ print_name_and_total_funding_received(p)
61
+ end
62
+
63
+ puts "\n#{under_funded_projects.size} under-funded projects:"
64
+ under_funded_projects.each do |p|
65
+ print_name_and_total_funding_received(p)
66
+ end
67
+
68
+ @projects.each do |project|
69
+ puts "\nProject #{project.name}'s pledges:"
70
+ project.each_received_pledge do |pledge|
71
+ puts "$#{pledge.amount} in #{pledge.name} pledges"
72
+ end
73
+ end
74
+
75
+ puts "\n#{@title} Needing Contributions:"
76
+ under_funded_projects.sort { |p| p.total_funding_still_needed }.each do |p|
77
+ formatted_name = p.name.ljust(20, '.')
78
+ puts "#{formatted_name} #{p.total_funding_still_needed}"
79
+ end
80
+ end
81
+
82
+ def save_needed_funding(to_file="needed_funding.txt")
83
+ File.open(to_file, "w") do |file|
84
+ file.puts "#{@title} Needing Funding:"
85
+ @projects.select { |project| project.total_funding_still_needed > 0 }.sort.each do |project|
86
+ file.puts(project.funding_needed_entry)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,27 @@
1
+ require 'crowd_fund/spec_helper'
2
+ require 'crowd_fund/grant_project'
3
+
4
+ module CrowdFund
5
+ describe GrantProject do
6
+
7
+ before do
8
+ $stdout = StringIO.new
9
+ @name = "Test Grant Project"
10
+ @initial_funding_amount = 2000
11
+ @target_funding_amount = 10000
12
+ @grant_project = GrantProject.new(@name, @initial_funding_amount, @target_funding_amount)
13
+ end
14
+
15
+ it "does not allow funds to be removed" do
16
+ @grant_project.remove_funds
17
+
18
+ @grant_project.current_funding_amount.should == @initial_funding_amount
19
+ end
20
+
21
+ it "does not allow deduction from funds" do
22
+ @grant_project.remove_from_funds(500)
23
+
24
+ @grant_project.current_funding_amount.should == @initial_funding_amount
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ require 'crowd_fund/spec_helper'
2
+ require 'crowd_fund/matched_funds_project'
3
+
4
+ module CrowdFund
5
+ describe MatchedFundsProject do
6
+
7
+ before do
8
+ $stdout = StringIO.new
9
+ @name = "Test Matched Funds Project"
10
+ @initial_funding_amount = 250
11
+ @target_funding_amount = 750
12
+ @matching_ratio = 0.5
13
+ @matched_funds_project = MatchedFundsProject.new(@name, @initial_funding_amount, @target_funding_amount, @matching_ratio)
14
+ end
15
+
16
+ it "does not match funds when less than half of target funds have been received" do
17
+ @matched_funds_project.match_funds
18
+
19
+ @matched_funds_project.current_funding_amount.should == @initial_funding_amount
20
+ end
21
+
22
+ it "matches funds when half or more of target funds have been received" do
23
+ @matched_funds_project.add_to_funds(250)
24
+ @matched_funds_project.match_funds
25
+
26
+ @matched_funds_project.current_funding_amount.should == 875
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,39 @@
1
+ require 'crowd_fund/spec_helper'
2
+ require 'crowd_fund/pledge_pool'
3
+
4
+ module CrowdFund
5
+ describe Pledge do
6
+
7
+ before do
8
+ @pledge = Pledge.new(:bronze, 50)
9
+ end
10
+
11
+ it "has a name attribute" do
12
+ @pledge.name.should == :bronze
13
+ end
14
+
15
+ it "has an amount attribute" do
16
+ @pledge.amount.should == 50
17
+ end
18
+
19
+ end
20
+
21
+ describe PledgePool do
22
+
23
+ it "has three pledges" do
24
+ PledgePool::PLEDGES.size.should == 3
25
+ end
26
+
27
+ it "has a bronze pledge worth $50" do
28
+ PledgePool::PLEDGES[0].should == Pledge.new(:bronze, 50)
29
+ end
30
+
31
+ it "has a silver pledge worth $75" do
32
+ PledgePool::PLEDGES[1].should == Pledge.new(:silver, 75)
33
+ end
34
+
35
+ it "has a gold pledge worth $100" do
36
+ PledgePool::PLEDGES[2].should == Pledge.new(:gold, 100)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,105 @@
1
+ require 'crowd_fund/spec_helper'
2
+ require 'crowd_fund/project'
3
+ require 'crowd_fund/pledge_pool'
4
+
5
+ module CrowdFund
6
+ describe Project do
7
+
8
+ before do
9
+ $stdout = StringIO.new
10
+ @name = "Test Project"
11
+ @initial_funding_amount = 1000
12
+ @target_funding_amount = 5000
13
+ @project = Project.new(@name, @initial_funding_amount, @target_funding_amount)
14
+ end
15
+
16
+ it "has an initial target funding amount" do
17
+ @project.initial_funding_amount.should == 1000
18
+ end
19
+
20
+ it "computes the total funding outstanding as the target funding amount minus the total funding received" do
21
+ @project.total_funding_still_needed.should == @project.target_funding_amount - @project.total_funding_received
22
+ end
23
+
24
+ it "increases funds by 25 when funds are added" do
25
+ Die.any_instance.stub(:roll).and_return(2)
26
+
27
+ @project.add_funds
28
+
29
+ @project.current_funding_amount.should == @initial_funding_amount + 25
30
+ end
31
+
32
+ it "decreases funds by 15 when funds are removed" do
33
+ Die.any_instance.stub(:roll).and_return(1)
34
+
35
+ @project.remove_funds
36
+
37
+ @project.current_funding_amount.should == @initial_funding_amount - 15
38
+ end
39
+
40
+ it "adds funds to a project when an even number is rolled" do
41
+ Die.any_instance.stub(:roll).and_return(2)
42
+
43
+ @project.add_funds
44
+
45
+ @project.current_funding_amount.should == @initial_funding_amount + 25
46
+ end
47
+
48
+ it "removes funds from a project when an odd number is rolled" do
49
+ Die.any_instance.stub(:roll).and_return(1)
50
+
51
+ @project.remove_funds
52
+
53
+ @project.current_funding_amount.should == @initial_funding_amount - 15
54
+ end
55
+
56
+ it "is fully funded when the total funding amount equals or exceeds the target funding amount" do
57
+ Die.any_instance.stub(:roll).and_return(2)
58
+
59
+ @project.add_to_funds(4000)
60
+
61
+ @project.should be_fully_funded
62
+ end
63
+
64
+ it "yields each received pledge and its total amount" do
65
+ @project.received_pledge(Pledge.new(:bronze, 40))
66
+ @project.received_pledge(Pledge.new(:silver, 65))
67
+ @project.received_pledge(Pledge.new(:gold, 90))
68
+ @project.received_pledge(Pledge.new(:gold, 90))
69
+ @project.received_pledge(Pledge.new(:gold, 90))
70
+ @project.received_pledge(Pledge.new(:gold, 90))
71
+ @project.received_pledge(Pledge.new(:gold, 90))
72
+ @project.received_pledge(Pledge.new(:gold, 90))
73
+ @project.received_pledge(Pledge.new(:gold, 90))
74
+ @project.received_pledge(Pledge.new(:gold, 90))
75
+
76
+ yielded = []
77
+ @project.each_received_pledge do |pledge|
78
+ yielded << pledge
79
+ end
80
+
81
+ yielded.should == [
82
+ Pledge.new(:bronze, 40),
83
+ Pledge.new(:silver, 65),
84
+ Pledge.new(:gold, 720)
85
+ ]
86
+ end
87
+
88
+ it "can be created from a CSV string" do
89
+ project = Project.from_csv("Project1,150")
90
+
91
+ project.name.should == "Project1"
92
+ project.initial_funding_amount.should == 150
93
+ end
94
+
95
+ context "created with a default value of 0 for funding amount" do
96
+ before do
97
+ @project = Project.new(@name, @initial_funding_amount=0)
98
+ end
99
+
100
+ it "has a default value of 0 for funding amount" do
101
+ @project.initial_funding_amount.should == 0
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,8 @@
1
+ RSpec.configure do |config|
2
+ config.expect_with :rspec do |c|
3
+ c.syntax = [:should, :expect]
4
+ end
5
+ config.mock_with :rspec do |c|
6
+ c.syntax = [:should, :expect]
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: crowd_fund_dubesoftware
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - "@dubesoftware"
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-10-31 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.8'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 2.8.0
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '2.8'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 2.8.0
33
+ description: |-
34
+ A console-based funding program written in Ruby.
35
+
36
+ This program is the outcome of bonus exercise work for the excellent Ruby course by Mike and Nicole Clark of The Pragmatic Studio.
37
+ email: zebra05+crowdfund@gmail.com
38
+ executables:
39
+ - crowd_fund
40
+ extensions: []
41
+ extra_rdoc_files: []
42
+ files:
43
+ - LICENSE
44
+ - README
45
+ - bin/crowd_fund
46
+ - bin/projects.csv
47
+ - lib/crowd_fund/die.rb
48
+ - lib/crowd_fund/fundable.rb
49
+ - lib/crowd_fund/funding_round.rb
50
+ - lib/crowd_fund/grant_project.rb
51
+ - lib/crowd_fund/matched_funds_project.rb
52
+ - lib/crowd_fund/pledge_pool.rb
53
+ - lib/crowd_fund/project.rb
54
+ - lib/crowd_fund/project_manager.rb
55
+ - spec/crowd_fund/grant_project_spec.rb
56
+ - spec/crowd_fund/matched_funds_project_spec.rb
57
+ - spec/crowd_fund/pledge_pool_spec.rb
58
+ - spec/crowd_fund/project_spec.rb
59
+ - spec/crowd_fund/spec_helper.rb
60
+ homepage: https://github.com/dubesoftware/ruby-programming/tree/main/crowd_fund
61
+ licenses:
62
+ - MIT
63
+ metadata: {}
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '1.9'
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubygems_version: 3.3.24
80
+ signing_key:
81
+ specification_version: 4
82
+ summary: A console-based game written in Ruby.
83
+ test_files:
84
+ - spec/crowd_fund/grant_project_spec.rb
85
+ - spec/crowd_fund/matched_funds_project_spec.rb
86
+ - spec/crowd_fund/pledge_pool_spec.rb
87
+ - spec/crowd_fund/project_spec.rb
88
+ - spec/crowd_fund/spec_helper.rb