money 6.13.5 → 6.14.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -2
- data/README.md +28 -6
- data/config/currency_backwards_compatible.json +4 -0
- data/config/currency_iso.json +101 -8
- data/config/currency_non_iso.json +1 -0
- data/lib/money/bank/variable_exchange.rb +12 -6
- data/lib/money/currency.rb +8 -3
- data/lib/money/money.rb +94 -46
- data/lib/money/money/allocation.rb +10 -4
- data/lib/money/money/arithmetic.rb +18 -13
- data/lib/money/money/formatter.rb +1 -1
- data/lib/money/money/formatting_rules.rb +2 -0
- data/lib/money/rates_store/memory.rb +12 -10
- data/lib/money/version.rb +1 -1
- metadata +3 -3
@@ -113,8 +113,10 @@ class Money
|
|
113
113
|
else
|
114
114
|
if rate = get_rate(from.currency, to_currency)
|
115
115
|
fractional = calculate_fractional(from, to_currency)
|
116
|
-
from.
|
117
|
-
exchange(fractional, rate, &block),
|
116
|
+
from.dup_with(
|
117
|
+
fractional: exchange(fractional, rate, &block),
|
118
|
+
currency: to_currency,
|
119
|
+
bank: self
|
118
120
|
)
|
119
121
|
else
|
120
122
|
raise UnknownRate, "No conversion rate known for '#{from.currency.iso_code}' -> '#{to_currency}'"
|
@@ -217,8 +219,7 @@ class Money
|
|
217
219
|
# s = bank.export_rates(:json)
|
218
220
|
# s #=> "{\"USD_TO_CAD\":1.24515,\"CAD_TO_USD\":0.803115}"
|
219
221
|
def export_rates(format, file = nil, opts = {})
|
220
|
-
raise Money::Bank::UnknownRateFormat unless
|
221
|
-
RATE_FORMATS.include? format
|
222
|
+
raise Money::Bank::UnknownRateFormat unless RATE_FORMATS.include?(format)
|
222
223
|
|
223
224
|
store.transaction do
|
224
225
|
s = FORMAT_SERIALIZERS[format].dump(rates)
|
@@ -258,8 +259,13 @@ class Money
|
|
258
259
|
# bank.get_rate("USD", "CAD") #=> 1.24515
|
259
260
|
# bank.get_rate("CAD", "USD") #=> 0.803115
|
260
261
|
def import_rates(format, s, opts = {})
|
261
|
-
raise Money::Bank::UnknownRateFormat unless
|
262
|
-
|
262
|
+
raise Money::Bank::UnknownRateFormat unless RATE_FORMATS.include?(format)
|
263
|
+
|
264
|
+
if format == :ruby
|
265
|
+
warn '[WARNING] Using :ruby format when importing rates is potentially unsafe and ' \
|
266
|
+
'might lead to remote code execution via Marshal.load deserializer. Consider using ' \
|
267
|
+
'safe alternatives such as :json and :yaml.'
|
268
|
+
end
|
263
269
|
|
264
270
|
store.transaction do
|
265
271
|
data = FORMAT_SERIALIZERS[format].load(s)
|
data/lib/money/currency.rb
CHANGED
@@ -128,7 +128,7 @@ class Money
|
|
128
128
|
# @return [Array]
|
129
129
|
#
|
130
130
|
# @example
|
131
|
-
# Money::Currency.
|
131
|
+
# Money::Currency.all()
|
132
132
|
# [#<Currency ..USD>, 'CAD', 'EUR']...
|
133
133
|
def all
|
134
134
|
table.keys.map do |curr|
|
@@ -206,6 +206,10 @@ class Money
|
|
206
206
|
all.each { |c| yield(c) }
|
207
207
|
end
|
208
208
|
|
209
|
+
def reset!
|
210
|
+
@@instances = {}
|
211
|
+
@table = Loader.load_currencies
|
212
|
+
end
|
209
213
|
|
210
214
|
private
|
211
215
|
|
@@ -253,7 +257,7 @@ class Money
|
|
253
257
|
|
254
258
|
attr_reader :id, :priority, :iso_code, :iso_numeric, :name, :symbol,
|
255
259
|
:disambiguate_symbol, :html_entity, :subunit, :subunit_to_unit, :decimal_mark,
|
256
|
-
:thousands_separator, :symbol_first, :smallest_denomination
|
260
|
+
:thousands_separator, :symbol_first, :smallest_denomination, :format
|
257
261
|
|
258
262
|
alias_method :separator, :decimal_mark
|
259
263
|
alias_method :delimiter, :thousands_separator
|
@@ -341,7 +345,7 @@ class Money
|
|
341
345
|
# @example
|
342
346
|
# Money::Currency.new(:usd) #=> #<Currency id: usd ...>
|
343
347
|
def inspect
|
344
|
-
"#<#{self.class.name} id: #{id}, priority: #{priority}, symbol_first: #{symbol_first}, thousands_separator: #{thousands_separator}, html_entity: #{html_entity}, decimal_mark: #{decimal_mark}, name: #{name}, symbol: #{symbol}, subunit_to_unit: #{subunit_to_unit}, exponent: #{exponent}, iso_code: #{iso_code}, iso_numeric: #{iso_numeric}, subunit: #{subunit}, smallest_denomination: #{smallest_denomination}>"
|
348
|
+
"#<#{self.class.name} id: #{id}, priority: #{priority}, symbol_first: #{symbol_first}, thousands_separator: #{thousands_separator}, html_entity: #{html_entity}, decimal_mark: #{decimal_mark}, name: #{name}, symbol: #{symbol}, subunit_to_unit: #{subunit_to_unit}, exponent: #{exponent}, iso_code: #{iso_code}, iso_numeric: #{iso_numeric}, subunit: #{subunit}, smallest_denomination: #{smallest_denomination}, format: #{format}>"
|
345
349
|
end
|
346
350
|
|
347
351
|
# Returns a string representation corresponding to the upcase +id+
|
@@ -441,6 +445,7 @@ class Money
|
|
441
445
|
@symbol = data[:symbol]
|
442
446
|
@symbol_first = data[:symbol_first]
|
443
447
|
@thousands_separator = data[:thousands_separator]
|
448
|
+
@format = data[:format]
|
444
449
|
end
|
445
450
|
end
|
446
451
|
end
|
data/lib/money/money.rb
CHANGED
@@ -91,38 +91,57 @@ class Money
|
|
91
91
|
class << self
|
92
92
|
|
93
93
|
# @!attribute [rw] default_bank
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
98
|
-
#
|
94
|
+
# Used to set a default bank for currency exchange.
|
95
|
+
#
|
96
|
+
# Each Money object is associated with a bank
|
97
|
+
# object, which is responsible for currency exchange. This property
|
98
|
+
# allows you to specify the default bank object. The default value for
|
99
|
+
# this property is an instance of +Bank::VariableExchange.+ It allows
|
100
|
+
# one to specify custom exchange rates.
|
101
|
+
#
|
102
|
+
# @return [Money::Bank::Base]
|
99
103
|
#
|
100
104
|
# @!attribute default_formatting_rules
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
105
|
+
# Used to define a default hash of rules for every time
|
106
|
+
# +Money#format+ is called. Rules provided on method call will be
|
107
|
+
# merged with the default ones. To overwrite a rule, just provide the
|
108
|
+
# intended value while calling +format+.
|
105
109
|
#
|
106
|
-
# @see
|
110
|
+
# @see Money::Formatter#initialize Money::Formatter for more details
|
107
111
|
#
|
108
112
|
# @example
|
109
113
|
# Money.default_formatting_rules = { display_free: true }
|
110
114
|
# Money.new(0, "USD").format # => "free"
|
111
115
|
# Money.new(0, "USD").format(display_free: false) # => "$0.00"
|
112
116
|
#
|
117
|
+
# @return [Hash]
|
118
|
+
#
|
113
119
|
# @!attribute [rw] use_i18n
|
114
|
-
#
|
115
|
-
# objects in your app.
|
120
|
+
# Used to disable i18n even if it's used by other components of your app.
|
116
121
|
#
|
117
|
-
#
|
118
|
-
#
|
122
|
+
# @return [Boolean]
|
123
|
+
#
|
124
|
+
# @!attribute [rw] default_infinite_precision
|
125
|
+
# @return [Boolean] Use this to enable infinite precision cents as the
|
126
|
+
# global default
|
119
127
|
#
|
120
128
|
# @!attribute [rw] conversion_precision
|
121
|
-
#
|
122
|
-
#
|
123
|
-
|
124
|
-
|
125
|
-
|
129
|
+
# Used to specify precision for converting Rational to BigDecimal
|
130
|
+
#
|
131
|
+
# @return [Integer]
|
132
|
+
attr_accessor :default_formatting_rules, :default_infinite_precision, :conversion_precision
|
133
|
+
attr_reader :use_i18n, :locale_backend
|
134
|
+
attr_writer :default_bank
|
135
|
+
|
136
|
+
def infinite_precision
|
137
|
+
warn '[DEPRECATION] `Money.infinite_precision` is deprecated - use `Money.default_infinite_precision` instead'
|
138
|
+
default_infinite_precision
|
139
|
+
end
|
140
|
+
|
141
|
+
def infinite_precision=(value)
|
142
|
+
warn '[DEPRECATION] `Money.infinite_precision=` is deprecated - use `Money.default_infinite_precision= ` instead'
|
143
|
+
self.default_infinite_precision = value
|
144
|
+
end
|
126
145
|
end
|
127
146
|
|
128
147
|
# @!attribute default_currency
|
@@ -132,8 +151,9 @@ class Money
|
|
132
151
|
# +Money::Currency+ instance.
|
133
152
|
def self.default_currency
|
134
153
|
if @using_deprecated_default_currency
|
135
|
-
warn '[WARNING] The default currency will change to `nil` in the next major release. Make ' \
|
154
|
+
warn '[WARNING] The default currency will change from `USD` to `nil` in the next major release. Make ' \
|
136
155
|
'sure to set it explicitly using `Money.default_currency=` to avoid potential issues'
|
156
|
+
@using_deprecated_default_currency = false
|
137
157
|
end
|
138
158
|
|
139
159
|
if @default_currency.respond_to?(:call)
|
@@ -148,6 +168,14 @@ class Money
|
|
148
168
|
@default_currency = currency
|
149
169
|
end
|
150
170
|
|
171
|
+
def self.default_bank
|
172
|
+
if @default_bank.respond_to?(:call)
|
173
|
+
@default_bank.call
|
174
|
+
else
|
175
|
+
@default_bank
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
151
179
|
def self.locale_backend=(value)
|
152
180
|
@locale_backend = value ? LocaleBackend.find(value) : nil
|
153
181
|
end
|
@@ -183,7 +211,7 @@ class Money
|
|
183
211
|
self.locale_backend = :legacy
|
184
212
|
|
185
213
|
# Default to not using infinite precision cents
|
186
|
-
self.
|
214
|
+
self.default_infinite_precision = false
|
187
215
|
|
188
216
|
# Default to bankers rounding
|
189
217
|
self.rounding_mode = BigDecimal::ROUND_HALF_EVEN
|
@@ -213,19 +241,22 @@ class Money
|
|
213
241
|
return Thread.current[:money_rounding_mode] if Thread.current[:money_rounding_mode]
|
214
242
|
|
215
243
|
if @using_deprecated_default_rounding_mode
|
216
|
-
warn '[WARNING] The default rounding mode will change to `ROUND_HALF_UP` in the
|
217
|
-
'release. Set it explicitly using `Money.rounding_mode=` to avoid potential problems.'
|
244
|
+
warn '[WARNING] The default rounding mode will change from `ROUND_HALF_EVEN` to `ROUND_HALF_UP` in the ' \
|
245
|
+
'next major release. Set it explicitly using `Money.rounding_mode=` to avoid potential problems.'
|
246
|
+
@using_deprecated_default_rounding_mode = false
|
218
247
|
end
|
219
248
|
|
220
249
|
@rounding_mode
|
221
250
|
end
|
222
251
|
|
223
|
-
#
|
224
|
-
# results of the block instead.
|
252
|
+
# Temporarily changes the rounding mode in a given block.
|
225
253
|
#
|
226
254
|
# @param [BigDecimal::ROUND_MODE] mode
|
227
255
|
#
|
228
|
-
# @
|
256
|
+
# @yield The block within which rounding mode will be changed. Its return
|
257
|
+
# value will also be the return value of the whole method.
|
258
|
+
#
|
259
|
+
# @return [Object] block results
|
229
260
|
#
|
230
261
|
# @example
|
231
262
|
# fee = Money.with_rounding_mode(BigDecimal::ROUND_HALF_UP) do
|
@@ -263,7 +294,8 @@ class Money
|
|
263
294
|
#
|
264
295
|
# @param [Numeric] amount The numerical value of the money.
|
265
296
|
# @param [Currency, String, Symbol] currency The currency format.
|
266
|
-
# @param [
|
297
|
+
# @param [Hash] options Optional settings for the new Money instance
|
298
|
+
# @option [Money::Bank::*] :bank The exchange bank to use.
|
267
299
|
#
|
268
300
|
# @example
|
269
301
|
# Money.from_amount(23.45, "USD") # => #<Money fractional:2345 currency:USD>
|
@@ -272,13 +304,12 @@ class Money
|
|
272
304
|
# @return [Money]
|
273
305
|
#
|
274
306
|
# @see #initialize
|
275
|
-
def self.from_amount(amount, currency = default_currency,
|
307
|
+
def self.from_amount(amount, currency = default_currency, options = {})
|
276
308
|
raise ArgumentError, "'amount' must be numeric" unless Numeric === amount
|
277
309
|
|
278
310
|
currency = Currency.wrap(currency) || Money.default_currency
|
279
311
|
value = amount.to_d * currency.subunit_to_unit
|
280
|
-
value
|
281
|
-
new(value, currency, bank)
|
312
|
+
new(value, currency, options)
|
282
313
|
end
|
283
314
|
|
284
315
|
# Creates a new Money object of value given in the
|
@@ -292,7 +323,8 @@ class Money
|
|
292
323
|
# argument, a Money will be created in that currency with fractional value
|
293
324
|
# = 0.
|
294
325
|
# @param [Currency, String, Symbol] currency The currency format.
|
295
|
-
# @param [
|
326
|
+
# @param [Hash] options Optional settings for the new Money instance
|
327
|
+
# @option [Money::Bank::*] :bank The exchange bank to use.
|
296
328
|
#
|
297
329
|
# @return [Money]
|
298
330
|
#
|
@@ -301,11 +333,17 @@ class Money
|
|
301
333
|
# Money.new(100, "USD") #=> #<Money @fractional=100 @currency="USD">
|
302
334
|
# Money.new(100, "EUR") #=> #<Money @fractional=100 @currency="EUR">
|
303
335
|
#
|
304
|
-
def initialize(obj, currency = Money.default_currency,
|
336
|
+
def initialize( obj, currency = Money.default_currency, options = {})
|
337
|
+
# For backwards compatability, if options is not a Hash, treat it as a bank parameter
|
338
|
+
unless options.is_a?(Hash)
|
339
|
+
options = { bank: options }
|
340
|
+
end
|
341
|
+
|
305
342
|
@fractional = as_d(obj.respond_to?(:fractional) ? obj.fractional : obj)
|
306
343
|
@currency = obj.respond_to?(:currency) ? obj.currency : Currency.wrap(currency)
|
307
344
|
@currency ||= Money.default_currency
|
308
|
-
@bank = obj.respond_to?(:bank) ? obj.bank : bank
|
345
|
+
@bank = obj.respond_to?(:bank) ? obj.bank : options[:bank]
|
346
|
+
@bank ||= Money.default_bank
|
309
347
|
|
310
348
|
# BigDecimal can be Infinity and NaN, money of that amount does not make sense
|
311
349
|
raise ArgumentError, 'must be initialized with a finite value' unless @fractional.finite?
|
@@ -454,7 +492,7 @@ class Money
|
|
454
492
|
if !new_currency || currency == new_currency
|
455
493
|
self
|
456
494
|
else
|
457
|
-
|
495
|
+
dup_with(currency: new_currency)
|
458
496
|
end
|
459
497
|
end
|
460
498
|
|
@@ -531,13 +569,15 @@ class Money
|
|
531
569
|
exchange_to("EUR")
|
532
570
|
end
|
533
571
|
|
534
|
-
# Splits a given amount in parts without
|
535
|
-
# distributed round-robin amongst the parties. This means that
|
536
|
-
# receive more pennies than ones
|
572
|
+
# Splits a given amount in parts without losing pennies. The left-over pennies will be
|
573
|
+
# distributed round-robin amongst the parties. This means that parts listed first will likely
|
574
|
+
# receive more pennies than ones listed later.
|
575
|
+
#
|
576
|
+
# Pass [2, 1, 1] as input to give twice as much to part1 as part2 or
|
577
|
+
# part3 which results in 50% of the cash to party1, 25% to part2, and 25% to part3. Passing a
|
578
|
+
# number instead of an array will split the amount evenly (without losing pennies when rounding).
|
537
579
|
#
|
538
|
-
# @param [Array<Numeric>, Numeric]
|
539
|
-
# party3 which results in 50% of the cash to party1, 25% to party2, and 25% to party3. Passing a
|
540
|
-
# number instead of an array will split the amount evenly (without loosing pennies when rounding).
|
580
|
+
# @param [Array<Numeric>, Numeric] parts how amount should be distributed to parts
|
541
581
|
#
|
542
582
|
# @return [Array<Money>]
|
543
583
|
#
|
@@ -548,8 +588,8 @@ class Money
|
|
548
588
|
# Money.new(100, "USD").allocate(3) #=> [Money.new(34), Money.new(33), Money.new(33)]
|
549
589
|
#
|
550
590
|
def allocate(parts)
|
551
|
-
amounts = Money::Allocation.generate(fractional, parts, !Money.
|
552
|
-
amounts.map { |amount|
|
591
|
+
amounts = Money::Allocation.generate(fractional, parts, !Money.default_infinite_precision)
|
592
|
+
amounts.map { |amount| dup_with(fractional: amount) }
|
553
593
|
end
|
554
594
|
alias_method :split, :allocate
|
555
595
|
|
@@ -566,16 +606,16 @@ class Money
|
|
566
606
|
# Money.new(10.1, 'USD').round #=> Money.new(10, 'USD')
|
567
607
|
#
|
568
608
|
# @see
|
569
|
-
# Money.
|
609
|
+
# Money.default_infinite_precision
|
570
610
|
#
|
571
611
|
def round(rounding_mode = self.class.rounding_mode, rounding_precision = 0)
|
572
612
|
rounded_amount = as_d(@fractional).round(rounding_precision, rounding_mode)
|
573
|
-
|
613
|
+
dup_with(fractional: rounded_amount)
|
574
614
|
end
|
575
615
|
|
576
616
|
# Creates a formatted price string according to several rules.
|
577
617
|
#
|
578
|
-
# @param [Hash] See Money::Formatter for the list of formatting options
|
618
|
+
# @param [Hash] rules See {Money::Formatter Money::Formatter} for the list of formatting options
|
579
619
|
#
|
580
620
|
# @return [String]
|
581
621
|
#
|
@@ -601,6 +641,14 @@ class Money
|
|
601
641
|
Money::Formatter::DEFAULTS[:decimal_mark]
|
602
642
|
end
|
603
643
|
|
644
|
+
def dup_with(options = {})
|
645
|
+
self.class.new(
|
646
|
+
options[:fractional] || fractional,
|
647
|
+
options[:currency] || currency,
|
648
|
+
bank: options[:bank] || bank
|
649
|
+
)
|
650
|
+
end
|
651
|
+
|
604
652
|
private
|
605
653
|
|
606
654
|
def as_d(num)
|
@@ -612,7 +660,7 @@ class Money
|
|
612
660
|
end
|
613
661
|
|
614
662
|
def return_value(value)
|
615
|
-
if self.class.
|
663
|
+
if self.class.default_infinite_precision
|
616
664
|
value
|
617
665
|
else
|
618
666
|
value.round(0, self.class.rounding_mode).to_i
|
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
class Money
|
4
4
|
class Allocation
|
5
|
-
# Splits a given amount in parts without
|
6
|
-
# The left-over pennies will be distributed round-robin amongst the
|
7
|
-
#
|
5
|
+
# Splits a given amount in parts without losing pennies.
|
6
|
+
# The left-over pennies will be distributed round-robin amongst the parts. This means that
|
7
|
+
# parts listed first will likely receive more pennies than the ones listed later.
|
8
8
|
#
|
9
9
|
# The results should always add up to the original amount.
|
10
10
|
#
|
@@ -13,7 +13,13 @@ class Money
|
|
13
13
|
# Array<Numeric> — allocates the amounts proportionally to the given array
|
14
14
|
#
|
15
15
|
def self.generate(amount, parts, whole_amounts = true)
|
16
|
-
parts = parts.is_a?(Numeric)
|
16
|
+
parts = if parts.is_a?(Numeric)
|
17
|
+
Array.new(parts, 1)
|
18
|
+
elsif parts.all?(&:zero?)
|
19
|
+
Array.new(parts.count, 1)
|
20
|
+
else
|
21
|
+
parts.dup
|
22
|
+
end
|
17
23
|
|
18
24
|
raise ArgumentError, 'need at least one party' if parts.empty?
|
19
25
|
|
@@ -16,7 +16,7 @@ class Money
|
|
16
16
|
# @example
|
17
17
|
# - Money.new(100) #=> #<Money @fractional=-100>
|
18
18
|
def -@
|
19
|
-
|
19
|
+
dup_with(fractional: -fractional)
|
20
20
|
end
|
21
21
|
|
22
22
|
# Checks whether two Money objects have the same currency and the same
|
@@ -46,7 +46,7 @@ class Money
|
|
46
46
|
# Compares two Money objects. If money objects have a different currency it
|
47
47
|
# will attempt to convert the currency.
|
48
48
|
#
|
49
|
-
# @param [Money]
|
49
|
+
# @param [Money] other Value to compare with.
|
50
50
|
#
|
51
51
|
# @return [Integer]
|
52
52
|
#
|
@@ -57,7 +57,12 @@ class Money
|
|
57
57
|
return unless other.respond_to?(:zero?) && other.zero?
|
58
58
|
return other.is_a?(CoercedNumeric) ? 0 <=> fractional : fractional <=> 0
|
59
59
|
end
|
60
|
-
|
60
|
+
|
61
|
+
# Always allow comparison with zero
|
62
|
+
if zero? || other.zero?
|
63
|
+
return fractional <=> other.fractional
|
64
|
+
end
|
65
|
+
|
61
66
|
other = other.exchange_to(currency)
|
62
67
|
fractional <=> other.fractional
|
63
68
|
rescue Money::Bank::UnknownRate
|
@@ -103,7 +108,7 @@ class Money
|
|
103
108
|
# values. If +other_money+ has a different currency then its monetary value
|
104
109
|
# is automatically exchanged to this object's currency using +exchange_to+.
|
105
110
|
#
|
106
|
-
# @param [Money]
|
111
|
+
# @param [Money] other Other +Money+ object to add.
|
107
112
|
#
|
108
113
|
# @return [Money]
|
109
114
|
#
|
@@ -116,7 +121,7 @@ class Money
|
|
116
121
|
# its monetary value is automatically exchanged to this object's currency
|
117
122
|
# using +exchange_to+.
|
118
123
|
#
|
119
|
-
# @param [Money]
|
124
|
+
# @param [Money] other Other +Money+ object to subtract.
|
120
125
|
#
|
121
126
|
# @return [Money]
|
122
127
|
#
|
@@ -132,10 +137,10 @@ class Money
|
|
132
137
|
when Money
|
133
138
|
other = other.exchange_to(currency)
|
134
139
|
new_fractional = fractional.public_send(op, other.fractional)
|
135
|
-
|
140
|
+
dup_with(fractional: new_fractional)
|
136
141
|
when CoercedNumeric
|
137
142
|
raise TypeError, non_zero_message.call(other.value) unless other.zero?
|
138
|
-
|
143
|
+
dup_with(fractional: other.value.public_send(op, fractional))
|
139
144
|
when Numeric
|
140
145
|
raise TypeError, non_zero_message.call(other) unless other.zero?
|
141
146
|
self
|
@@ -162,7 +167,7 @@ class Money
|
|
162
167
|
def *(value)
|
163
168
|
value = value.value if value.is_a?(CoercedNumeric)
|
164
169
|
if value.is_a? Numeric
|
165
|
-
|
170
|
+
dup_with(fractional: fractional * value)
|
166
171
|
else
|
167
172
|
raise TypeError, "Can't multiply a #{self.class.name} by a #{value.class.name}'s value"
|
168
173
|
end
|
@@ -188,7 +193,7 @@ class Money
|
|
188
193
|
fractional / as_d(value.exchange_to(currency).fractional).to_f
|
189
194
|
else
|
190
195
|
raise TypeError, 'Can not divide by Money' if value.is_a?(CoercedNumeric)
|
191
|
-
|
196
|
+
dup_with(fractional: fractional / as_d(value))
|
192
197
|
end
|
193
198
|
end
|
194
199
|
|
@@ -226,13 +231,13 @@ class Money
|
|
226
231
|
def divmod_money(val)
|
227
232
|
cents = val.exchange_to(currency).cents
|
228
233
|
quotient, remainder = fractional.divmod(cents)
|
229
|
-
[quotient,
|
234
|
+
[quotient, dup_with(fractional: remainder)]
|
230
235
|
end
|
231
236
|
private :divmod_money
|
232
237
|
|
233
238
|
def divmod_other(val)
|
234
239
|
quotient, remainder = fractional.divmod(as_d(val))
|
235
|
-
[
|
240
|
+
[dup_with(fractional: quotient), dup_with(fractional: remainder)]
|
236
241
|
end
|
237
242
|
private :divmod_other
|
238
243
|
|
@@ -276,7 +281,7 @@ class Money
|
|
276
281
|
if (fractional < 0 && val < 0) || (fractional > 0 && val > 0)
|
277
282
|
self.modulo(val)
|
278
283
|
else
|
279
|
-
self.modulo(val) - (val.is_a?(Money) ? val :
|
284
|
+
self.modulo(val) - (val.is_a?(Money) ? val : dup_with(fractional: val))
|
280
285
|
end
|
281
286
|
end
|
282
287
|
|
@@ -287,7 +292,7 @@ class Money
|
|
287
292
|
# @example
|
288
293
|
# Money.new(-100).abs #=> #<Money @fractional=100>
|
289
294
|
def abs
|
290
|
-
|
295
|
+
dup_with(fractional: fractional.abs)
|
291
296
|
end
|
292
297
|
|
293
298
|
# Test if the money amount is zero.
|