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.
@@ -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 mathmatically split has been performed, left over pennies will
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 recieve more pennies then ones that are listed later
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 money objects have the same currency and the same
15
- # amount. If money objects have a different currency it will only be true
16
- # if the amounts are both zero. Checks against objects that do not respond
17
- # to #to_money, in which case, it will always return false.
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)) #=> false
25
- # Money.new(100).eql?(Money.new(100)) #=> true
26
- # Money.new(0, "USD").eql?(Money.new(0, "EUR")) #=> true
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.respond_to?(:to_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
- def <=>(val)
38
- if val.respond_to?(:to_money)
39
- val = val.to_money unless val.respond_to?(:fractional)
40
- if fractional != 0 && val.fractional != 0 && currency != val.currency
41
- val = val.exchange_to(currency)
42
- end
43
- fractional <=> val.fractional
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 == 0
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 == 0
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
- [self, other]
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!(/#{currency.decimal_mark}/, '.') unless '.' == currency.decimal_mark
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!(/\./, currency.decimal_mark) unless '.' == currency.decimal_mark
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
@@ -1,3 +1,3 @@
1
1
  class Money
2
- VERSION = "6.6.1"
2
+ VERSION = "6.7.0"
3
3
  end
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.2.0"
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
 
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  class Money
4
2
  module Bank
5
3
  describe Base do
@@ -1,5 +1,3 @@
1
- require 'spec_helper'
2
-
3
1
  class Money
4
2
  module Bank
5
3
  describe SingleCurrency do
@@ -1,4 +1,3 @@
1
- require 'spec_helper'
2
1
  require 'json'
3
2
  require 'yaml'
4
3
 
@@ -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
@@ -1,5 +1,4 @@
1
1
  # encoding: utf-8
2
- require 'spec_helper'
3
2
 
4
3
  describe Money::Currency::Loader do
5
4
  class CurrencyLoader
@@ -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": 450, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": ",", "smallest_denomination": 1 }'
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 == 0
343
- Currency.new(:usd).exponent == 2
344
- Currency.new(:iqd).exponent == 3
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 == 1
351
- Currency.new(:usd).decimal_places == 2
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 == 3
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 respond to #to_money" do
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 /foo/
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 responds to #to_money" do
43
- klass = Class.new do
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(Money.new(1_00, "USD"))
54
- expect(Money.new(2_50, "USD")).to eq klass.new(Money.new(2_50, "USD"))
55
- expect(Money.new(2_50, "USD")).not_to eq klass.new(Money.new(3_00, "USD"))
56
- expect(Money.new(1_00, "GBP")).not_to eq klass.new(Money.new(1_00, "USD"))
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 respond to #to_money" do
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 responds to #to_money" do
77
- klass = Class.new do
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
- def to_money
83
- @money
84
- end
85
- end
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 "can be used to compare with an object that responds to #to_money" do
116
- klass = Class.new do
117
- def initialize(money)
118
- @money = money
119
- end
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
- def to_money
122
- @money
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(Money.new(1_00))).to eq 0
127
- expect(Money.new(1_00) <=> klass.new(Money.new(99))).to be > 0
128
- expect(Money.new(1_00) <=> klass.new(Money.new(2_00))).to be < 0
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 "returns nil when used to compare with an object that doesn't respond to #to_money" do
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