percentage 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.
- data/README.md +84 -0
- data/Rakefile.rb +7 -0
- data/lib/percentage.rb +132 -0
- data/percentage.gemspec +12 -0
- data/spec/percentage_spec.rb +352 -0
- metadata +51 -0
data/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
Percentage
|
|
2
|
+
==========
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
A little library for working with percentages.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Feature Tour
|
|
9
|
+
------------
|
|
10
|
+
|
|
11
|
+
The `Percentage` method converts Integer/BigDecimal/Rational objects
|
|
12
|
+
to percentage objects with values that you would expect:
|
|
13
|
+
|
|
14
|
+
```ruby
|
|
15
|
+
Percentage(50) # => 50%
|
|
16
|
+
|
|
17
|
+
Percentage(BigDecimal('17.5')) # => 17.5%
|
|
18
|
+
|
|
19
|
+
Percentage(Rational(25, 2)) # => 12.5%
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Percentage objects can also be constructed directly, but in this case
|
|
23
|
+
BigDecimal/Rational values are treated as fractions, for example:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
Percentage.new(50) # => 50%
|
|
27
|
+
|
|
28
|
+
Percentage.new(BigDecimal('0.175')) # => 17.5%
|
|
29
|
+
|
|
30
|
+
Percentage.new(Rational(1, 8)) # => 12.5%
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
As with other numerics, percentage objects are conceptually immutable.
|
|
34
|
+
Common numeric functionality like `to_i`, `to_f`, `to_s`, `to_r`, `zero?`,
|
|
35
|
+
and equality/comparison methods are defined.
|
|
36
|
+
|
|
37
|
+
Percentages can be added together:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
Percentage(10) + Percentage(20) # => 30%
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
They can also be "scaled up" using the `scale` method:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
Percentage(10).scale(5) # => 50%
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Multiplication is then defined using the fractional value of the percentage.
|
|
50
|
+
BigDecimal objects can't be coerced into rational objects, so the order of
|
|
51
|
+
the multiplication will matter in some cases. For example:
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
Percentage(50) * 10 # => (5/1)
|
|
55
|
+
|
|
56
|
+
Percentage(50) * Percentage(10) # => 5.0%
|
|
57
|
+
|
|
58
|
+
BigDecimal('150.00') * Percentage(50) # => BigDecimal('75.00')
|
|
59
|
+
|
|
60
|
+
Percentage(50) * BigDecimal('150.00') # raises TypeError
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
Bonus Extras
|
|
65
|
+
------------
|
|
66
|
+
|
|
67
|
+
Some shortcut methods are defined on Integer/BigDecimal for convenience:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
50.percent # => 50%
|
|
71
|
+
|
|
72
|
+
50.percent_of(BigDecimal(150)) # => BigDecimal('75.00')
|
|
73
|
+
|
|
74
|
+
10.percent_of(100) # => (10/1)
|
|
75
|
+
|
|
76
|
+
5.as_percentage_of(10) # => 50.0%
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
And there's also a class method for calculating the percentage
|
|
80
|
+
change between two values:
|
|
81
|
+
|
|
82
|
+
```ruby
|
|
83
|
+
Percentage.change(2, 3) # => 50.0%
|
|
84
|
+
```
|
data/Rakefile.rb
ADDED
data/lib/percentage.rb
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
require 'rational'
|
|
2
|
+
require 'bigdecimal'
|
|
3
|
+
|
|
4
|
+
class Percentage
|
|
5
|
+
attr_reader :value
|
|
6
|
+
|
|
7
|
+
include Comparable
|
|
8
|
+
|
|
9
|
+
def initialize(value)
|
|
10
|
+
@value = value
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_i
|
|
14
|
+
(fractional_value * 100).to_i
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def to_f
|
|
18
|
+
(fractional_value * 100).to_f
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def to_s
|
|
22
|
+
"#{string_value}%"
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def to_r
|
|
26
|
+
fractional_value.to_r
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def zero?
|
|
30
|
+
@value.zero?
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def <=>(object)
|
|
34
|
+
if self.class === object
|
|
35
|
+
fractional_value <=> object.fractional_value
|
|
36
|
+
elsif Numeric === object
|
|
37
|
+
fractional_value <=> object
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def eql?(object)
|
|
42
|
+
object.instance_of?(self.class) && @value.eql?(object.value)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def hash
|
|
46
|
+
@value.hash
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def +(object)
|
|
50
|
+
if self.class === object
|
|
51
|
+
if @value.integer? ^ object.value.integer?
|
|
52
|
+
self.class.new(fractional_value + object.fractional_value)
|
|
53
|
+
else
|
|
54
|
+
self.class.new(@value + object.value)
|
|
55
|
+
end
|
|
56
|
+
else
|
|
57
|
+
raise TypeError, "cannot add #{object.class} to #{self.class}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def *(object)
|
|
62
|
+
case object
|
|
63
|
+
when self.class
|
|
64
|
+
self.class.new(fractional_value.to_r * object.fractional_value)
|
|
65
|
+
else
|
|
66
|
+
fractional_value.coerce(object).inject(&:*)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def coerce(other)
|
|
71
|
+
case other
|
|
72
|
+
when Numeric
|
|
73
|
+
return fractional_value, other
|
|
74
|
+
else
|
|
75
|
+
raise TypeError, "#{self.class} can't be coerced into #{other.class}"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def truncate(n)
|
|
80
|
+
self.class.new(fractional_value.truncate(n))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def scale(n)
|
|
84
|
+
self.class.new(@value * n)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
protected
|
|
88
|
+
|
|
89
|
+
def fractional_value
|
|
90
|
+
@fractional_value ||= @value.integer? ? Rational(@value, 100) : @value
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
private
|
|
94
|
+
|
|
95
|
+
def string_value
|
|
96
|
+
if @value.integer?
|
|
97
|
+
@value.to_s
|
|
98
|
+
elsif BigDecimal === @value
|
|
99
|
+
(@value * 100).to_s('F')
|
|
100
|
+
else
|
|
101
|
+
BigDecimal(@value * 100, _precision=10).to_s('F')
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def Percentage(object)
|
|
107
|
+
Percentage.new(object.integer? ? object : object / 100)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def Percentage.change(a, b)
|
|
111
|
+
Percentage.new((b - a).to_r / a)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
class BigDecimal
|
|
115
|
+
def as_percentage_of(n)
|
|
116
|
+
Percentage.new(self / n)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
class Integer
|
|
121
|
+
def percent
|
|
122
|
+
Percentage.new(self)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def percent_of(n)
|
|
126
|
+
n * Percentage.new(self)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def as_percentage_of(n)
|
|
130
|
+
Percentage.new(Rational(self, n))
|
|
131
|
+
end
|
|
132
|
+
end
|
data/percentage.gemspec
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Gem::Specification.new do |s|
|
|
2
|
+
s.name = 'percentage'
|
|
3
|
+
s.version = '1.0.0'
|
|
4
|
+
s.platform = Gem::Platform::RUBY
|
|
5
|
+
s.authors = ['Tim Craft']
|
|
6
|
+
s.email = ['mail@timcraft.com']
|
|
7
|
+
s.homepage = 'http://github.com/timcraft/percentage'
|
|
8
|
+
s.description = 'A little library for working with percentages'
|
|
9
|
+
s.summary = 'See description'
|
|
10
|
+
s.files = Dir.glob('{lib,spec}/**/*') + %w(README.md Rakefile.rb percentage.gemspec)
|
|
11
|
+
s.require_path = 'lib'
|
|
12
|
+
end
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
require 'minitest/autorun'
|
|
2
|
+
|
|
3
|
+
require_relative '../lib/percentage'
|
|
4
|
+
|
|
5
|
+
describe 'Percentage object' do
|
|
6
|
+
it 'is comparable' do
|
|
7
|
+
percentage = Percentage.new(Rational(1, 8))
|
|
8
|
+
|
|
9
|
+
(Comparable === percentage).must_equal(true)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'is comparable to other objects of the same class' do
|
|
13
|
+
(Percentage.new(Rational(1, 8)) > Percentage.new(Rational(1, 10))).must_equal(true)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it 'is comparable to other numeric objects' do
|
|
17
|
+
(Percentage.new(Rational(1, 8)) > Rational(1, 10)).must_equal(true)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'can be used as a hash key' do
|
|
21
|
+
hash = Hash.new(0)
|
|
22
|
+
|
|
23
|
+
3.times { hash[Percentage.new(Rational(1, 10))] += 1 }
|
|
24
|
+
|
|
25
|
+
hash[Percentage.new(Rational(1, 10))].must_equal(3)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
describe 'Percentage object initialized with an integer value' do
|
|
30
|
+
before do
|
|
31
|
+
@percentage = Percentage.new(10)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
describe 'value method' do
|
|
35
|
+
it 'returns the value passed to the constructor' do
|
|
36
|
+
@percentage.value.must_equal(10)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
describe 'to_i method' do
|
|
41
|
+
it 'returns the integer value of the percentage' do
|
|
42
|
+
@percentage.to_i.must_equal(10)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
describe 'to_f method' do
|
|
47
|
+
it 'returns the float value of the percentage' do
|
|
48
|
+
@percentage.to_f.must_be_close_to(10.0)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
describe 'to_s method' do
|
|
53
|
+
it 'returns the integer value of the percentage suffixed with the percent symbol' do
|
|
54
|
+
@percentage.to_s.must_equal('10%')
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe 'to_r method' do
|
|
59
|
+
it 'returns the rational value of the percentage' do
|
|
60
|
+
@percentage.to_r.must_equal(Rational(1, 10))
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
describe 'zero query method' do
|
|
65
|
+
it 'returns true if the percentage has a zero value' do
|
|
66
|
+
Percentage.new(0).zero?.must_equal(true)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
it 'returns false otherwise' do
|
|
70
|
+
@percentage.zero?.must_equal(false)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
describe 'truncate method' do
|
|
75
|
+
it 'returns a percentage object with a truncated rational value' do
|
|
76
|
+
percentage = @percentage.truncate(1)
|
|
77
|
+
percentage.must_be_instance_of(Percentage)
|
|
78
|
+
percentage.value.must_equal(Rational(1, 10))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
describe 'scale method' do
|
|
83
|
+
it 'returns a percentage object with the value of the percentage multiplied by the integer argument' do
|
|
84
|
+
percentage = @percentage.scale(2)
|
|
85
|
+
percentage.must_be_instance_of(Percentage)
|
|
86
|
+
percentage.value.must_equal(20)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
describe 'Percentage object initialized with a rational value' do
|
|
92
|
+
before do
|
|
93
|
+
@percentage = Percentage.new(Rational(1, 8))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
describe 'value method' do
|
|
97
|
+
it 'returns the value passed to the constructor' do
|
|
98
|
+
@percentage.value.must_equal(Rational(1, 8))
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
describe 'to_i method' do
|
|
103
|
+
it 'returns the integer value of the percentage' do
|
|
104
|
+
@percentage.to_i.must_equal(12)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
describe 'to_f method' do
|
|
109
|
+
it 'returns the float value of the percentage' do
|
|
110
|
+
@percentage.to_f.must_be_close_to(12.5)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
describe 'to_s method' do
|
|
115
|
+
it 'returns the decimal value of the percentage suffixed with the percent symbol' do
|
|
116
|
+
@percentage.to_s.must_equal('12.5%')
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
describe 'to_r method' do
|
|
121
|
+
it 'returns the rational value of the percentage' do
|
|
122
|
+
@percentage.to_r.must_equal(Rational(1, 8))
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
describe 'zero query method' do
|
|
127
|
+
it 'returns true if the percentage has a zero value' do
|
|
128
|
+
Percentage.new(Rational(0)).zero?.must_equal(true)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
it 'returns false otherwise' do
|
|
132
|
+
@percentage.zero?.must_equal(false)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
describe 'truncate method' do
|
|
137
|
+
it 'returns a percentage object with a truncated rational value' do
|
|
138
|
+
percentage = @percentage.truncate(1)
|
|
139
|
+
percentage.must_be_instance_of(Percentage)
|
|
140
|
+
percentage.value.must_equal(Rational(1, 10))
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
describe 'scale method' do
|
|
145
|
+
it 'returns a percentage object with the value of the percentage multiplied by the integer argument' do
|
|
146
|
+
percentage = @percentage.scale(2)
|
|
147
|
+
percentage.must_be_instance_of(Percentage)
|
|
148
|
+
percentage.value.must_equal(Rational(1, 4))
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
describe 'Percentage object initialized with a decimal value' do
|
|
154
|
+
before do
|
|
155
|
+
@percentage = Percentage.new(BigDecimal('0.175'))
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
describe 'value method' do
|
|
159
|
+
it 'returns the value passed to the constructor' do
|
|
160
|
+
@percentage.value.must_equal(BigDecimal('0.175'))
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
describe 'to_i method' do
|
|
165
|
+
it 'returns the integer value of the percentage' do
|
|
166
|
+
@percentage.to_i.must_equal(17)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
describe 'to_f method' do
|
|
171
|
+
it 'returns the float value of the percentage' do
|
|
172
|
+
@percentage.to_f.must_be_close_to(17.5)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
describe 'to_s method' do
|
|
177
|
+
it 'returns the decimal value of the percentage suffixed with the percent symbol' do
|
|
178
|
+
@percentage.to_s.must_equal('17.5%')
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
describe 'to_r method' do
|
|
183
|
+
it 'returns the rational value of the percentage' do
|
|
184
|
+
@percentage.to_r.must_equal(Rational(175, 1000))
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
describe 'zero query method' do
|
|
189
|
+
it 'returns true if the percentage has a zero value' do
|
|
190
|
+
Percentage.new(BigDecimal(0)).zero?.must_equal(true)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
it 'returns false otherwise' do
|
|
194
|
+
@percentage.zero?.must_equal(false)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
describe 'truncate method' do
|
|
199
|
+
it 'returns a percentage object with a truncated decimal value' do
|
|
200
|
+
percentage = @percentage.truncate(1)
|
|
201
|
+
percentage.must_be_instance_of(Percentage)
|
|
202
|
+
percentage.value.must_equal(BigDecimal('0.1'))
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
describe 'scale method' do
|
|
207
|
+
it 'returns a percentage object with the value of the percentage multiplied by the integer argument' do
|
|
208
|
+
percentage = @percentage.scale(2)
|
|
209
|
+
percentage.must_be_instance_of(Percentage)
|
|
210
|
+
percentage.value.must_equal(BigDecimal('0.35'))
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
describe 'Addition of percentage objects' do
|
|
216
|
+
it 'returns a percentage object with the value of the two percentages added together' do
|
|
217
|
+
percentage = Percentage.new(10) + Percentage.new(10)
|
|
218
|
+
percentage.must_be_instance_of(Percentage)
|
|
219
|
+
percentage.value.must_equal(20)
|
|
220
|
+
|
|
221
|
+
percentage = Percentage.new(Rational(1, 8)) + Percentage.new(10)
|
|
222
|
+
percentage.must_be_instance_of(Percentage)
|
|
223
|
+
percentage.value.must_equal(Rational(9, 40))
|
|
224
|
+
|
|
225
|
+
percentage = Percentage.new(BigDecimal('0.175')) + Percentage.new(BigDecimal('0.025'))
|
|
226
|
+
percentage.must_be_instance_of(Percentage)
|
|
227
|
+
percentage.value.must_equal(BigDecimal('0.2'))
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
describe 'Addition of percentage object with another type of object' do
|
|
232
|
+
it 'raises an exception' do
|
|
233
|
+
proc { Percentage.new(10) + 5 }.must_raise(TypeError)
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
describe 'Multiplication of percentage objects' do
|
|
238
|
+
it 'returns a percentage object with the fractional value of the two percentages multiplied together' do
|
|
239
|
+
percentage = Percentage.new(10) * Percentage.new(10)
|
|
240
|
+
percentage.must_be_instance_of(Percentage)
|
|
241
|
+
percentage.value.must_equal(Rational(1, 100))
|
|
242
|
+
|
|
243
|
+
percentage = Percentage.new(Rational(1, 8)) * Percentage.new(10)
|
|
244
|
+
percentage.must_be_instance_of(Percentage)
|
|
245
|
+
percentage.value.must_equal(Rational(1, 80))
|
|
246
|
+
|
|
247
|
+
percentage = Percentage.new(BigDecimal('0.175')) * Percentage.new(10)
|
|
248
|
+
percentage.must_be_instance_of(Percentage)
|
|
249
|
+
percentage.value.must_equal(Rational(7, 400))
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
describe 'Multiplication of a decimal object with a percentage object' do
|
|
254
|
+
it 'returns a decimal object with the value of the decimal multiplied by the fractional value of the percentage' do
|
|
255
|
+
percentage, decimal = Percentage.new(BigDecimal('0.175')), BigDecimal('99.00')
|
|
256
|
+
|
|
257
|
+
(decimal * percentage).must_equal(BigDecimal('17.325'))
|
|
258
|
+
(percentage * decimal).must_equal(BigDecimal('17.325'))
|
|
259
|
+
end
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
describe 'Percentage object equality' do
|
|
263
|
+
describe 'double equals method' do
|
|
264
|
+
it 'returns true for percentage objects with the same fractional value' do
|
|
265
|
+
(Percentage.new(50) == Percentage.new(50)).must_equal(true)
|
|
266
|
+
(Percentage.new(50) == Percentage.new(Rational(1, 2))).must_equal(true)
|
|
267
|
+
(Percentage.new(50) == Percentage.new(BigDecimal('0.5'))).must_equal(true)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
it 'returns false otherwise' do
|
|
271
|
+
(Percentage.new(50) == Percentage.new(100)).must_equal(false)
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
describe 'eql query method' do
|
|
276
|
+
it 'returns true for percentage objects with exactly the same fractional value' do
|
|
277
|
+
(Percentage.new(50).eql? Percentage.new(50)).must_equal(true)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
it 'returns false otherwise' do
|
|
281
|
+
(Percentage.new(50).eql? Percentage.new(Rational(1, 2))).must_equal(false)
|
|
282
|
+
(Percentage.new(50).eql? Percentage.new(BigDecimal('0.5'))).must_equal(false)
|
|
283
|
+
(Percentage.new(50).eql? Percentage.new(100)).must_equal(false)
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
describe 'Percentage method' do
|
|
289
|
+
describe 'when called with an integer argument' do
|
|
290
|
+
it 'returns a percentage object with the integer value' do
|
|
291
|
+
percentage = Percentage(10)
|
|
292
|
+
percentage.must_be_instance_of(Percentage)
|
|
293
|
+
percentage.value.must_equal(10)
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
describe 'when called with a decimal argument' do
|
|
298
|
+
it 'returns a percentage object with the value of the argument divided by 100' do
|
|
299
|
+
percentage = Percentage(BigDecimal('17.5'))
|
|
300
|
+
percentage.must_be_instance_of(Percentage)
|
|
301
|
+
percentage.value.must_equal(BigDecimal('0.175'))
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
describe 'when called with a rational argument' do
|
|
306
|
+
it 'returns a percentage object with the value of the argument divided by 100' do
|
|
307
|
+
percentage = Percentage(Rational(100, 3))
|
|
308
|
+
percentage.must_be_instance_of(Percentage)
|
|
309
|
+
percentage.value.must_equal(Rational(1, 3))
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
describe 'Percentage change method' do
|
|
315
|
+
it 'returns the difference between the arguments as a percentage of the first argument' do
|
|
316
|
+
percentage = Percentage.change(2, 3)
|
|
317
|
+
percentage.must_be_instance_of(Percentage)
|
|
318
|
+
percentage.must_equal(Percentage.new(50))
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
describe 'BigDecimal as_percentage_of method' do
|
|
323
|
+
it 'returns a percentage object with the value of the decimal divided by the argument' do
|
|
324
|
+
percentage = BigDecimal('50.00').as_percentage_of(BigDecimal('100.00'))
|
|
325
|
+
percentage.must_be_instance_of(Percentage)
|
|
326
|
+
percentage.value.must_equal(BigDecimal('0.5'))
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
describe 'Integer percent method' do
|
|
331
|
+
it 'returns a percentage object with the value of the integer' do
|
|
332
|
+
percentage = 10.percent
|
|
333
|
+
percentage.must_be_instance_of(Percentage)
|
|
334
|
+
percentage.value.must_equal(10)
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
describe 'Integer percent_of method' do
|
|
339
|
+
it 'returns the value of the receiver as a percentage multiplied by the argument' do
|
|
340
|
+
10.percent_of(100).must_equal(10)
|
|
341
|
+
10.percent_of(BigDecimal(15)).must_equal(BigDecimal('1.5'))
|
|
342
|
+
10.percent_of(Rational(150, 2)).must_equal(Rational(15, 2))
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
describe 'Integer as_percentage_of method' do
|
|
347
|
+
it 'returns a percentage object with the value of the integer divided by the argument' do
|
|
348
|
+
percentage = 10.as_percentage_of(20)
|
|
349
|
+
percentage.must_be_instance_of(Percentage)
|
|
350
|
+
percentage.value.must_equal(Rational(1, 2))
|
|
351
|
+
end
|
|
352
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: percentage
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Tim Craft
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-11-29 00:00:00.000000000 Z
|
|
13
|
+
dependencies: []
|
|
14
|
+
description: A little library for working with percentages
|
|
15
|
+
email:
|
|
16
|
+
- mail@timcraft.com
|
|
17
|
+
executables: []
|
|
18
|
+
extensions: []
|
|
19
|
+
extra_rdoc_files: []
|
|
20
|
+
files:
|
|
21
|
+
- lib/percentage.rb
|
|
22
|
+
- spec/percentage_spec.rb
|
|
23
|
+
- README.md
|
|
24
|
+
- Rakefile.rb
|
|
25
|
+
- percentage.gemspec
|
|
26
|
+
homepage: http://github.com/timcraft/percentage
|
|
27
|
+
licenses: []
|
|
28
|
+
post_install_message:
|
|
29
|
+
rdoc_options: []
|
|
30
|
+
require_paths:
|
|
31
|
+
- lib
|
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ! '>='
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '0'
|
|
44
|
+
requirements: []
|
|
45
|
+
rubyforge_project:
|
|
46
|
+
rubygems_version: 1.8.24
|
|
47
|
+
signing_key:
|
|
48
|
+
specification_version: 3
|
|
49
|
+
summary: See description
|
|
50
|
+
test_files: []
|
|
51
|
+
has_rdoc:
|