amortizer 0.0.1
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 +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +36 -0
- data/Rakefile +1 -0
- data/amortizer.gemspec +26 -0
- data/lib/amortizer.rb +6 -0
- data/lib/amortizer/loan.rb +53 -0
- data/lib/amortizer/version.rb +3 -0
- data/spec/amortizer_spec.rb +163 -0
- data/spec/spec_helper.rb +8 -0
- metadata +114 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 40ee0ac1619616e17dd30ee17321e671db480385
|
4
|
+
data.tar.gz: f891a114ab17c803d693581af5c035dc2130e388
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5b4c38fc4ae89262c94b208a404a88486097c777faa158e4d4692d95592980ab00374f5448664d57c47ee525e9079662bc52a96434f06780e2ae1523ae555410
|
7
|
+
data.tar.gz: 9656c03ea6096a98096d3f096b46a9e888671feba7aff3de151ec3901b04574f6f6dc20fc832d27e4b3d150409eaac4872e66bff05e6886bbaae005274b5317d
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Geordee Naliyath
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Amortizer
|
2
|
+
|
3
|
+
A simple gem to calculate amortizing loan payments, as part of training at Samyukti
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'amortizer'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install amortizer
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
|
22
|
+
require 'amortizer'
|
23
|
+
include Amortizer
|
24
|
+
|
25
|
+
loan = Loan.new(1000000, 10.5, 120)
|
26
|
+
loan.payment
|
27
|
+
|
28
|
+
Please see the spec for more examples.
|
29
|
+
|
30
|
+
## Contributing
|
31
|
+
|
32
|
+
1. Fork it ( http://github.com/samyukti/amortizer/fork )
|
33
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
34
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
35
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
36
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/amortizer.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'amortizer/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "amortizer"
|
8
|
+
spec.version = Amortizer::VERSION
|
9
|
+
spec.authors = ["Geordee Naliyath"]
|
10
|
+
spec.email = ["geordee@gmail.com"]
|
11
|
+
spec.summary = %q{A simple gem to calculate amortizing loan payments.}
|
12
|
+
spec.description = %q{A simple gem to calculate amortizing loan payments, as part of training at Samyukti}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "bigdecimal"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
end
|
data/lib/amortizer.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module Amortizer
|
2
|
+
|
3
|
+
require 'bigdecimal/util'
|
4
|
+
|
5
|
+
class Loan
|
6
|
+
attr_accessor :decimals
|
7
|
+
|
8
|
+
def initialize(principal, rate, term)
|
9
|
+
@principal = principal
|
10
|
+
@rate = rate
|
11
|
+
@term = term
|
12
|
+
|
13
|
+
@valid = convert_to_bigdecimals && validate_inputs if inputs_are_numbers
|
14
|
+
@rate /= (12 * 100) if @valid
|
15
|
+
end
|
16
|
+
|
17
|
+
def payment
|
18
|
+
calculate_payment if @valid
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def calculate_payment
|
24
|
+
if @rate == 0
|
25
|
+
payment = (@principal / @term)
|
26
|
+
else
|
27
|
+
payment = @principal * ((@rate * ((1 + @rate) ** @term)) /
|
28
|
+
(((1 + @rate) ** @term) - 1))
|
29
|
+
end
|
30
|
+
payment.nan? ? nil : payment.to_f.round(@decimals || 2)
|
31
|
+
end
|
32
|
+
|
33
|
+
def is_number?(input)
|
34
|
+
true if Float(input) rescue false
|
35
|
+
end
|
36
|
+
|
37
|
+
def inputs_are_numbers
|
38
|
+
is_number?(@principal) && is_number?(@rate) && is_number?(@term)
|
39
|
+
end
|
40
|
+
|
41
|
+
def convert_to_bigdecimals
|
42
|
+
@principal = @principal.to_d
|
43
|
+
@rate = @rate.to_d
|
44
|
+
@term = @term.to_d
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate_inputs
|
48
|
+
(@principal >= 0.0) && (@rate >= 0.0) && (@term >= 1.0)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
include Amortizer
|
3
|
+
|
4
|
+
describe Loan do
|
5
|
+
describe "#payment" do
|
6
|
+
# valid scenarios
|
7
|
+
it "returns 13493.5 for principal 1000000, rate 10.5, term 120" do
|
8
|
+
loan = Loan.new(1000000, 10.5, 120)
|
9
|
+
loan.payment.should == 13493.5
|
10
|
+
end
|
11
|
+
|
12
|
+
it "returns payment correctly, with rate as integer" do
|
13
|
+
loan = Loan.new(1000000, 10, 120)
|
14
|
+
loan.payment.should == 13215.07
|
15
|
+
end
|
16
|
+
|
17
|
+
it "returns payment correctly, if principal contains decimal" do
|
18
|
+
loan = Loan.new(1000.1, 10.1, 110)
|
19
|
+
loan.payment.should == 13.98
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns payment correctly, if term contains decimal" do
|
23
|
+
loan = Loan.new(1000.1, 10.1, 10.1)
|
24
|
+
loan.payment.should == 103.7
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns payment if principal, rate and term are strings of valid numbers" do
|
28
|
+
loan = Loan.new("1000000", "10.5", "120")
|
29
|
+
loan.payment.should == 13493.5
|
30
|
+
end
|
31
|
+
|
32
|
+
it "returns payment if the parameter is string of a valid number without integer part" do
|
33
|
+
loan = Loan.new("1000000", ".5", "120")
|
34
|
+
loan.payment.should == 8545.14
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns the same payment if the method is executed multiple times" do
|
38
|
+
loan = Loan.new(1000000, 10, 120)
|
39
|
+
loan.payment
|
40
|
+
loan.payment.should == 13215.07
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns the payment without decimals if 0 decimals are specified" do
|
44
|
+
loan = Loan.new(1000000, 10, 120)
|
45
|
+
loan.decimals = 0
|
46
|
+
loan.payment.should == 13215
|
47
|
+
end
|
48
|
+
|
49
|
+
it "returns the payment with 4 decimals if 4 decimals are specified" do
|
50
|
+
loan = Loan.new(1000000, 10, 120)
|
51
|
+
loan.decimals = 4
|
52
|
+
loan.payment.should == 13215.0737
|
53
|
+
end
|
54
|
+
|
55
|
+
# edge scenarios
|
56
|
+
it "returns 0 if the principal is 0" do
|
57
|
+
loan = Loan.new(0, 1, 1)
|
58
|
+
loan.payment.should == 0
|
59
|
+
end
|
60
|
+
|
61
|
+
it "returns principal in equal parts for term if the rate is 0" do
|
62
|
+
loan = Loan.new(1, 0, 2)
|
63
|
+
loan.payment.should == 0.5
|
64
|
+
end
|
65
|
+
|
66
|
+
it "returns payment if the term is equal to 1" do
|
67
|
+
loan = Loan.new(100, 1, 1)
|
68
|
+
loan.payment.should == 100.08
|
69
|
+
end
|
70
|
+
|
71
|
+
it "returns nil if the term is less than 1" do
|
72
|
+
loan = Loan.new(100, 1, 0.99)
|
73
|
+
loan.payment.should be_nil
|
74
|
+
end
|
75
|
+
|
76
|
+
it "returns nil if the term is less than 1" do
|
77
|
+
loan = Loan.new(100, 1, "0.9999999999999999999".to_d)
|
78
|
+
loan.payment.should be_nil
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns payment if the principal is very low" do
|
82
|
+
loan = Loan.new(0.0000000001, 1.0, 1.0)
|
83
|
+
loan.payment.should == 0.0
|
84
|
+
end
|
85
|
+
|
86
|
+
it "returns payment if the principal is very low" do
|
87
|
+
loan = Loan.new(0.0000000001, 1.0, 1.0)
|
88
|
+
loan.decimals = 20
|
89
|
+
loan.payment.should == 1.0008333333e-10
|
90
|
+
end
|
91
|
+
|
92
|
+
it "returns payment if the rate is very low" do
|
93
|
+
loan = Loan.new(1.0, 0.0000000001, 1.0)
|
94
|
+
loan.decimals = 20
|
95
|
+
loan.payment.should == 1.0000000000000833
|
96
|
+
end
|
97
|
+
|
98
|
+
it "returns payment if the principal is very large" do
|
99
|
+
loan = Loan.new(99999999999999999999, 1.0, 1.0)
|
100
|
+
loan.payment.should > 0
|
101
|
+
end
|
102
|
+
|
103
|
+
it "returns payment if the rate is very large" do
|
104
|
+
loan = Loan.new(1.0, 99999999999999999999, 1.0)
|
105
|
+
loan.payment.should > 0
|
106
|
+
end
|
107
|
+
|
108
|
+
it "returns payment if the term is very large and rate is 0" do
|
109
|
+
loan = Loan.new(1.0, 0, 99999999999999999999)
|
110
|
+
loan.decimals = 20
|
111
|
+
loan.payment.should > 0
|
112
|
+
end
|
113
|
+
|
114
|
+
# invalid scenarios
|
115
|
+
it "returns nil if the principal is negative" do
|
116
|
+
loan = Loan.new(-0.1, 1.0, 1.0)
|
117
|
+
loan.payment.should be_nil
|
118
|
+
end
|
119
|
+
|
120
|
+
it "returns nil if the rate is negative" do
|
121
|
+
loan = Loan.new(1.0, -0.1, 1.0)
|
122
|
+
loan.payment.should be_nil
|
123
|
+
end
|
124
|
+
|
125
|
+
it "returns nil if the term is negative" do
|
126
|
+
loan = Loan.new(1.0, 1.0, -0.1)
|
127
|
+
loan.payment.should be_nil
|
128
|
+
end
|
129
|
+
|
130
|
+
it "returns nil if the principal is string which does not convert to a number" do
|
131
|
+
loan = Loan.new("x", "1", "1")
|
132
|
+
loan.payment.should be_nil
|
133
|
+
end
|
134
|
+
|
135
|
+
it "returns nil if the rate is string which does not convert to a number" do
|
136
|
+
loan = Loan.new("1", "x", "1")
|
137
|
+
loan.payment.should be_nil
|
138
|
+
end
|
139
|
+
|
140
|
+
it "returns nil if the term is string which does not convert to a number" do
|
141
|
+
loan = Loan.new("1", "5", "x")
|
142
|
+
loan.payment.should be_nil
|
143
|
+
end
|
144
|
+
|
145
|
+
it "returns nil if the parameter is string of a valid number without decimal part" do
|
146
|
+
loan = Loan.new("1", "5.", "1")
|
147
|
+
loan.payment.should be_nil
|
148
|
+
end
|
149
|
+
|
150
|
+
it "returns nil if the term is very large and rate is 1" do
|
151
|
+
loan = Loan.new(1.0, 1, 99999999999999999999)
|
152
|
+
loan.decimals = 20
|
153
|
+
loan.payment.should == nil
|
154
|
+
end
|
155
|
+
|
156
|
+
it "returns nil if the term is very large and rate is very small" do
|
157
|
+
loan = Loan.new(1.0, 0.000000000000000001, 99999999999999999999)
|
158
|
+
loan.decimals = 20
|
159
|
+
loan.payment.should == nil
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: amortizer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Geordee Naliyath
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bigdecimal
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: A simple gem to calculate amortizing loan payments, as part of training
|
70
|
+
at Samyukti
|
71
|
+
email:
|
72
|
+
- geordee@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- .gitignore
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- amortizer.gemspec
|
83
|
+
- lib/amortizer.rb
|
84
|
+
- lib/amortizer/loan.rb
|
85
|
+
- lib/amortizer/version.rb
|
86
|
+
- spec/amortizer_spec.rb
|
87
|
+
- spec/spec_helper.rb
|
88
|
+
homepage: ''
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.0.6
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: A simple gem to calculate amortizing loan payments.
|
112
|
+
test_files:
|
113
|
+
- spec/amortizer_spec.rb
|
114
|
+
- spec/spec_helper.rb
|