money 6.6.1 → 6.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.travis.yml +3 -2
- data/AUTHORS +16 -13
- data/CHANGELOG.md +12 -3
- data/Gemfile +1 -1
- data/README.md +31 -13
- data/Rakefile +1 -0
- data/config/currency_iso.json +23 -7
- data/config/currency_non_iso.json +34 -1
- data/lib/money.rb +13 -0
- data/lib/money/bank/base.rb +1 -1
- data/lib/money/currency.rb +4 -4
- data/lib/money/currency/heuristics.rb +3 -2
- data/lib/money/money.rb +2 -2
- data/lib/money/money/arithmetic.rb +44 -19
- data/lib/money/money/formatting.rb +35 -26
- data/lib/money/version.rb +1 -1
- data/money.gemspec +2 -1
- data/spec/bank/base_spec.rb +0 -2
- data/spec/bank/single_currency_spec.rb +0 -2
- data/spec/bank/variable_exchange_spec.rb +0 -1
- data/spec/currency/heuristics_spec.rb +79 -1
- data/spec/currency/loader_spec.rb +0 -1
- data/spec/currency_spec.rb +7 -9
- data/spec/money/arithmetic_spec.rb +99 -46
- data/spec/money/constructors_spec.rb +0 -2
- data/spec/money/formatting_spec.rb +42 -2
- data/spec/money_spec.rb +3 -5
- data/spec/rates_store/memory_spec.rb +2 -4
- data/spec/spec_helper.rb +2 -7
- metadata +26 -5
@@ -53,7 +53,7 @@ class Money
|
|
53
53
|
def currencies_by_name
|
54
54
|
{}.tap do |r|
|
55
55
|
table.each do |dummy,c|
|
56
|
-
name_parts = c[:name].downcase.split
|
56
|
+
name_parts = c[:name].unaccent.downcase.split
|
57
57
|
name_parts.each {|part| part.chomp!('.')}
|
58
58
|
|
59
59
|
# construct one branch per word
|
@@ -94,7 +94,7 @@ class Money
|
|
94
94
|
str.gsub!(/[0-9][\.,:0-9]*[0-9]/,'')
|
95
95
|
str.gsub!(/[0-9]/, '')
|
96
96
|
str.downcase!
|
97
|
-
@words = str.split
|
97
|
+
@words = str.unaccent.split
|
98
98
|
@words.each {|word| word.chomp!('.'); word.chomp!(',') }
|
99
99
|
end
|
100
100
|
|
@@ -151,3 +151,4 @@ class Money
|
|
151
151
|
end
|
152
152
|
end
|
153
153
|
end
|
154
|
+
|
data/lib/money/money.rb
CHANGED
@@ -472,9 +472,9 @@ class Money
|
|
472
472
|
end
|
473
473
|
|
474
474
|
# Allocates money between different parties without losing pennies.
|
475
|
-
# After the
|
475
|
+
# After the mathematical split has been performed, leftover pennies will
|
476
476
|
# be distributed round-robin amongst the parties. This means that parties
|
477
|
-
# listed first will likely
|
477
|
+
# listed first will likely receive more pennies than ones that are listed later
|
478
478
|
#
|
479
479
|
# @param [Array<Numeric>] splits [0.50, 0.25, 0.25] to give 50% of the cash to party1, 25% to party2, and 25% to party3.
|
480
480
|
#
|
@@ -1,4 +1,17 @@
|
|
1
1
|
class Money
|
2
|
+
CoercedNumber = Struct.new(:value) do
|
3
|
+
include Comparable
|
4
|
+
|
5
|
+
def +(other) raise TypeError; end
|
6
|
+
def -(other) raise TypeError; end
|
7
|
+
def /(other) raise TypeError; end
|
8
|
+
def <=>(other) raise TypeError; end
|
9
|
+
|
10
|
+
def *(other)
|
11
|
+
other * value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
2
15
|
module Arithmetic
|
3
16
|
|
4
17
|
# Returns a money object with changed polarity.
|
@@ -11,22 +24,23 @@ class Money
|
|
11
24
|
self.class.new(-fractional, currency)
|
12
25
|
end
|
13
26
|
|
14
|
-
# Checks whether two
|
15
|
-
# amount. If
|
16
|
-
# if the amounts are both zero. Checks against objects that
|
17
|
-
#
|
27
|
+
# Checks whether two Money objects have the same currency and the same
|
28
|
+
# amount. If Money objects have a different currency it will only be true
|
29
|
+
# if the amounts are both zero. Checks against objects that are not Money or
|
30
|
+
# a subclass will always return false.
|
18
31
|
#
|
19
32
|
# @param [Money] other_money Value to compare with.
|
20
33
|
#
|
21
34
|
# @return [Boolean]
|
22
35
|
#
|
23
36
|
# @example
|
24
|
-
# Money.new(100).eql?(Money.new(101))
|
25
|
-
# Money.new(100).eql?(Money.new(100))
|
26
|
-
# Money.new(
|
37
|
+
# Money.new(100).eql?(Money.new(101)) #=> false
|
38
|
+
# Money.new(100).eql?(Money.new(100)) #=> true
|
39
|
+
# Money.new(100, "USD").eql?(Money.new(100, "GBP")) #=> false
|
40
|
+
# Money.new(0, "USD").eql?(Money.new(0, "EUR")) #=> true
|
41
|
+
# Money.new(100).eql?("1.00") #=> false
|
27
42
|
def eql?(other_money)
|
28
|
-
if other_money.
|
29
|
-
other_money = other_money.to_money
|
43
|
+
if other_money.is_a?(Money)
|
30
44
|
(fractional == other_money.fractional && currency == other_money.currency) ||
|
31
45
|
(fractional == 0 && other_money.fractional == 0)
|
32
46
|
else
|
@@ -34,14 +48,23 @@ class Money
|
|
34
48
|
end
|
35
49
|
end
|
36
50
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
51
|
+
# Compares two Money objects. If money objects have a different currency it
|
52
|
+
# will attempt to convert the currency.
|
53
|
+
#
|
54
|
+
# @param [Money] other_money Value to compare with.
|
55
|
+
#
|
56
|
+
# @return [Fixnum]
|
57
|
+
#
|
58
|
+
# @raise [TypeError] when other object is not Money
|
59
|
+
#
|
60
|
+
def <=>(other_money)
|
61
|
+
return nil unless other_money.is_a?(Money)
|
62
|
+
if fractional != 0 && other_money.fractional != 0 && currency != other_money.currency
|
63
|
+
other_money = other_money.exchange_to(currency)
|
44
64
|
end
|
65
|
+
fractional <=> other_money.fractional
|
66
|
+
rescue Money::Bank::UnknownRate
|
67
|
+
nil
|
45
68
|
end
|
46
69
|
|
47
70
|
# Test if the amount is positive. Returns +true+ if the money amount is
|
@@ -81,7 +104,8 @@ class Money
|
|
81
104
|
# @example
|
82
105
|
# Money.new(100) + Money.new(100) #=> #<Money @fractional=200>
|
83
106
|
def +(other_money)
|
84
|
-
return self if other_money
|
107
|
+
return self if other_money.zero?
|
108
|
+
raise TypeError unless other_money.is_a?(Money)
|
85
109
|
other_money = other_money.exchange_to(currency)
|
86
110
|
self.class.new(fractional + other_money.fractional, currency)
|
87
111
|
end
|
@@ -98,7 +122,8 @@ class Money
|
|
98
122
|
# @example
|
99
123
|
# Money.new(100) - Money.new(99) #=> #<Money @fractional=1>
|
100
124
|
def -(other_money)
|
101
|
-
return self if other_money
|
125
|
+
return self if other_money.zero?
|
126
|
+
raise TypeError unless other_money.is_a?(Money)
|
102
127
|
other_money = other_money.exchange_to(currency)
|
103
128
|
self.class.new(fractional - other_money.fractional, currency)
|
104
129
|
end
|
@@ -279,7 +304,7 @@ class Money
|
|
279
304
|
# @example
|
280
305
|
# 2 * Money.new(10) #=> #<Money @fractional=20>
|
281
306
|
def coerce(other)
|
282
|
-
[
|
307
|
+
[CoercedNumber.new(other), self]
|
283
308
|
end
|
284
309
|
end
|
285
310
|
end
|
@@ -1,30 +1,6 @@
|
|
1
1
|
# encoding: UTF-8
|
2
2
|
class Money
|
3
3
|
module Formatting
|
4
|
-
def self.included(base)
|
5
|
-
[
|
6
|
-
[:thousands_separator, :delimiter, ","],
|
7
|
-
[:decimal_mark, :separator, "."]
|
8
|
-
].each do |method, name, character|
|
9
|
-
define_i18n_method(method, name, character)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def self.define_i18n_method(method, name, character)
|
14
|
-
define_method(method) do
|
15
|
-
if self.class.use_i18n
|
16
|
-
begin
|
17
|
-
I18n.t name, :scope => "number.currency.format", :raise => true
|
18
|
-
rescue I18n::MissingTranslationData
|
19
|
-
I18n.t name, :scope =>"number.format", :default => (currency.send(method) || character)
|
20
|
-
end
|
21
|
-
else
|
22
|
-
currency.send(method) || character
|
23
|
-
end
|
24
|
-
end
|
25
|
-
alias_method name, method
|
26
|
-
end
|
27
|
-
|
28
4
|
# Creates a formatted price string according to several rules.
|
29
5
|
#
|
30
6
|
# @param [Hash] rules The options used to format the string.
|
@@ -215,6 +191,10 @@ class Money
|
|
215
191
|
# # CAD: "CAD$"
|
216
192
|
# Money.new(10000, "CAD").format(:translate => true) #=> "CAD$100.00"
|
217
193
|
#
|
194
|
+
# @example
|
195
|
+
# Money.new(89000, :btc).format(:drop_trailing_zeros => true) #=> B⃦0.00089
|
196
|
+
# Money.new(110, :usd).format(:drop_trailing_zeros => true) #=> $1.1
|
197
|
+
#
|
218
198
|
# Note that the default rules can be defined through +Money.default_formatting_rules+ hash.
|
219
199
|
#
|
220
200
|
# @see +Money.default_formatting_rules+ for more information.
|
@@ -239,13 +219,13 @@ class Money
|
|
239
219
|
formatted = self.abs.to_s
|
240
220
|
|
241
221
|
if rules[:rounded_infinite_precision]
|
242
|
-
formatted.gsub!(/#{
|
222
|
+
formatted.gsub!(/#{decimal_mark}/, '.') unless '.' == decimal_mark
|
243
223
|
formatted = ((BigDecimal(formatted) * currency.subunit_to_unit).round / BigDecimal(currency.subunit_to_unit.to_s)).to_s("F")
|
244
224
|
formatted.gsub!(/\..*/) do |decimal_part|
|
245
225
|
decimal_part << '0' while decimal_part.length < (currency.decimal_places + 1)
|
246
226
|
decimal_part
|
247
227
|
end
|
248
|
-
formatted.gsub!(/\./,
|
228
|
+
formatted.gsub!(/\./, decimal_mark) unless '.' == decimal_mark
|
249
229
|
end
|
250
230
|
|
251
231
|
sign = self.negative? ? '-' : ''
|
@@ -254,6 +234,12 @@ class Money
|
|
254
234
|
formatted = "#{formatted.to_i}"
|
255
235
|
end
|
256
236
|
|
237
|
+
# Inspiration: https://github.com/rails/rails/blob/16214d1108c31174c94503caced3855b0f6bad95/activesupport/lib/active_support/number_helper/number_to_rounded_converter.rb#L72-L79
|
238
|
+
if rules[:drop_trailing_zeros]
|
239
|
+
escaped_decimal_mark = Regexp.escape(decimal_mark)
|
240
|
+
formatted = formatted.sub(/(#{escaped_decimal_mark})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_decimal_mark}\z/, '')
|
241
|
+
end
|
242
|
+
|
257
243
|
thousands_separator_value = thousands_separator
|
258
244
|
# Determine thousands_separator
|
259
245
|
if rules.has_key?(:thousands_separator)
|
@@ -300,8 +286,31 @@ class Money
|
|
300
286
|
formatted
|
301
287
|
end
|
302
288
|
|
289
|
+
def thousands_separator
|
290
|
+
i18n_format_for(:thousands_separator, :delimiter, ",")
|
291
|
+
end
|
292
|
+
|
293
|
+
def decimal_mark
|
294
|
+
i18n_format_for(:decimal_mark, :separator, ".")
|
295
|
+
end
|
296
|
+
|
297
|
+
alias_method :delimiter, :thousands_separator
|
298
|
+
alias_method :separator, :decimal_mark
|
299
|
+
|
303
300
|
private
|
304
301
|
|
302
|
+
def i18n_format_for(method, name, character)
|
303
|
+
if self.class.use_i18n
|
304
|
+
begin
|
305
|
+
I18n.t name, :scope => "number.currency.format", :raise => true
|
306
|
+
rescue I18n::MissingTranslationData
|
307
|
+
I18n.t name, :scope =>"number.format", :default => (currency.send(method) || character)
|
308
|
+
end
|
309
|
+
else
|
310
|
+
currency.send(method) || character
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
305
314
|
# Cleans up formatting rules.
|
306
315
|
#
|
307
316
|
# @param [Hash] rules
|
data/lib/money/version.rb
CHANGED
data/money.gemspec
CHANGED
@@ -26,10 +26,11 @@ Test responsibly :-)
|
|
26
26
|
MSG
|
27
27
|
|
28
28
|
s.add_dependency 'i18n', ['>= 0.6.4', '<= 0.7.0']
|
29
|
+
s.add_dependency 'sixarm_ruby_unaccent', ['>= 1.1.1', '< 2']
|
29
30
|
|
30
31
|
s.add_development_dependency "bundler", "~> 1.3"
|
31
32
|
s.add_development_dependency "rake"
|
32
|
-
s.add_development_dependency "rspec", "~> 3.
|
33
|
+
s.add_development_dependency "rspec", "~> 3.4.0"
|
33
34
|
s.add_development_dependency "yard", "~> 0.8"
|
34
35
|
s.add_development_dependency "kramdown", "~> 1.1"
|
35
36
|
|
data/spec/bank/base_spec.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require 'spec_helper'
|
3
2
|
|
4
3
|
describe Money::Currency::Heuristics do
|
5
4
|
describe "#analyze_string" do
|
@@ -77,6 +76,85 @@ describe Money::Currency::Heuristics do
|
|
77
76
|
expect(it.analyze("10EUR is less than 100:- but really, I want US$1")).to eq ['EUR', 'SEK', 'USD']
|
78
77
|
end
|
79
78
|
|
79
|
+
it "find currencies with normal characters in name using unaccent" do
|
80
|
+
expect(it.analyze("10 Nicaraguan Cordoba")).to eq ["NIO"]
|
81
|
+
end
|
82
|
+
|
83
|
+
it "find currencies with special characters in name using unaccent" do
|
84
|
+
expect(it.analyze("10 Nicaraguan Córdoba")).to eq ["NIO"]
|
85
|
+
end
|
86
|
+
|
87
|
+
it "find currencies with special symbols using unaccent" do
|
88
|
+
expect(it.analyze("ل.س")).not_to eq []
|
89
|
+
expect(it.analyze("R₣")).not_to eq []
|
90
|
+
expect(it.analyze("ரூ")).not_to eq []
|
91
|
+
expect(it.analyze("රු")).not_to eq []
|
92
|
+
expect(it.analyze("сом")).not_to eq []
|
93
|
+
expect(it.analyze("圓")).not_to eq []
|
94
|
+
expect(it.analyze("円")).not_to eq []
|
95
|
+
expect(it.analyze("৳")).not_to eq []
|
96
|
+
expect(it.analyze("૱")).not_to eq []
|
97
|
+
expect(it.analyze("௹")).not_to eq []
|
98
|
+
expect(it.analyze("रु")).not_to eq []
|
99
|
+
expect(it.analyze("ש״ח")).not_to eq []
|
100
|
+
expect(it.analyze("元")).not_to eq []
|
101
|
+
expect(it.analyze("¢")).not_to eq []
|
102
|
+
expect(it.analyze("£")).not_to eq []
|
103
|
+
expect(it.analyze("€")).not_to eq []
|
104
|
+
expect(it.analyze("¥")).not_to eq []
|
105
|
+
expect(it.analyze("د.إ")).not_to eq []
|
106
|
+
expect(it.analyze("؋")).not_to eq []
|
107
|
+
expect(it.analyze("դր.")).not_to eq []
|
108
|
+
expect(it.analyze("ƒ")).not_to eq []
|
109
|
+
expect(it.analyze("₼")).not_to eq []
|
110
|
+
expect(it.analyze("৳")).not_to eq []
|
111
|
+
expect(it.analyze("лв")).not_to eq []
|
112
|
+
expect(it.analyze("лев")).not_to eq []
|
113
|
+
expect(it.analyze("дин")).not_to eq []
|
114
|
+
expect(it.analyze("ب.د")).not_to eq []
|
115
|
+
expect(it.analyze("₡")).not_to eq []
|
116
|
+
expect(it.analyze("Kč")).not_to eq []
|
117
|
+
expect(it.analyze("د.ج")).not_to eq []
|
118
|
+
expect(it.analyze("ج.م")).not_to eq []
|
119
|
+
expect(it.analyze("ლ")).not_to eq []
|
120
|
+
expect(it.analyze("₵")).not_to eq []
|
121
|
+
expect(it.analyze("₪")).not_to eq []
|
122
|
+
expect(it.analyze("₹")).not_to eq []
|
123
|
+
expect(it.analyze("ع.د")).not_to eq []
|
124
|
+
expect(it.analyze("﷼")).not_to eq []
|
125
|
+
expect(it.analyze("د.ا")).not_to eq []
|
126
|
+
expect(it.analyze("៛")).not_to eq []
|
127
|
+
expect(it.analyze("₩")).not_to eq []
|
128
|
+
expect(it.analyze("د.ك")).not_to eq []
|
129
|
+
expect(it.analyze("〒")).not_to eq []
|
130
|
+
expect(it.analyze("₭")).not_to eq []
|
131
|
+
expect(it.analyze("ل.ل")).not_to eq []
|
132
|
+
expect(it.analyze("₨")).not_to eq []
|
133
|
+
expect(it.analyze("ل.د")).not_to eq []
|
134
|
+
expect(it.analyze("د.م.")).not_to eq []
|
135
|
+
expect(it.analyze("ден")).not_to eq []
|
136
|
+
expect(it.analyze("₮")).not_to eq []
|
137
|
+
expect(it.analyze("₦")).not_to eq []
|
138
|
+
expect(it.analyze("C$")).not_to eq []
|
139
|
+
expect(it.analyze("ر.ع.")).not_to eq []
|
140
|
+
expect(it.analyze("₱")).not_to eq []
|
141
|
+
expect(it.analyze("zł")).not_to eq []
|
142
|
+
expect(it.analyze("₲")).not_to eq []
|
143
|
+
expect(it.analyze("ر.ق")).not_to eq []
|
144
|
+
expect(it.analyze("РСД")).not_to eq []
|
145
|
+
expect(it.analyze("₽")).not_to eq []
|
146
|
+
expect(it.analyze("ر.س")).not_to eq []
|
147
|
+
expect(it.analyze("฿")).not_to eq []
|
148
|
+
expect(it.analyze("د.ت")).not_to eq []
|
149
|
+
expect(it.analyze("T$")).not_to eq []
|
150
|
+
expect(it.analyze("₺")).not_to eq []
|
151
|
+
expect(it.analyze("₴")).not_to eq []
|
152
|
+
expect(it.analyze("₫")).not_to eq []
|
153
|
+
expect(it.analyze("B⃦")).not_to eq []
|
154
|
+
expect(it.analyze("₤")).not_to eq []
|
155
|
+
expect(it.analyze("ރ")).not_to eq []
|
156
|
+
end
|
157
|
+
|
80
158
|
it "should function with unicode characters" do
|
81
159
|
expect(it.analyze("10 դր.")).to eq ["AMD"]
|
82
160
|
end
|
data/spec/currency_spec.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
3
|
class Money
|
6
4
|
describe Currency do
|
7
5
|
|
8
|
-
FOO = '{ "priority": 1, "iso_code": "FOO", "iso_numeric": "840", "name": "United States Dollar", "symbol": "$", "subunit": "Cent", "subunit_to_unit":
|
6
|
+
FOO = '{ "priority": 1, "iso_code": "FOO", "iso_numeric": "840", "name": "United States Dollar", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 1000, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": ",", "smallest_denomination": 1 }'
|
9
7
|
|
10
8
|
def register_foo(opts={})
|
11
9
|
foo_attrs = JSON.parse(FOO, :symbolize_names => true)
|
@@ -339,21 +337,21 @@ class Money
|
|
339
337
|
|
340
338
|
describe "#exponent" do
|
341
339
|
it "conforms to iso 4217" do
|
342
|
-
Currency.new(:jpy).exponent
|
343
|
-
Currency.new(:usd).exponent
|
344
|
-
Currency.new(:iqd).exponent
|
340
|
+
expect(Currency.new(:jpy).exponent).to eq 0
|
341
|
+
expect(Currency.new(:usd).exponent).to eq 2
|
342
|
+
expect(Currency.new(:iqd).exponent).to eq 3
|
345
343
|
end
|
346
344
|
end
|
347
345
|
|
348
346
|
describe "#decimal_places" do
|
349
347
|
it "proper places for known currency" do
|
350
|
-
Currency.new(:mro).decimal_places
|
351
|
-
Currency.new(:usd).decimal_places
|
348
|
+
expect(Currency.new(:mro).decimal_places).to eq 1
|
349
|
+
expect(Currency.new(:usd).decimal_places).to eq 2
|
352
350
|
end
|
353
351
|
|
354
352
|
it "proper places for custom currency" do
|
355
353
|
register_foo
|
356
|
-
Currency.new(:foo).decimal_places
|
354
|
+
expect(Currency.new(:foo).decimal_places).to eq 3
|
357
355
|
unregister_foo
|
358
356
|
end
|
359
357
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
3
|
describe Money do
|
6
4
|
describe "-@" do
|
7
5
|
it "changes the sign of a number" do
|
@@ -31,29 +29,21 @@ describe Money do
|
|
31
29
|
expect(Money.new(0, "USD")).to eq Money.new(0, "JPY")
|
32
30
|
end
|
33
31
|
|
34
|
-
it "returns false if used to compare with an object that doesn't
|
32
|
+
it "returns false if used to compare with an object that doesn't inherit from Money" do
|
35
33
|
expect(Money.new(1_00, "USD")).not_to eq Object.new
|
36
34
|
expect(Money.new(1_00, "USD")).not_to eq Class
|
37
35
|
expect(Money.new(1_00, "USD")).not_to eq Kernel
|
38
|
-
expect(Money.new(1_00, "USD")).not_to eq
|
36
|
+
expect(Money.new(1_00, "USD")).not_to eq(/foo/)
|
39
37
|
expect(Money.new(1_00, "USD")).not_to eq nil
|
40
38
|
end
|
41
39
|
|
42
|
-
it "can be used to compare with an object that
|
43
|
-
klass = Class.new
|
44
|
-
def initialize(money)
|
45
|
-
@money = money
|
46
|
-
end
|
47
|
-
|
48
|
-
def to_money
|
49
|
-
@money
|
50
|
-
end
|
51
|
-
end
|
40
|
+
it "can be used to compare with an object that inherits from Money" do
|
41
|
+
klass = Class.new(Money)
|
52
42
|
|
53
|
-
expect(Money.new(1_00, "USD")).to eq klass.new(
|
54
|
-
expect(Money.new(2_50, "USD")).to eq klass.new(
|
55
|
-
expect(Money.new(2_50, "USD")).not_to eq klass.new(
|
56
|
-
expect(Money.new(1_00, "GBP")).not_to eq klass.new(
|
43
|
+
expect(Money.new(1_00, "USD")).to eq klass.new(1_00, "USD")
|
44
|
+
expect(Money.new(2_50, "USD")).to eq klass.new(2_50, "USD")
|
45
|
+
expect(Money.new(2_50, "USD")).not_to eq klass.new(3_00, "USD")
|
46
|
+
expect(Money.new(1_00, "GBP")).not_to eq klass.new(1_00, "USD")
|
57
47
|
end
|
58
48
|
end
|
59
49
|
|
@@ -65,7 +55,7 @@ describe Money do
|
|
65
55
|
expect(Money.new(1_00, "USD").eql?(Money.new(99_00, "EUR"))).to be false
|
66
56
|
end
|
67
57
|
|
68
|
-
it "returns false if used to compare with an object that doesn't
|
58
|
+
it "returns false if used to compare with an object that doesn't inherit from Money" do
|
69
59
|
expect(Money.new(1_00, "USD").eql?(Object.new)).to be false
|
70
60
|
expect(Money.new(1_00, "USD").eql?(Class)).to be false
|
71
61
|
expect(Money.new(1_00, "USD").eql?(Kernel)).to be false
|
@@ -73,21 +63,13 @@ describe Money do
|
|
73
63
|
expect(Money.new(1_00, "USD").eql?(nil)).to be false
|
74
64
|
end
|
75
65
|
|
76
|
-
it "can be used to compare with an object that
|
77
|
-
klass = Class.new
|
78
|
-
def initialize(money)
|
79
|
-
@money = money
|
80
|
-
end
|
66
|
+
it "can be used to compare with an object that inherits from Money" do
|
67
|
+
klass = Class.new(Money)
|
81
68
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
expect(Money.new(1_00, "USD").eql?(klass.new(Money.new(1_00, "USD")))).to be true
|
88
|
-
expect(Money.new(2_50, "USD").eql?(klass.new(Money.new(2_50, "USD")))).to be true
|
89
|
-
expect(Money.new(2_50, "USD").eql?(klass.new(Money.new(3_00, "USD")))).to be false
|
90
|
-
expect(Money.new(1_00, "GBP").eql?(klass.new(Money.new(1_00, "USD")))).to be false
|
69
|
+
expect(Money.new(1_00, "USD").eql?(klass.new(1_00, "USD"))).to be true
|
70
|
+
expect(Money.new(2_50, "USD").eql?(klass.new(2_50, "USD"))).to be true
|
71
|
+
expect(Money.new(2_50, "USD").eql?(klass.new(3_00, "USD"))).to be false
|
72
|
+
expect(Money.new(1_00, "GBP").eql?(klass.new(1_00, "USD"))).to be false
|
91
73
|
end
|
92
74
|
end
|
93
75
|
|
@@ -112,26 +94,29 @@ describe Money do
|
|
112
94
|
expect(Money.new(100_00, "USD") <=> target).to be > 0
|
113
95
|
end
|
114
96
|
|
115
|
-
it "
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
97
|
+
it "returns nil if currency conversion fails, and therefore cannot be compared" do
|
98
|
+
target = Money.new(200_00, "EUR")
|
99
|
+
expect(target).to receive(:exchange_to).with(Money::Currency.new("USD")).and_raise(Money::Bank::UnknownRate)
|
100
|
+
expect(Money.new(100_00, "USD") <=> target).to be_nil
|
101
|
+
end
|
120
102
|
|
121
|
-
|
122
|
-
|
123
|
-
end
|
124
|
-
end
|
103
|
+
it "can be used to compare with an object that inherits from Money" do
|
104
|
+
klass = Class.new(Money)
|
125
105
|
|
126
|
-
expect(Money.new(1_00) <=> klass.new(
|
127
|
-
expect(Money.new(1_00) <=> klass.new(
|
128
|
-
expect(Money.new(1_00) <=> klass.new(
|
106
|
+
expect(Money.new(1_00) <=> klass.new(1_00)).to eq 0
|
107
|
+
expect(Money.new(1_00) <=> klass.new(99)).to be > 0
|
108
|
+
expect(Money.new(1_00) <=> klass.new(2_00)).to be < 0
|
129
109
|
end
|
130
110
|
|
131
|
-
it "
|
111
|
+
it "raises TypeError when used to compare with an object that doesn't inherit from Money" do
|
112
|
+
expect(Money.new(1_00) <=> 100).to be_nil
|
113
|
+
|
132
114
|
expect(Money.new(1_00) <=> Object.new).to be_nil
|
115
|
+
|
133
116
|
expect(Money.new(1_00) <=> Class).to be_nil
|
117
|
+
|
134
118
|
expect(Money.new(1_00) <=> Kernel).to be_nil
|
119
|
+
|
135
120
|
expect(Money.new(1_00) <=> /foo/).to be_nil
|
136
121
|
end
|
137
122
|
end
|
@@ -586,5 +571,73 @@ describe Money do
|
|
586
571
|
result = 2 * Money.new(4, 'USD')
|
587
572
|
expect(result).to eq Money.new(8, 'USD')
|
588
573
|
end
|
574
|
+
|
575
|
+
it "raises TypeError dividing by a Money (unless other is a Money)" do
|
576
|
+
expect {
|
577
|
+
2 / Money.new(2, 'USD')
|
578
|
+
}.to raise_exception(TypeError)
|
579
|
+
end
|
580
|
+
|
581
|
+
it "raises TypeError subtracting by a Money (unless other is a Money)" do
|
582
|
+
expect {
|
583
|
+
2 - Money.new(2, 'USD')
|
584
|
+
}.to raise_exception(TypeError)
|
585
|
+
end
|
586
|
+
|
587
|
+
it "raises TypeError adding by a Money (unless other is a Money)" do
|
588
|
+
expect {
|
589
|
+
2 + Money.new(2, 'USD')
|
590
|
+
}.to raise_exception(TypeError)
|
591
|
+
end
|
592
|
+
|
593
|
+
it "treats multiplication as commutative" do
|
594
|
+
expect {
|
595
|
+
2 * Money.new(2, 'USD')
|
596
|
+
}.to_not raise_exception
|
597
|
+
result = 2 * Money.new(2, 'USD')
|
598
|
+
expect(result).to eq(Money.new(4, 'USD'))
|
599
|
+
end
|
600
|
+
|
601
|
+
it "doesn't work with non-numerics" do
|
602
|
+
expect {
|
603
|
+
"2" * Money.new(2, 'USD')
|
604
|
+
}.to raise_exception(TypeError)
|
605
|
+
end
|
606
|
+
|
607
|
+
it "correctly handles <=>" do
|
608
|
+
expect {
|
609
|
+
2 < Money.new(2, 'USD')
|
610
|
+
}.to raise_exception(TypeError)
|
611
|
+
|
612
|
+
expect {
|
613
|
+
2 > Money.new(2, 'USD')
|
614
|
+
}.to raise_exception(TypeError)
|
615
|
+
|
616
|
+
expect {
|
617
|
+
2 <= Money.new(2, 'USD')
|
618
|
+
}.to raise_exception(TypeError)
|
619
|
+
|
620
|
+
expect {
|
621
|
+
2 >= Money.new(2, 'USD')
|
622
|
+
}.to raise_exception(TypeError)
|
623
|
+
|
624
|
+
expect {
|
625
|
+
2 <=> Money.new(2, 'USD')
|
626
|
+
}.to raise_exception(TypeError)
|
627
|
+
end
|
628
|
+
|
629
|
+
it "raises exceptions for all numeric types, not just Integer" do
|
630
|
+
expect {
|
631
|
+
2.0 / Money.new(2, 'USD')
|
632
|
+
}.to raise_exception(TypeError)
|
633
|
+
|
634
|
+
expect {
|
635
|
+
Rational(2,3) / Money.new(2, 'USD')
|
636
|
+
}.to raise_exception(TypeError)
|
637
|
+
|
638
|
+
expect {
|
639
|
+
BigDecimal(2) / Money.new(2, 'USD')
|
640
|
+
}.to raise_exception(TypeError)
|
641
|
+
end
|
589
642
|
end
|
590
643
|
end
|