roth_ira 1.2.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
+ SHA1:
3
+ metadata.gz: 5a7bbd77d6a3283c63e435ca850c4d6935621f37
4
+ data.tar.gz: 01be66098e85fb161399ca7b43b56f68ee61b04d
5
+ SHA512:
6
+ metadata.gz: 5637387863f8e81764a364750842fa8685ad9c014252c64ce2c4e2620b9239ca620ec832fe32e8271c8158864238afea10881908a045a6b9a4d516fcdf194d2b
7
+ data.tar.gz: c6450b1b50a392c65bca6039076b4ea759946acc7953d133ded5236dcde46601e9631f18066830b1771911549a2353a8bf05a99a6251c8f712af5c60948c2381
data/lib/limits.yaml ADDED
@@ -0,0 +1,55 @@
1
+ ---
2
+ 2015:
3
+ :single:
4
+ :lower_income_limit: 116000
5
+ :upper_income_limit: 131000
6
+ :contribution_limit: 5500
7
+ :minimum_phase_out: 200
8
+ :head_of_household:
9
+ :lower_income_limit: 116000
10
+ :upper_income_limit: 131000
11
+ :contribution_limit: 5500
12
+ :minimum_phase_out: 200
13
+ :married_filing_jointly:
14
+ :lower_income_limit: 183000
15
+ :upper_income_limit: 193000
16
+ :contribution_limit: 11000
17
+ :minimum_phase_out: 400
18
+ :catch_up_contribution: 1000
19
+ :catch_up_age: 50
20
+ 2016:
21
+ :single:
22
+ :lower_income_limit: 117000
23
+ :upper_income_limit: 132000
24
+ :contribution_limit: 5500
25
+ :minimum_phase_out: 200
26
+ :head_of_household:
27
+ :lower_income_limit: 117000
28
+ :upper_income_limit: 132000
29
+ :contribution_limit: 5500
30
+ :minimum_phase_out: 200
31
+ :married_filing_jointly:
32
+ :lower_income_limit: 184000
33
+ :upper_income_limit: 194000
34
+ :contribution_limit: 11000
35
+ :minimum_phase_out: 400
36
+ :catch_up_contribution: 1000
37
+ :catch_up_age: 50
38
+ 2017:
39
+ :single:
40
+ :lower_income_limit: 118000
41
+ :upper_income_limit: 133000
42
+ :contribution_limit: 5500
43
+ :minimum_phase_out: 200
44
+ :head_of_household:
45
+ :lower_income_limit: 118000
46
+ :upper_income_limit: 133000
47
+ :contribution_limit: 5500
48
+ :minimum_phase_out: 200
49
+ :married_filing_jointly:
50
+ :lower_income_limit: 186000
51
+ :upper_income_limit: 196000
52
+ :contribution_limit: 11000
53
+ :minimum_phase_out: 400
54
+ :catch_up_contribution: 1000
55
+ :catch_up_age: 50
data/lib/roth_ira.rb ADDED
@@ -0,0 +1,92 @@
1
+ require 'yaml'
2
+
3
+ class RothIRA
4
+ attr_reader :age, :agi, :filing_status, :limits, :spouse_age, :year
5
+
6
+ def initialize(year)
7
+ @year = year
8
+ @limits = YAML.load(File.read(__dir__ + "/limits.yaml"))[year]
9
+ end
10
+
11
+ def calculate(agi, filing_status, *ages)
12
+ @agi = agi
13
+ @filing_status = filing_status
14
+ @age = ages.first
15
+ @spouse_age = ages.size == 2 ? ages.last : 0
16
+
17
+ calculate_limit
18
+ end
19
+
20
+ private
21
+
22
+ def calculate_limit
23
+ case agi
24
+ when 0..max_contribution then agi
25
+ when max_contribution..lower_limit then max_contribution
26
+ when lower_limit...upper_limit then calculate_phase_out
27
+ else 0
28
+ end
29
+ end
30
+
31
+ def lower_limit
32
+ limits[filing_status][:lower_income_limit]
33
+ end
34
+
35
+ def upper_limit
36
+ limits[filing_status][:upper_income_limit]
37
+ end
38
+
39
+ def max_contribution
40
+ contribution_limit + calculate_catch_up
41
+ end
42
+
43
+ def catch_up_age
44
+ limits[:catch_up_age]
45
+ end
46
+
47
+ def catch_up_contribution
48
+ limits[:catch_up_contribution]
49
+ end
50
+
51
+ def contribution_limit
52
+ limits[filing_status][:contribution_limit]
53
+ end
54
+
55
+ def minimum_phase_out
56
+ limits[filing_status][:minimum_phase_out]
57
+ end
58
+
59
+ def calculate_catch_up
60
+ eligible_for_catch_up = [primary_eligible_for_catch_up, spouse_eligible_for_catch_up]
61
+ eligible_for_catch_up.count(true) * catch_up_contribution
62
+ end
63
+
64
+ def primary_eligible_for_catch_up
65
+ age >= catch_up_age
66
+ end
67
+
68
+ def spouse_eligible_for_catch_up
69
+ filing_status == :married_filing_jointly && spouse_age >= catch_up_age
70
+ end
71
+
72
+ def calculate_phase_out
73
+ excess_income = (agi - lower_limit).to_f
74
+ ratio = 1 - (excess_income/limit_range).round(3)
75
+ phase_out_amount = nearest_10(max_contribution * ratio)
76
+ minimum_200 phase_out_amount
77
+ end
78
+
79
+ def limit_range
80
+ upper_limit - lower_limit
81
+ end
82
+
83
+ def nearest_10(amount)
84
+ # IRS instructions are to round contribution limit UP to the nearest $10
85
+ (amount.to_f / 10).ceil * 10
86
+ end
87
+
88
+ def minimum_200(amount)
89
+ # IRS instructions allow minimum contribution of $200 in phase-out range (per person)
90
+ amount < minimum_phase_out ? minimum_phase_out : amount
91
+ end
92
+ end
@@ -0,0 +1,3 @@
1
+ class RothIRA
2
+ VERSION = '1.2.0'
3
+ end
@@ -0,0 +1,314 @@
1
+ require 'spec_helper'
2
+
3
+ describe RothIRA do
4
+ it 'should return correct version string' do
5
+ expect(RothIRA::VERSION).to eq('1.2.0')
6
+ end
7
+
8
+ context 'Tax year 2015' do
9
+ let(:roth_ira) { RothIRA.new(2015) }
10
+ context 'Single' do
11
+ it 'should return correct value for low income single' do
12
+ expect(roth_ira.calculate(1500, :single, 25)).to eq(1500)
13
+ end
14
+
15
+ it 'should return correct value for moderate income single' do
16
+ expect(roth_ira.calculate(75000, :single, 25)).to eq(5500)
17
+ end
18
+
19
+ it 'should return correct value for high income single' do
20
+ expect(roth_ira.calculate(135000, :single, 25)).to eq(0)
21
+ end
22
+
23
+ it 'should return correct value for phase-out income single' do
24
+ expect(roth_ira.calculate(122800, :single, 25)).to eq(3010)
25
+ end
26
+
27
+ it 'should correctly handle minimum phase out amount of $200' do
28
+ expect(roth_ira.calculate(130900, :single, 25)).to eq(200)
29
+ end
30
+
31
+ context 'over 50' do
32
+ it 'should return correct value for low income single over 50' do
33
+ expect(roth_ira.calculate(1500, :single, 50)).to eq(1500)
34
+ end
35
+
36
+ it 'should return correct value for moderate income single over 50' do
37
+ expect(roth_ira.calculate(75000, :single, 50)).to eq(6500)
38
+ end
39
+
40
+ it 'should return correct value for phase-out income single over 50' do
41
+ expect(roth_ira.calculate(121600, :single, 50)).to eq(4080)
42
+ end
43
+ end
44
+ end
45
+
46
+ context 'Head of Household' do
47
+ it 'should return correct value for low income head of household' do
48
+ expect(roth_ira.calculate(1500, :head_of_household, 25)).to eq(1500)
49
+ end
50
+
51
+ it 'should return correct value for moderate income head of household' do
52
+ expect(roth_ira.calculate(75000, :head_of_household, 25)).to eq(5500)
53
+ end
54
+
55
+ it 'should return correct value for high income head of household' do
56
+ expect(roth_ira.calculate(135000, :head_of_household, 25)).to eq(0)
57
+ end
58
+
59
+ it 'should return correct value for phase-out income head of household' do
60
+ expect(roth_ira.calculate(122800, :head_of_household, 25)).to eq(3010)
61
+ end
62
+
63
+ it 'should correctly handle minimum phase out amount of $200' do
64
+ expect(roth_ira.calculate(130900, :head_of_household, 25)).to eq(200)
65
+ end
66
+
67
+ context 'over 50' do
68
+ it 'should return correct value for low income head of household over 50' do
69
+ expect(roth_ira.calculate(1500, :head_of_household, 50)).to eq(1500)
70
+ end
71
+
72
+ it 'should return correct value for moderate income head of household over 50' do
73
+ expect(roth_ira.calculate(75000, :head_of_household, 50)).to eq(6500)
74
+ end
75
+
76
+ it 'should return correct value for phase-out income head of household over 50' do
77
+ expect(roth_ira.calculate(121600, :head_of_household, 50)).to eq(4080)
78
+ end
79
+ end
80
+ end
81
+
82
+ context 'Married Filing Jointly' do
83
+ it 'should return correct value for low income married filing jointly' do
84
+ expect(roth_ira.calculate(9900, :married_filing_jointly, 25, 26)).to eq(9900)
85
+ end
86
+
87
+ it 'should return correct value for moderate income married filing jointly' do
88
+ expect(roth_ira.calculate(135000, :married_filing_jointly, 25, 26)).to eq(11000)
89
+ end
90
+
91
+ it 'should return correct value for high income married filing jointly' do
92
+ expect(roth_ira.calculate(195000, :married_filing_jointly, 25, 26)).to eq(0)
93
+ end
94
+
95
+ it 'should return correct value for phase-out income married filing jointly' do
96
+ expect(roth_ira.calculate(189500, :married_filing_jointly, 25, 26)).to eq(3850)
97
+ end
98
+
99
+ context 'over 50' do
100
+ it 'should return correct value for low income married filing jointly, with one over 50' do
101
+ expect(roth_ira.calculate(9900, :married_filing_jointly, 50, 49)).to eq(9900)
102
+ end
103
+
104
+ it 'should return correct value for moderate income married filing jointly, with one over 50' do
105
+ expect(roth_ira.calculate(135000, :married_filing_jointly, 50, 49)).to eq(12000)
106
+ end
107
+
108
+ it 'should return correct value for moderate income married filing jointly, with both over 50' do
109
+ expect(roth_ira.calculate(135000, :married_filing_jointly, 50, 51)).to eq(13000)
110
+ end
111
+
112
+ it 'should return correct value for phase-out income married filing jointly, with one over 50' do
113
+ expect(roth_ira.calculate(189500, :married_filing_jointly, 50, 49)).to eq(4200)
114
+ end
115
+
116
+ it 'should return correct value for phase-out income married filing jointly, with both over 50' do
117
+ expect(roth_ira.calculate(189500, :married_filing_jointly, 50, 65)).to eq(4550)
118
+ end
119
+
120
+ it 'should return correct value for high income married filing jointly, with both over 50' do
121
+ expect(roth_ira.calculate(195000, :married_filing_jointly, 55, 62)).to eq(0)
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ context 'Tax year 2016' do
128
+ let(:roth_ira) { RothIRA.new(2016) }
129
+ context 'Single' do
130
+ it 'should return correct value for low income single' do
131
+ expect(roth_ira.calculate(1500, :single, 25)).to eq(1500)
132
+ end
133
+
134
+ it 'should return correct value for moderate income single' do
135
+ expect(roth_ira.calculate(75000, :single, 25)).to eq(5500)
136
+ end
137
+
138
+ it 'should return correct value for high income single' do
139
+ expect(roth_ira.calculate(135000, :single, 25)).to eq(0)
140
+ end
141
+
142
+ it 'should return correct value for phase-out income single' do
143
+ expect(roth_ira.calculate(122800, :single, 25)).to eq(3380)
144
+ end
145
+
146
+ it 'should correctly handle minimum phase out amount of $200' do
147
+ expect(roth_ira.calculate(131900, :single, 25)).to eq(200)
148
+ end
149
+
150
+ context 'over 50' do
151
+ it 'should return correct value for low income single over 50' do
152
+ expect(roth_ira.calculate(1500, :single, 50)).to eq(1500)
153
+ end
154
+
155
+ it 'should return correct value for moderate income single over 50' do
156
+ expect(roth_ira.calculate(75000, :single, 50)).to eq(6500)
157
+ end
158
+
159
+ it 'should return correct value for phase-out income single over 50' do
160
+ expect(roth_ira.calculate(121600, :single, 50)).to eq(4510)
161
+ end
162
+ end
163
+ end
164
+
165
+ context 'Head of Household' do
166
+ it 'should return correct value for low income head of household' do
167
+ expect(roth_ira.calculate(1500, :head_of_household, 25)).to eq(1500)
168
+ end
169
+
170
+ it 'should return correct value for moderate income head of household' do
171
+ expect(roth_ira.calculate(75000, :head_of_household, 25)).to eq(5500)
172
+ end
173
+
174
+ it 'should return correct value for high income head of household' do
175
+ expect(roth_ira.calculate(135000, :head_of_household, 25)).to eq(0)
176
+ end
177
+
178
+ it 'should return correct value for phase-out income head of household' do
179
+ expect(roth_ira.calculate(122800, :head_of_household, 25)).to eq(3380)
180
+ end
181
+
182
+ it 'should correctly handle minimum phase out amount of $200' do
183
+ expect(roth_ira.calculate(131900, :head_of_household, 25)).to eq(200)
184
+ end
185
+
186
+ context 'over 50' do
187
+ it 'should return correct value for low income head of household over 50' do
188
+ expect(roth_ira.calculate(1500, :head_of_household, 50)).to eq(1500)
189
+ end
190
+
191
+ it 'should return correct value for moderate income head of household over 50' do
192
+ expect(roth_ira.calculate(75000, :head_of_household, 50)).to eq(6500)
193
+ end
194
+
195
+ it 'should return correct value for phase-out income head of household over 50' do
196
+ expect(roth_ira.calculate(121600, :head_of_household, 50)).to eq(4510)
197
+ end
198
+ end
199
+ end
200
+
201
+ context 'Married Filing Jointly' do
202
+ it 'should return correct value for low income married filing jointly' do
203
+ expect(roth_ira.calculate(9900, :married_filing_jointly, 25, 26)).to eq(9900)
204
+ end
205
+
206
+ it 'should return correct value for moderate income married filing jointly' do
207
+ expect(roth_ira.calculate(135000, :married_filing_jointly, 25, 26)).to eq(11000)
208
+ end
209
+
210
+ it 'should return correct value for high income married filing jointly' do
211
+ expect(roth_ira.calculate(195000, :married_filing_jointly, 25, 26)).to eq(0)
212
+ end
213
+
214
+ it 'should return correct value for phase-out income married filing jointly' do
215
+ expect(roth_ira.calculate(189500, :married_filing_jointly, 25, 26)).to eq(4950)
216
+ end
217
+
218
+ context 'over 50' do
219
+ it 'should return correct value for low income married filing jointly, with one over 50' do
220
+ expect(roth_ira.calculate(9900, :married_filing_jointly, 50, 49)).to eq(9900)
221
+ end
222
+
223
+ it 'should return correct value for moderate income married filing jointly, with one over 50' do
224
+ expect(roth_ira.calculate(135000, :married_filing_jointly, 50, 49)).to eq(12000)
225
+ end
226
+
227
+ it 'should return correct value for moderate income married filing jointly, with both over 50' do
228
+ expect(roth_ira.calculate(135000, :married_filing_jointly, 50, 51)).to eq(13000)
229
+ end
230
+
231
+ it 'should return correct value for phase-out income married filing jointly, with one over 50' do
232
+ expect(roth_ira.calculate(189500, :married_filing_jointly, 50, 49)).to eq(5400)
233
+ end
234
+
235
+ it 'should return correct value for phase-out income married filing jointly, with both over 50' do
236
+ expect(roth_ira.calculate(189500, :married_filing_jointly, 50, 65)).to eq(5850)
237
+ end
238
+
239
+ it 'should return correct value for high income married filing jointly, with both over 50' do
240
+ expect(roth_ira.calculate(195000, :married_filing_jointly, 55, 62)).to eq(0)
241
+ end
242
+
243
+ it 'should correctly handle minimum phase out amount of $200' do
244
+ expect(roth_ira.calculate(193900, :married_filing_jointly, 55, 62)).to eq(400)
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ context 'Tax year 2017' do
251
+ let(:roth_ira) { RothIRA.new(2017) }
252
+ context 'Single' do
253
+ it 'returns maximum contribution for bottom of income limit' do
254
+ expect(roth_ira.calculate(118000, :single, 40)).to eq(5500)
255
+ end
256
+
257
+ it 'returns maximum contribution for bottom of income limit with catch-up' do
258
+ expect(roth_ira.calculate(118000, :single, 50)).to eq(6500)
259
+ end
260
+
261
+ it 'returns zero contribution for top of income limit' do
262
+ expect(roth_ira.calculate(133000, :single, 40)).to eq(0)
263
+ end
264
+
265
+ it 'returns maximum contribution for bottom of income limit with catch-up' do
266
+ expect(roth_ira.calculate(133000, :single, 50)).to eq(0)
267
+ end
268
+ end
269
+
270
+ context 'Head of Household' do
271
+ it 'returns maximum contribution for bottom of income limit' do
272
+ expect(roth_ira.calculate(118000, :head_of_household, 40)).to eq(5500)
273
+ end
274
+
275
+ it 'returns maximum contribution for bottom of income limit with catch-up' do
276
+ expect(roth_ira.calculate(118000, :head_of_household, 50)).to eq(6500)
277
+ end
278
+
279
+ it 'returns zero contribution for top of income limit' do
280
+ expect(roth_ira.calculate(133000, :head_of_household, 40)).to eq(0)
281
+ end
282
+
283
+ it 'returns maximum contribution for bottom of income limit with catch-up' do
284
+ expect(roth_ira.calculate(133000, :head_of_household, 50)).to eq(0)
285
+ end
286
+ end
287
+
288
+ context 'Married Filing Jointly' do
289
+ it 'returns maximum contribution for bottom of income limit' do
290
+ expect(roth_ira.calculate(186000, :married_filing_jointly, 40, 40)).to eq(11000)
291
+ end
292
+
293
+ it 'returns maximum contribution for bottom of income limit with one catch-up' do
294
+ expect(roth_ira.calculate(186000, :married_filing_jointly, 40, 50)).to eq(12000)
295
+ end
296
+
297
+ it 'returns maximum contribution for bottom of income limit with both catch-up' do
298
+ expect(roth_ira.calculate(186000, :married_filing_jointly, 50, 50)).to eq(13000)
299
+ end
300
+
301
+ it 'returns zero contribution for top of income limit' do
302
+ expect(roth_ira.calculate(196000, :married_filing_jointly, 40, 40)).to eq(0)
303
+ end
304
+
305
+ it 'returns maximum contribution for bottom of income limit with one catch-up' do
306
+ expect(roth_ira.calculate(196000, :married_filing_jointly, 40, 50)).to eq(0)
307
+ end
308
+
309
+ it 'returns maximum contribution for bottom of income limit with both catch-up' do
310
+ expect(roth_ira.calculate(196000, :married_filing_jointly, 50, 50)).to eq(0)
311
+ end
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,10 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'byebug'
5
+ require 'roth_ira'
6
+
7
+ RSpec.configure do |config|
8
+ config.filter_run :focus
9
+ config.run_all_when_everything_filtered = true
10
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: roth_ira
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Randall Reed
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-13 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.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.3'
27
+ description: Determines max Roth IRA contribution amount based on year, filing status,
28
+ and MAGI
29
+ email:
30
+ - randallreedjr@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - lib/limits.yaml
36
+ - lib/roth_ira.rb
37
+ - lib/roth_ira/version.rb
38
+ - spec/roth_ira_spec.rb
39
+ - spec/spec_helper.rb
40
+ homepage: http://github.com/randallreedjr/roth_ira
41
+ licenses:
42
+ - MIT
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 2.4.8
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: Roth IRA Contribution Limit Calculator
64
+ test_files:
65
+ - spec/roth_ira_spec.rb
66
+ - spec/spec_helper.rb