money 6.6.1 → 6.7.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.
- 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
|