money 6.13.5 → 6.14.1
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/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.
|