spook_and_puff_money 0.5
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/MIT-LICENSE +20 -0
- data/README.md +7 -0
- data/lib/spook_and_puff/money.rb +249 -0
- metadata +98 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 Spook and Puff
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# SpookAndPuff::Money
|
2
|
+
|
3
|
+
This is yet another Ruby class made to represent money. This one is dead simple. It doesn't integrate with any frameworks, it simply enforces precison by ensuring all operands -- for methods like +, -, / etc. -- are BigDecimals.
|
4
|
+
|
5
|
+
It also provides a few other conveniences like formatting.
|
6
|
+
|
7
|
+
It is primarily intended for our own projects, but is offered here as an open sourced project, because.
|
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
3
|
+
# The main Spook and Puff module which namespaces our projects. Yay?
|
4
|
+
module SpookAndPuff
|
5
|
+
# The money class represents monetary values with a precision of up to seven
|
6
|
+
# digits. When used as part of a comparison or mathmatical operation it
|
7
|
+
# always ensures the other operand is coerced into a BigDecimal. This ensures
|
8
|
+
# the precision is maintained.
|
9
|
+
class Money
|
10
|
+
include Comparable
|
11
|
+
|
12
|
+
# Stores the raw BigDecimal instance that a Money instance wraps.
|
13
|
+
attr_reader :raw
|
14
|
+
|
15
|
+
# Initializes the money class from a BigDecimal instance.
|
16
|
+
#
|
17
|
+
# @param [BigDecimal, String] value
|
18
|
+
#
|
19
|
+
# @return self
|
20
|
+
#
|
21
|
+
# @raise TypeError
|
22
|
+
#
|
23
|
+
# @todo This potentially should only handle BigDecimal, Money and String
|
24
|
+
def initialize(value)
|
25
|
+
@raw = case value
|
26
|
+
when BigDecimal then value.round(7)
|
27
|
+
when String then BigDecimal.new(value).round(7)
|
28
|
+
else raise TypeError.new("Money can only be initalized with a BigDecimal or String not #{value.class}.")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Value comparison.
|
33
|
+
#
|
34
|
+
# @param [Money, Numeric, String] other
|
35
|
+
#
|
36
|
+
# @return Money
|
37
|
+
#
|
38
|
+
# @raise ArgumentError
|
39
|
+
def ==(other)
|
40
|
+
@raw == for_comparison(other)
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param [Money, Numeric, String] other
|
44
|
+
#
|
45
|
+
# @return Money
|
46
|
+
#
|
47
|
+
# @raise ArgumentError
|
48
|
+
def <=>(other)
|
49
|
+
@raw <=> for_comparison(other)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Multiplication. Numerics and strings only.
|
53
|
+
#
|
54
|
+
# @param [Integer, Fixnum, String] other
|
55
|
+
#
|
56
|
+
# @return Money
|
57
|
+
#
|
58
|
+
# @raise ArgumentError
|
59
|
+
def *(other)
|
60
|
+
Money.new(@raw * coerce(other))
|
61
|
+
end
|
62
|
+
|
63
|
+
# Division. Numerics and strings only.
|
64
|
+
#
|
65
|
+
# @param [Integer, Fixnum, String] other
|
66
|
+
#
|
67
|
+
# @return Money
|
68
|
+
#
|
69
|
+
# @raise ArgumentError
|
70
|
+
def /(other)
|
71
|
+
Money.new(@raw / coerce(other))
|
72
|
+
end
|
73
|
+
|
74
|
+
# Minus.
|
75
|
+
#
|
76
|
+
# @param [Money, Numeric, String] other
|
77
|
+
#
|
78
|
+
# @return Money
|
79
|
+
#
|
80
|
+
# @raise ArgumentError
|
81
|
+
def -(other)
|
82
|
+
Money.new(@raw - for_operation(other, 'subtraction'))
|
83
|
+
end
|
84
|
+
|
85
|
+
# Add.
|
86
|
+
#
|
87
|
+
# @param [Money, Numeric, String] other
|
88
|
+
#
|
89
|
+
# @return Money
|
90
|
+
#
|
91
|
+
# @raise ArgumentError
|
92
|
+
def +(other)
|
93
|
+
Money.new(@raw + for_operation(other, 'addition'))
|
94
|
+
end
|
95
|
+
|
96
|
+
# Unary minus. This negates the money value
|
97
|
+
#
|
98
|
+
# @return Money
|
99
|
+
def -@
|
100
|
+
Money.new(-@raw)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Unary plus. Just returns the reciever.
|
104
|
+
#
|
105
|
+
# @return Money
|
106
|
+
def +@
|
107
|
+
self
|
108
|
+
end
|
109
|
+
|
110
|
+
# Returns a new Money instance with the absolute value.
|
111
|
+
#
|
112
|
+
# @return Money
|
113
|
+
def abs
|
114
|
+
Money.new(@raw.abs)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns a BigDecimal representation of the value in cents.
|
118
|
+
#
|
119
|
+
# @return BigDecimal
|
120
|
+
def cents
|
121
|
+
@raw * BigDecimal.new('100')
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns the raw BigDecimal value.
|
125
|
+
#
|
126
|
+
# @return BigDecimal
|
127
|
+
def to_big_decimal
|
128
|
+
@raw
|
129
|
+
end
|
130
|
+
|
131
|
+
# Checks for zero value.
|
132
|
+
#
|
133
|
+
# @return [true, false]
|
134
|
+
def zero?
|
135
|
+
@raw == 0
|
136
|
+
end
|
137
|
+
|
138
|
+
# Checks to see if the value is positive i.e. non-negative, non-zero.
|
139
|
+
#
|
140
|
+
# @return [true, false]
|
141
|
+
def positive?
|
142
|
+
@raw > 0
|
143
|
+
end
|
144
|
+
|
145
|
+
# Checks to see if the value is positive.
|
146
|
+
#
|
147
|
+
# @return [true, false]
|
148
|
+
def non_negative?
|
149
|
+
@raw > -1
|
150
|
+
end
|
151
|
+
|
152
|
+
# Checks to see if the value is less than zero.
|
153
|
+
#
|
154
|
+
# @return [true, false]
|
155
|
+
def negative?
|
156
|
+
@raw < 0
|
157
|
+
end
|
158
|
+
|
159
|
+
# Calculates an amount based on the provided percentage.
|
160
|
+
#
|
161
|
+
# @param [Numeric, String] percentage
|
162
|
+
#
|
163
|
+
# @return Money
|
164
|
+
#
|
165
|
+
# @raise TypeError
|
166
|
+
def percent(percentage)
|
167
|
+
Money.new(@raw * (coerce(percentage) / BigDecimal.new('100')))
|
168
|
+
end
|
169
|
+
|
170
|
+
# Calculates the proportion of the provided amount as a percentage.
|
171
|
+
#
|
172
|
+
# @param SpookAndPuff::Money amount
|
173
|
+
#
|
174
|
+
# @return BigDecimal
|
175
|
+
#
|
176
|
+
# @raise TypeError
|
177
|
+
def proportion(amount)
|
178
|
+
amount.raw / @raw * BigDecimal('100')
|
179
|
+
end
|
180
|
+
|
181
|
+
# Rounds to the specified places; defaults to two.
|
182
|
+
#
|
183
|
+
# @param Integer places
|
184
|
+
#
|
185
|
+
# @return Money
|
186
|
+
def round(places = 2)
|
187
|
+
Money.new(@raw.round(places))
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns a currency formatted string.
|
191
|
+
#
|
192
|
+
# @return String
|
193
|
+
def to_s
|
194
|
+
"$%.2f" % @raw.round(2)
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
# Grabs the raw value of a Money instance, erroring with a message
|
200
|
+
# about comparison if it's the wrong type.
|
201
|
+
#
|
202
|
+
# @param SpookAndPuff::Money other
|
203
|
+
#
|
204
|
+
# @return BigDecimal
|
205
|
+
#
|
206
|
+
# @raise ArgumentError
|
207
|
+
def for_comparison(other)
|
208
|
+
unless other.is_a?(Money)
|
209
|
+
raise ArgumentError.new("#{other.class} cannot be compared with SpookAndPuff::Money")
|
210
|
+
end
|
211
|
+
|
212
|
+
other.raw
|
213
|
+
end
|
214
|
+
|
215
|
+
# Grabs the raw value of a Money instance, erroring with a message
|
216
|
+
# about comparison if it's the wrong type.
|
217
|
+
#
|
218
|
+
# @param SpookAndPuff::Money other
|
219
|
+
# @param String op
|
220
|
+
#
|
221
|
+
# @return BigDecimal
|
222
|
+
#
|
223
|
+
# @raise ArgumentError
|
224
|
+
def for_operation(other, op)
|
225
|
+
unless other.is_a?(Money)
|
226
|
+
raise ArgumentError.new("Cannot perform #{op} on SpookAndPuff::Money with #{other.class}")
|
227
|
+
end
|
228
|
+
|
229
|
+
other.raw
|
230
|
+
end
|
231
|
+
|
232
|
+
# Coerces the provided value into a BigDecimal. It will handle any
|
233
|
+
# Numeric or string.
|
234
|
+
#
|
235
|
+
# @param [Numeric, String] other
|
236
|
+
#
|
237
|
+
# @return BigDecimal
|
238
|
+
#
|
239
|
+
# @raise TypeError
|
240
|
+
def coerce(other)
|
241
|
+
case other
|
242
|
+
when BigDecimal then other
|
243
|
+
when String then BigDecimal.new(other)
|
244
|
+
when Integer, Fixnum, Bignum then BigDecimal.new(other.to_s)
|
245
|
+
else raise TypeError
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end # Money
|
249
|
+
end # SpookAndPuff
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spook_and_puff_money
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.5'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Luke Sutton
|
9
|
+
- Ben Hull
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2013-05-10 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - '='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 2.13.0
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - '='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 2.13.0
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
name: yard
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - '='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 0.8.6.1
|
39
|
+
type: :development
|
40
|
+
prerelease: false
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - '='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 0.8.6.1
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: redcarpet
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.2.2
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - '='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: 2.2.2
|
63
|
+
description:
|
64
|
+
email:
|
65
|
+
- lukeandben@spookandpuff.com
|
66
|
+
executables: []
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- lib/spook_and_puff/money.rb
|
71
|
+
- MIT-LICENSE
|
72
|
+
- README.md
|
73
|
+
homepage: http://spookandpuff.com
|
74
|
+
licenses: []
|
75
|
+
post_install_message:
|
76
|
+
rdoc_options: []
|
77
|
+
require_paths:
|
78
|
+
- lib
|
79
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ! '>='
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ! '>='
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
requirements: []
|
92
|
+
rubyforge_project:
|
93
|
+
rubygems_version: 1.8.24
|
94
|
+
signing_key:
|
95
|
+
specification_version: 3
|
96
|
+
summary: A simple money class.
|
97
|
+
test_files: []
|
98
|
+
has_rdoc:
|