From cc5ac2c8443926c982cf1af949f9f21f91a74cdb Mon Sep 17 00:00:00 2001 From: Jonathan Martindell Date: Tue, 27 Jan 2015 11:46:30 -0500 Subject: [PATCH 1/4] Create initial scenario comparer --- JonsGame/.retirement_account_scenario.rb.swp | Bin 0 -> 12288 bytes JonsGame/controller.rb | 38 ++++++ JonsGame/retirement_account_scenario.rb | 63 ++++++++++ .../spec/retirement_account_scenario_test.rb | 86 ++++++++++++++ JonsGame/view.rb | 109 ++++++++++++++++++ 5 files changed, 296 insertions(+) create mode 100644 JonsGame/.retirement_account_scenario.rb.swp create mode 100644 JonsGame/controller.rb create mode 100644 JonsGame/retirement_account_scenario.rb create mode 100644 JonsGame/spec/retirement_account_scenario_test.rb create mode 100644 JonsGame/view.rb diff --git a/JonsGame/.retirement_account_scenario.rb.swp b/JonsGame/.retirement_account_scenario.rb.swp new file mode 100644 index 0000000000000000000000000000000000000000..0d6a7397e5832e50e36d86d04e82196e3a44c740 GIT binary patch literal 12288 zcmeI2yN?@19LMJh!Tm4mNrPFqUTvqr9nv4(13UeiGP6a_^}UreH2un+32ISJ-_|V{J!&>S)DSi z>fuh4K4)DZxE>|s!#m&XzdyW8zK)j2>pqjp6>aW$x^>~T)odI#U)p>5>VEaY^OhGl zEIq?}80b2QPp#;QI#%`3&3!9{~s400&?nTmd`a9QfmYLcRhYgCo!a z4e%m(3Oot^LSDav-@v!vQ*Z}-0zLpf=z3gL>b&AJ>RZQh|n&SYr2LLYvM{K#X<4MbX{&6(=y2OyADoK;9# z)6Y9}g%OGH2GcBm_vt%iM0*VbrV2C6q|I7Z(! zq>*_Rrx%a7vSC0Z#YklodzSW;>enlk!C+wdf#5@@=lXri3H*va@920utaMy42;>ny z_$?N?;-=M8J|5ST(_$VI4!7AzdvZ)sd)1fjO^h||b7D_X=G2d?V1B^x9OCo`0! zTe2h$J&r4eC+5v)jPhA%%1kgUD&`Iya~{`QGL~cnI_KnfR3)6sBq^C2a=IzI(dfkH zwv>USk${gOJ>0yy)3A4TH|_mbu5a&Ic-o8F{8Di`k>i=ol_)f$P|>)Ucws7F$~q8FnH6`Etk?Ba^JK+iQ^<3$ zup1wOo#`OnGAMR{xR2Bv>5XB1W@j0>voW=uF1WCUWjC}sTsggBeHvZHTH{^pLvlP_ z8>zmgOEe>xmC1=N(`w05f!!GoE3E@yHU_pr?(+jxXNXodyJ)qj;Y>iXLcBj>BjiYkQD8! zI*utN@iJg-tWZ>Qe7<~IZL=YnBFig@dM09#Olk?`RKaYgXt39dWr#{sxznBOCw# literal 0 HcmV?d00001 diff --git a/JonsGame/controller.rb b/JonsGame/controller.rb new file mode 100644 index 0000000..1f15485 --- /dev/null +++ b/JonsGame/controller.rb @@ -0,0 +1,38 @@ +require_relative 'view' +require_relative 'retirement_account_scenario' + +class GameController + include GameView + + def run! + sample_values = {name: "Sample Scenario", + beginning_age: 23} + scenario1 = RetirementAccountScenario.new sample_values + scenario2 = RetirementAccountScenario.new sample_values + + Print::run_spinner + Print::title_screen + + #Make todoList methods like RESTful endpoints (new/edit/update/delete) + #Think Backbone Model & View + + loop do + Print::menu + case Print::fetch_user_input + when "V" + Print::print_retirement_comparison(scenario1, scenario2) + when "S1" + scenario1 = RetirementAccountScenario.new Print::serialize_scenario + when "S2" + scenario2 = RetirementAccountScenario.new Print::serialize_scenario + when "Q" + puts "We're done" + exit + else + Print::error_message + end + end + end +end + +GameController.new.run! diff --git a/JonsGame/retirement_account_scenario.rb b/JonsGame/retirement_account_scenario.rb new file mode 100644 index 0000000..061d915 --- /dev/null +++ b/JonsGame/retirement_account_scenario.rb @@ -0,0 +1,63 @@ +require 'faker' +require 'pry' + +class RetirementAccountScenario + attr_reader :name, :beginning_age, :retirement_age, :death_age + attr_reader :ira_type, :annual_contribution + attr_reader :accumulation_market_return_rate, :accumulation_tax_rate + attr_reader :retirement_tax_rate + + VALID_IRA_TYPES = [ :roth, :regular] + + def initialize args + @name = args.fetch(:name, "UNNAMED") + @beginning_age = args.fetch(:beginning_age, 25).to_f + @retirement_age = args.fetch(:retirement_age, 65).to_f + @death_age = args.fetch(:death_age, 100).to_f + @annual_contribution = args.fetch(:annual_contribution, 500).to_f + @accumulation_market_return_rate = args.fetch(:accumulation_market_return_rate, 0.01).to_f + @accumulation_tax_rate = args.fetch(:accumulation_tax_rate, 0).to_f + @retirement_tax_rate = args.fetch(:retirement_tax_rate, 0).to_f + @ira_type = args.fetch(:ira_type, :regular) + + raise ArguementError "Retirement must be after beginning age" unless retirement_age > beginning_age + raise ArguementError "Death must be after retirement" unless death_age > retirement_age + raise ArguementError "Must earn interest" unless accumulation_market_return_rate > 0 + raise ArguementError "Must contribute" unless annual_contribution > 0 + raise ArguementError unless VALID_IRA_TYPES.include?(ira_type) + end + + def retirement_income + yearly_withdraw = (balance_at_retirement / years_in_retirement).round + if ira_type == :roth + yearly_withdraw + else + (yearly_withdraw * (1.0 - retirement_tax_rate)).round + end + end + + private + + def balance_at_retirement + # http://www.moneychimp.com/articles/finworks/fmbasinv.htm + z = 1 + accumulation_market_return_rate + if ira_type == :roth + c = annual_contribution * (1.0 - accumulation_tax_rate) + else + c = annual_contribution + end + y = years_in_accumulation + + c * ( ( z**(y + 1) - z ) / ( z - 1 )) + end + + def years_in_retirement + death_age - retirement_age + end + + def years_in_accumulation + retirement_age - beginning_age + end + +end + diff --git a/JonsGame/spec/retirement_account_scenario_test.rb b/JonsGame/spec/retirement_account_scenario_test.rb new file mode 100644 index 0000000..ff31dae --- /dev/null +++ b/JonsGame/spec/retirement_account_scenario_test.rb @@ -0,0 +1,86 @@ +require_relative '../retirement_account_scenario' + +describe RetirementAccountScenario do + describe "#retirement_income" do + context "when given valid parameters" do + subject(:valid_scenario) { + setup_values = {} + setup_values[:name] = "Valid Account" + setup_values[:beginning_age] = 35 + setup_values[:retirement_age] = 65 + setup_values[:death_age] = 95 + setup_values[:annual_contribution] = 5000 + setup_values[:accumulation_market_return_rate] = 0.08 + setup_values[:ira_type] = :roth + RetirementAccountScenario.new setup_values + } + + it "correctly calculates income" do + expect(valid_scenario.retirement_income).to eq(20391) + end + end + + context "when roth is given accumulation tax rate" do + subject() { + setup_values = {} + setup_values[:name] = "Valid with Tax Rate" + setup_values[:beginning_age] = 35 + setup_values[:retirement_age] = 65 + setup_values[:death_age] = 95 + setup_values[:annual_contribution] = 5000 + setup_values[:accumulation_market_return_rate] = 0.08 + setup_values[:accumulation_tax_rate] = 0.28 + setup_values[:ira_type] = :roth + RetirementAccountScenario.new setup_values + } + it "reduces contributions by tax rate" do + expect(subject.retirement_income).to eq(14682) + end + end + + context "when roth is given retirement tax rate" do + subject() { + setup_values = {} + setup_values[:name] = "Valid with Tax Rate" + setup_values[:beginning_age] = 35 + setup_values[:retirement_age] = 65 + setup_values[:death_age] = 95 + setup_values[:annual_contribution] = 5000 + setup_values[:accumulation_market_return_rate] = 0.08 + setup_values[:accumulation_tax_rate] = 0.28 + setup_values[:retirement_tax_rate] = 0.20 + setup_values[:ira_type] = :roth + RetirementAccountScenario.new setup_values + } + it "does not reduce retirement income" do + expect(subject.retirement_income).to eq(14682) + end + end + + context "when a regular retirement account" do + subject() { + setup_values = {} + setup_values[:name] = "Regular retirement account" + setup_values[:beginning_age] = 35 + setup_values[:retirement_age] = 65 + setup_values[:death_age] = 95 + setup_values[:annual_contribution] = 5000 + setup_values[:accumulation_market_return_rate] = 0.08 + setup_values[:accumulation_tax_rate] = 0.28 + setup_values[:retirement_tax_rate] = 0.20 + setup_values[:ira_type] = :regular + RetirementAccountScenario.new setup_values + } + it "gets taxed in retirement" do + expect(subject.retirement_income).to eq (16313) + end + + end + end + + describe "#initialize" do + context "when a required parameter is missing" do + it { expect { RetirementAccountScenario.new nothing: "yes" }.to_not raise_error() } + end + end +end diff --git a/JonsGame/view.rb b/JonsGame/view.rb new file mode 100644 index 0000000..854e899 --- /dev/null +++ b/JonsGame/view.rb @@ -0,0 +1,109 @@ +module GameView + + module Print + + class << self + def run_spinner + print "Loading (please wait) " + #5.times { print "."; sleep 1; } + 5.times { print "."; } + print "\n" + end + + def error_message + puts "That's not a command key. Try again!" + end + + def title_screen +title = < " + gets.chomp + end + end + end +end From fd7bfae7fcd4dd2dae5f885b66dbceb086bbafaa Mon Sep 17 00:00:00 2001 From: Jonathan Martindell <jmartindell@covermymeds.com> Date: Tue, 27 Jan 2015 13:04:21 -0500 Subject: [PATCH 2/4] Break out print into more modules --- JonsGame/view.rb | 105 +++++++++++++++++++++++++++++------------------ 1 file changed, 64 insertions(+), 41 deletions(-) diff --git a/JonsGame/view.rb b/JonsGame/view.rb index 854e899..b858c13 100644 --- a/JonsGame/view.rb +++ b/JonsGame/view.rb @@ -40,49 +40,72 @@ def menu end def print_retirement_comparison(scenario1, scenario2) - output = [] - output[0] = "********************************" - output[1] = "" - output[2] = "********************************" - output[3] = sprintf("%-21s", "Beginning Age:") - output[4] = sprintf("%-21s", "Retirement Age:") - output[5] = sprintf("%-21s", "Age of Death:") - output[6] = sprintf("%-21s", "Annual Contribution:") - output[7] = sprintf("%-21s", "Market Return:") - output[8] = sprintf("%-21s", "Accum. Tax Rate:") - output[9] = sprintf("%-21s", "Retir. Tax Rate:") - output[10] = sprintf("%-21s", "Account Type:") - output[11] = "********************************" - output[12] = sprintf("%-21s", "Retirement Income:") - output[13] = "********************************" - - output[1] << sprintf("%-32s", scenario1.name) - output[3] << sprintf("%-12d", scenario1.beginning_age) - output[4] << sprintf("%-12d", scenario1.retirement_age) - output[5] << sprintf("%-12d", scenario1.death_age) - output[6] << sprintf("$%-11d", scenario1.annual_contribution) - output[7] << sprintf("%-12.2f", scenario1.accumulation_market_return_rate) - output[8] << sprintf("%-12.2f", scenario1.accumulation_tax_rate) - output[9] << sprintf("%-12.2f", scenario1.retirement_tax_rate) - output[10] << sprintf("%-12s", scenario1.ira_type.to_s) - output[12] << sprintf("$%-11d", scenario1.retirement_income) - - output[0] << " ********************************" - output[1] << " %s" % scenario2.name - output[2] << " ********************************" - output[3] << sprintf("%-12d", scenario2.beginning_age) - output[4] << sprintf("%-12d", scenario2.retirement_age) - output[5] << sprintf("%-12d", scenario2.death_age) - output[6] << sprintf("$%-11d", scenario2.annual_contribution) - output[7] << sprintf("%-12.2f", scenario2.accumulation_market_return_rate) - output[8] << sprintf("%-12.2f", scenario2.accumulation_tax_rate) - output[9] << sprintf("%-12.2f", scenario2.retirement_tax_rate) - output[10] << sprintf("%-12s", scenario2.ira_type.to_s) - output[11] << " ********************************" - output[12] << sprintf("$%-11d", scenario2.retirement_income) - output[13] << " ********************************" + output = Array.new(14, "") + add_frame output + add_row_header output + add_scenario_output(output, scenario1) + add_frame output + add_scenario_output(output, scenario2) puts output + puts results_output(scenario1, scenario2) + end + + def add_scenario_output(output, scenario) + output[1] += sprintf("%-32s", scenario.name) + output[3] += sprintf("%-12d", scenario.beginning_age) + output[4] += sprintf("%-12d", scenario.retirement_age) + output[5] += sprintf("%-12d", scenario.death_age) + output[6] += sprintf("$%-11d", scenario.annual_contribution) + output[7] += sprintf("%-12.2f", scenario.accumulation_market_return_rate) + output[8] += sprintf("%-12.2f", scenario.accumulation_tax_rate) + output[9] += sprintf("%-12.2f", scenario.retirement_tax_rate) + output[10] += sprintf("%-12s", scenario.ira_type.to_s) + output[12] += sprintf("$%-11d", scenario.retirement_income) + end + + def add_frame(output) + output[0] += "********************************" + output[1] += "" + output[2] += "********************************" + output[3] += "" + output[4] += "" + output[5] += "" + output[6] += "" + output[7] += "" + output[8] += "" + output[9] += "" + output[10] += "" + output[11] += "********************************" + output[12] += "" + output[13] += "********************************" + end + + def add_row_header(output) + output[0] += "" + output[1] += "" + output[2] += "" + output[3] += sprintf("%-21s", "Beginning Age:") + output[4] += sprintf("%-21s", "Retirement Age:") + output[5] += sprintf("%-21s", "Age of Death:") + output[6] += sprintf("%-21s", "Annual Contribution:") + output[7] += sprintf("%-21s", "Market Return:") + output[8] += sprintf("%-21s", "Accum. Tax Rate:") + output[9] += sprintf("%-21s", "Retir. Tax Rate:") + output[10] += sprintf("%-21s", "Account Type:") + output[11] += "" + output[12] += sprintf("%-21s", "Retirement Income:") + output[13] += "" + end + + def results_output(scenario1, scenario2) + if scenario1.retirement_income == scenario2.retirement_income + "Result: both scenarios generate the same income in retirement." + elsif scenario1.retirement_income > scenario2.retirement_income + "Result: #{scenario1.name} generates $#{scenario1.retirement_income - scenario2.retirement_income} more income in retirement!" + else + "Result: #{scenario2.name} generates $#{scenario2.retirement_income - scenario1.retirement_income} more income in retirement!" + end end def serialize_scenario From 0a60cb90d8ef553ee2d922d86f7c23bf712a4983 Mon Sep 17 00:00:00 2001 From: Jonathan Martindell <jmartindell@covermymeds.com> Date: Tue, 27 Jan 2015 13:20:29 -0500 Subject: [PATCH 3/4] Fix alignment in output --- JonsGame/view.rb | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/JonsGame/view.rb b/JonsGame/view.rb index b858c13..63cd43a 100644 --- a/JonsGame/view.rb +++ b/JonsGame/view.rb @@ -53,21 +53,21 @@ def print_retirement_comparison(scenario1, scenario2) def add_scenario_output(output, scenario) output[1] += sprintf("%-32s", scenario.name) - output[3] += sprintf("%-12d", scenario.beginning_age) - output[4] += sprintf("%-12d", scenario.retirement_age) - output[5] += sprintf("%-12d", scenario.death_age) - output[6] += sprintf("$%-11d", scenario.annual_contribution) - output[7] += sprintf("%-12.2f", scenario.accumulation_market_return_rate) - output[8] += sprintf("%-12.2f", scenario.accumulation_tax_rate) - output[9] += sprintf("%-12.2f", scenario.retirement_tax_rate) - output[10] += sprintf("%-12s", scenario.ira_type.to_s) - output[12] += sprintf("$%-11d", scenario.retirement_income) + output[3] += sprintf("%-11d", scenario.beginning_age) + output[4] += sprintf("%-11d", scenario.retirement_age) + output[5] += sprintf("%-11d", scenario.death_age) + output[6] += sprintf("$%-10d", scenario.annual_contribution) + output[7] += sprintf("%-11.2f", scenario.accumulation_market_return_rate) + output[8] += sprintf("%-11.2f", scenario.accumulation_tax_rate) + output[9] += sprintf("%-11.2f", scenario.retirement_tax_rate) + output[10] += sprintf("%-11s", scenario.ira_type.to_s) + output[12] += sprintf("$%-10d", scenario.retirement_income) end def add_frame(output) - output[0] += "********************************" + output[0] += "******************************* " output[1] += "" - output[2] += "********************************" + output[2] += "******************************* " output[3] += "" output[4] += "" output[5] += "" @@ -76,9 +76,9 @@ def add_frame(output) output[8] += "" output[9] += "" output[10] += "" - output[11] += "********************************" + output[11] += "******************************* " output[12] += "" - output[13] += "********************************" + output[13] += "******************************* " end def add_row_header(output) @@ -99,13 +99,10 @@ def add_row_header(output) end def results_output(scenario1, scenario2) - if scenario1.retirement_income == scenario2.retirement_income - "Result: both scenarios generate the same income in retirement." - elsif scenario1.retirement_income > scenario2.retirement_income - "Result: #{scenario1.name} generates $#{scenario1.retirement_income - scenario2.retirement_income} more income in retirement!" - else - "Result: #{scenario2.name} generates $#{scenario2.retirement_income - scenario1.retirement_income} more income in retirement!" - end + difference = (scenario1.retirement_income - scenario2.retirement_income).abs + return "Result: both scenarios generate the same income in retirement." if difference == 0 + return "Result: #{scenario1.name} generates $#{difference} more income in retirement!" if scenario1.retirement_income > scenario2.retirement_income + return "Result: #{scenario2.name} generates $#{difference} more income in retirement!" if scenario2.retirement_income > scenario1.retirement_income end def serialize_scenario From 75f19d165a55600e9d68737c86288eb2402cffa6 Mon Sep 17 00:00:00 2001 From: Jonathan Martindell <jmartindell@covermymeds.com> Date: Tue, 27 Jan 2015 16:04:52 -0500 Subject: [PATCH 4/4] Clean up some code formatting --- JonsGame/.retirement_account_scenario.rb.swp | Bin 12288 -> 0 bytes JonsGame/controller.rb | 6 +----- JonsGame/retirement_account_scenario.rb | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 JonsGame/.retirement_account_scenario.rb.swp diff --git a/JonsGame/.retirement_account_scenario.rb.swp b/JonsGame/.retirement_account_scenario.rb.swp deleted file mode 100644 index 0d6a7397e5832e50e36d86d04e82196e3a44c740..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2yN?@19LMJ<c!dChiUKroB+fpZwS7r=Sls1K=Tmf19HM&(6rp9j9^1?9&YIcT z7+(P>h!Tm4mNrPFqUTvqr9nv4(13UeiGP6a_^}UreH2un+32ISJ-_|V{J!&>S)DSi z>fuh4K4)DZxE>|s!#m&XzdyW8zK)j2>pqjp6>aW$x^>~T)odI#U)p>5>VEaY^OhGl zEI<COlwjpLbLCdxMSd#^E6u~LF7tWiN+80^`h(=kmE7k-+01c*2=~z8f=M^9WNYC_ zOoMU2II!4()8zbWt*YD47S7PeA3InaYFdl~#sTAialklW954<T2aE&8f&ZNYs=q|u zMppMsGP^XnFU{O1U(L-pU>q<G7zd04#sTAialklW954<T2aE&8f&ZWbtWC(yy@as) zFnIj`|Mc(wKOZ6FNALsq61)R;!71?b!-V_<z5t(t+n^1e0C!Il@*X%3zI%v}TVNIZ zdWw)=z%B4JxceX>?}80b2QPp#;QI#%`3&3!9{~s400&?nTmd`a9QfmYLcRhYgCo!a z4e%m(3Oot^LSDav-@v!vQ*Z}-0zLpf=z<Pt0qw(Uz+A=w<A8C%IA9$3cMfoYS1hIa zMscN2wsko@M_at>3gL>b&AJ>RZQh|n&SYr2LLYvM{K#X<4MbX{&6(=y2OyADoK;9# z)6Y9}g%<Jecx9zHq|eY=2|~xTL@WA7e6G<_Nk2g8kghElk8Yi&MX7^0CPc(M+X;k{ zZYvs%dKBe_IyHSQ2-D-KSyl9$PT5xMO<OX>OGH2GcBm_vt%iM0*VbrV2C6q|I7Z(! zq>*_Rrx%a7vSC0Z#YklodzSW;>enlk!C+wdf#5@@=lXri3H*va@920utaMy42;>ny z_$?N?;-=M8J|5ST(_$VI4!7AzdvZ)sd)1fjO^h||b7D_X=G2d?V1B^x9OCo`0! zTe2h$J&r4eC+5v)jPhA%%1kgUD&`Iya~{`QGL~cnI_KnfR3)6sBq^C2a=IzI(dfkH zwv>USk${gOJ>0yy)3A4TH|_mbu5a&Ic-o8F{8Di`k>i=ol_)f$P|>)Ucws7<Q)YCX zR!4T6po9q!)D?<L9;!qvwZ!cCn)wIehF*x&*8^>F$~q8FnH6`Etk?Ba^JK+iQ^<3$ zup1wOo#`OnGAMR{xR2Bv>5XB1W@j0>voW=uF1WCUWjC}sTsggBeHvZHTH{^pLvlP_ z8>zmgOEe>xmC1=N(`w05f!!GoE3<HO6tjA{(I+Y9OzR1X^U=_@s<q63=Xbgj49F?i zGQC*MSdf%RMkxN09dI07rnRh2%4}T*b5~X;8(pT)UQA~h7ux7mGbg2Y{8Y7MqRVtO zBPDRGdtM;<B`c+)%d~KC?b>E@yHU_pr?(+jxXNXodyJ)qj;Y>iXLcBj>BjiYkQD8! zI*utN@iJg-tWZ>Qe7<~IZL=YnBFig@dM09#Olk?`RKaYg<GkxLPNs*8IYnae^b^Np l*k*U*d^X3&<@YA1<q47ejR-F(y4;t;<>Xt39dWr#{sxznBOCw# diff --git a/JonsGame/controller.rb b/JonsGame/controller.rb index 1f15485..2cf70be 100644 --- a/JonsGame/controller.rb +++ b/JonsGame/controller.rb @@ -5,17 +5,13 @@ class GameController include GameView def run! - sample_values = {name: "Sample Scenario", - beginning_age: 23} + sample_values = {name: "Sample Scenario", beginning_age: 23} scenario1 = RetirementAccountScenario.new sample_values scenario2 = RetirementAccountScenario.new sample_values Print::run_spinner Print::title_screen - #Make todoList methods like RESTful endpoints (new/edit/update/delete) - #Think Backbone Model & View - loop do Print::menu case Print::fetch_user_input diff --git a/JonsGame/retirement_account_scenario.rb b/JonsGame/retirement_account_scenario.rb index 061d915..484af5d 100644 --- a/JonsGame/retirement_account_scenario.rb +++ b/JonsGame/retirement_account_scenario.rb @@ -31,7 +31,7 @@ def retirement_income yearly_withdraw = (balance_at_retirement / years_in_retirement).round if ira_type == :roth yearly_withdraw - else + else #Must take out taxes (yearly_withdraw * (1.0 - retirement_tax_rate)).round end end