money 6.7.1 → 6.13.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/.rspec +2 -1
- data/.travis.yml +15 -6
- data/AUTHORS +5 -0
- data/CHANGELOG.md +98 -3
- data/Gemfile +13 -4
- data/LICENSE +2 -0
- data/README.md +64 -44
- data/config/currency_backwards_compatible.json +30 -0
- data/config/currency_iso.json +135 -59
- data/config/currency_non_iso.json +66 -2
- data/lib/money.rb +0 -13
- data/lib/money/bank/variable_exchange.rb +9 -22
- data/lib/money/currency.rb +33 -39
- data/lib/money/currency/heuristics.rb +1 -144
- data/lib/money/currency/loader.rb +1 -1
- data/lib/money/locale_backend/base.rb +7 -0
- data/lib/money/locale_backend/errors.rb +6 -0
- data/lib/money/locale_backend/i18n.rb +24 -0
- data/lib/money/locale_backend/legacy.rb +28 -0
- data/lib/money/money.rb +106 -139
- data/lib/money/money/allocation.rb +37 -0
- data/lib/money/money/arithmetic.rb +31 -28
- data/lib/money/money/constructors.rb +1 -2
- data/lib/money/money/formatter.rb +397 -0
- data/lib/money/money/formatting_rules.rb +120 -0
- data/lib/money/money/locale_backend.rb +20 -0
- data/lib/money/rates_store/memory.rb +1 -2
- data/lib/money/version.rb +1 -1
- data/money.gemspec +10 -16
- data/spec/bank/variable_exchange_spec.rb +7 -3
- data/spec/currency/heuristics_spec.rb +2 -153
- data/spec/currency_spec.rb +44 -3
- data/spec/locale_backend/i18n_spec.rb +62 -0
- data/spec/locale_backend/legacy_spec.rb +74 -0
- data/spec/money/allocation_spec.rb +130 -0
- data/spec/money/arithmetic_spec.rb +184 -90
- data/spec/money/constructors_spec.rb +0 -12
- data/spec/money/formatting_spec.rb +296 -179
- data/spec/money/locale_backend_spec.rb +14 -0
- data/spec/money_spec.rb +159 -26
- data/spec/rates_store/memory_spec.rb +13 -2
- data/spec/spec_helper.rb +2 -0
- data/spec/support/shared_examples/money_examples.rb +14 -0
- metadata +32 -40
- data/lib/money/money/formatting.rb +0 -418
data/lib/money/money.rb
CHANGED
@@ -3,7 +3,9 @@ require "money/bank/variable_exchange"
|
|
3
3
|
require "money/bank/single_currency"
|
4
4
|
require "money/money/arithmetic"
|
5
5
|
require "money/money/constructors"
|
6
|
-
require "money/money/
|
6
|
+
require "money/money/formatter"
|
7
|
+
require "money/money/allocation"
|
8
|
+
require "money/money/locale_backend"
|
7
9
|
|
8
10
|
# "Money is any object or record that is generally accepted as payment for
|
9
11
|
# goods and services and repayment of debts in a given socio-economic context
|
@@ -17,7 +19,6 @@ require "money/money/formatting"
|
|
17
19
|
class Money
|
18
20
|
include Comparable
|
19
21
|
include Money::Arithmetic
|
20
|
-
include Money::Formatting
|
21
22
|
extend Constructors
|
22
23
|
|
23
24
|
# Raised when smallest denomination of a currency is not defined
|
@@ -105,9 +106,9 @@ class Money
|
|
105
106
|
# @see +Money::Formatting#format+ for more details.
|
106
107
|
#
|
107
108
|
# @example
|
108
|
-
# Money.default_formatting_rules = { :
|
109
|
+
# Money.default_formatting_rules = { display_free: true }
|
109
110
|
# Money.new(0, "USD").format # => "free"
|
110
|
-
# Money.new(0, "USD").format(:
|
111
|
+
# Money.new(0, "USD").format(display_free: false) # => "$0.00"
|
111
112
|
#
|
112
113
|
# @!attribute [rw] use_i18n
|
113
114
|
# @return [Boolean] Use this to disable i18n even if it's used by other
|
@@ -117,10 +118,11 @@ class Money
|
|
117
118
|
# @return [Boolean] Use this to enable infinite precision cents
|
118
119
|
#
|
119
120
|
# @!attribute [rw] conversion_precision
|
120
|
-
# @return [
|
121
|
+
# @return [Integer] Use this to specify precision for converting Rational
|
121
122
|
# to BigDecimal
|
122
123
|
attr_accessor :default_bank, :default_formatting_rules,
|
123
|
-
:use_i18n, :infinite_precision, :conversion_precision
|
124
|
+
:use_i18n, :infinite_precision, :conversion_precision,
|
125
|
+
:locale_backend
|
124
126
|
|
125
127
|
# @attr_writer rounding_mode Use this to specify the rounding mode
|
126
128
|
#
|
@@ -141,6 +143,18 @@ class Money
|
|
141
143
|
end
|
142
144
|
end
|
143
145
|
|
146
|
+
def self.locale_backend=(value)
|
147
|
+
@locale_backend = value ? LocaleBackend.find(value) : nil
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.use_i18n=(value)
|
151
|
+
if value
|
152
|
+
warn '[DEPRECATION] `use_i18n` is deprecated - use `Money.locale_backend = :i18n` instead'
|
153
|
+
end
|
154
|
+
|
155
|
+
@use_i18n = value
|
156
|
+
end
|
157
|
+
|
144
158
|
def self.setup_defaults
|
145
159
|
# Set the default bank for creating new +Money+ objects.
|
146
160
|
self.default_bank = Bank::VariableExchange.instance
|
@@ -149,7 +163,10 @@ class Money
|
|
149
163
|
self.default_currency = Currency.new("USD")
|
150
164
|
|
151
165
|
# Default to using i18n
|
152
|
-
|
166
|
+
@use_i18n = true
|
167
|
+
|
168
|
+
# Default to using legacy locale backend
|
169
|
+
self.locale_backend = :legacy
|
153
170
|
|
154
171
|
# Default to not using infinite precision cents
|
155
172
|
self.infinite_precision = false
|
@@ -177,9 +194,9 @@ class Money
|
|
177
194
|
#
|
178
195
|
# @example
|
179
196
|
# fee = Money.rounding_mode(BigDecimal::ROUND_HALF_UP) do
|
180
|
-
# Money.new(1200) * BigDecimal
|
197
|
+
# Money.new(1200) * BigDecimal('0.029')
|
181
198
|
# end
|
182
|
-
def self.rounding_mode(mode=nil)
|
199
|
+
def self.rounding_mode(mode = nil)
|
183
200
|
if mode.nil?
|
184
201
|
Thread.current[:money_rounding_mode] || @rounding_mode
|
185
202
|
else
|
@@ -228,8 +245,9 @@ class Money
|
|
228
245
|
#
|
229
246
|
# @see #initialize
|
230
247
|
def self.from_amount(amount, currency = default_currency, bank = default_bank)
|
231
|
-
|
232
|
-
|
248
|
+
raise ArgumentError, "'amount' must be numeric" unless Numeric === amount
|
249
|
+
|
250
|
+
currency = Currency.wrap(currency) || Money.default_currency
|
233
251
|
value = amount.to_d * currency.subunit_to_unit
|
234
252
|
value = value.round(0, rounding_mode) unless infinite_precision
|
235
253
|
new(value, currency, bank)
|
@@ -271,7 +289,7 @@ class Money
|
|
271
289
|
# @return [BigDecimal]
|
272
290
|
#
|
273
291
|
# @example
|
274
|
-
# Money.new(1_00, "USD").dollars # => BigDecimal
|
292
|
+
# Money.new(1_00, "USD").dollars # => BigDecimal("1.00")
|
275
293
|
#
|
276
294
|
# @see #amount
|
277
295
|
# @see #to_d
|
@@ -286,7 +304,7 @@ class Money
|
|
286
304
|
# @return [BigDecimal]
|
287
305
|
#
|
288
306
|
# @example
|
289
|
-
# Money.new(1_00, "USD").amount # => BigDecimal
|
307
|
+
# Money.new(1_00, "USD").amount # => BigDecimal("1.00")
|
290
308
|
#
|
291
309
|
# @see #to_d
|
292
310
|
# @see #fractional
|
@@ -302,6 +320,7 @@ class Money
|
|
302
320
|
# @example
|
303
321
|
# Money.new(100, :USD).currency_as_string #=> "USD"
|
304
322
|
def currency_as_string
|
323
|
+
warn "[DEPRECATION] `currency_as_string` is deprecated. Please use `.currency.to_s` instead."
|
305
324
|
currency.to_s
|
306
325
|
end
|
307
326
|
|
@@ -314,13 +333,15 @@ class Money
|
|
314
333
|
# @example
|
315
334
|
# Money.new(100).currency_as_string("CAD") #=> #<Money::Currency id: cad>
|
316
335
|
def currency_as_string=(val)
|
336
|
+
warn "[DEPRECATION] `currency_as_string=` is deprecated - Money instances are immutable." \
|
337
|
+
" Please use `with_currency` instead."
|
317
338
|
@currency = Currency.wrap(val)
|
318
339
|
end
|
319
340
|
|
320
|
-
# Returns a
|
341
|
+
# Returns a Integer hash value based on the +fractional+ and +currency+ attributes
|
321
342
|
# in order to use functions like & (intersection), group_by, etc.
|
322
343
|
#
|
323
|
-
# @return [
|
344
|
+
# @return [Integer]
|
324
345
|
#
|
325
346
|
# @example
|
326
347
|
# Money.new(100).hash #=> 908351
|
@@ -352,19 +373,10 @@ class Money
|
|
352
373
|
# @example
|
353
374
|
# Money.ca_dollar(100).to_s #=> "1.00"
|
354
375
|
def to_s
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
unit
|
360
|
-
else
|
361
|
-
"#{unit}#{decimal_mark}#{fraction}"
|
362
|
-
end
|
363
|
-
else
|
364
|
-
"#{unit}#{decimal_mark}#{pad_subunit(subunit)}#{fraction}"
|
365
|
-
end
|
366
|
-
|
367
|
-
fractional < 0 ? "-#{str}" : str
|
376
|
+
format thousands_separator: '',
|
377
|
+
no_cents_if_whole: currency.decimal_places == 0,
|
378
|
+
symbol: false,
|
379
|
+
ignore_defaults: true
|
368
380
|
end
|
369
381
|
|
370
382
|
# Return the amount of money as a BigDecimal.
|
@@ -372,7 +384,7 @@ class Money
|
|
372
384
|
# @return [BigDecimal]
|
373
385
|
#
|
374
386
|
# @example
|
375
|
-
# Money.us_dollar(1_00).to_d #=> BigDecimal
|
387
|
+
# Money.us_dollar(1_00).to_d #=> BigDecimal("1.00")
|
376
388
|
def to_d
|
377
389
|
as_d(fractional) / as_d(currency.subunit_to_unit)
|
378
390
|
end
|
@@ -400,7 +412,22 @@ class Money
|
|
400
412
|
to_d.to_f
|
401
413
|
end
|
402
414
|
|
403
|
-
#
|
415
|
+
# Returns a new Money instance in a given currency leaving the amount intact
|
416
|
+
# and not performing currency conversion.
|
417
|
+
#
|
418
|
+
# @param [Currency, String, Symbol] new_currency Currency of the new object.
|
419
|
+
#
|
420
|
+
# @return [self]
|
421
|
+
def with_currency(new_currency)
|
422
|
+
new_currency = Currency.wrap(new_currency)
|
423
|
+
if !new_currency || currency == new_currency
|
424
|
+
self
|
425
|
+
else
|
426
|
+
self.class.new(fractional, new_currency, bank)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
# Conversion to +self+.
|
404
431
|
#
|
405
432
|
# @return [self]
|
406
433
|
def to_money(given_currency = nil)
|
@@ -473,52 +500,27 @@ class Money
|
|
473
500
|
exchange_to("EUR")
|
474
501
|
end
|
475
502
|
|
476
|
-
#
|
477
|
-
#
|
478
|
-
#
|
479
|
-
# listed first will likely receive more pennies than ones that are listed later
|
503
|
+
# Splits a given amount in parts without loosing pennies. The left-over pennies will be
|
504
|
+
# distributed round-robin amongst the parties. This means that parties listed first will likely
|
505
|
+
# receive more pennies than ones that are listed later.
|
480
506
|
#
|
481
|
-
# @param [Array<Numeric
|
507
|
+
# @param [Array<Numeric>, Numeric] pass [2, 1, 1] to give twice as much to party1 as party2 or
|
508
|
+
# party3 which results in 50% of the cash to party1, 25% to party2, and 25% to party3. Passing a
|
509
|
+
# number instead of an array will split the amount evenly (without loosing pennies when rounding).
|
482
510
|
#
|
483
511
|
# @return [Array<Money>]
|
484
512
|
#
|
485
513
|
# @example
|
486
|
-
# Money.new(5, "USD").allocate([
|
487
|
-
# Money.new(100, "USD").allocate([
|
488
|
-
#
|
489
|
-
|
490
|
-
allocations = allocations_from_splits(splits)
|
491
|
-
|
492
|
-
if (allocations - BigDecimal("1")) > Float::EPSILON
|
493
|
-
raise ArgumentError, "splits add to more then 100%"
|
494
|
-
end
|
495
|
-
|
496
|
-
amounts, left_over = amounts_from_splits(allocations, splits)
|
497
|
-
|
498
|
-
unless self.class.infinite_precision
|
499
|
-
left_over.to_i.times { |i| amounts[i % amounts.length] += 1 }
|
500
|
-
end
|
501
|
-
|
502
|
-
amounts.collect { |fractional| self.class.new(fractional, currency) }
|
503
|
-
end
|
504
|
-
|
505
|
-
# Split money amongst parties evenly without losing pennies.
|
506
|
-
#
|
507
|
-
# @param [Numeric] num number of parties.
|
514
|
+
# Money.new(5, "USD").allocate([3, 7]) #=> [Money.new(2), Money.new(3)]
|
515
|
+
# Money.new(100, "USD").allocate([1, 1, 1]) #=> [Money.new(34), Money.new(33), Money.new(33)]
|
516
|
+
# Money.new(100, "USD").allocate(2) #=> [Money.new(50), Money.new(50)]
|
517
|
+
# Money.new(100, "USD").allocate(3) #=> [Money.new(34), Money.new(33), Money.new(33)]
|
508
518
|
#
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
# Money.new(100, "USD").split(3) #=> [Money.new(34), Money.new(33), Money.new(33)]
|
513
|
-
def split(num)
|
514
|
-
raise ArgumentError, "need at least one party" if num < 1
|
515
|
-
|
516
|
-
if self.class.infinite_precision
|
517
|
-
split_infinite(num)
|
518
|
-
else
|
519
|
-
split_flat(num)
|
520
|
-
end
|
519
|
+
def allocate(parts)
|
520
|
+
amounts = Money::Allocation.generate(fractional, parts, !Money.infinite_precision)
|
521
|
+
amounts.map { |amount| self.class.new(amount, currency) }
|
521
522
|
end
|
523
|
+
alias_method :split, :allocate
|
522
524
|
|
523
525
|
# Round the monetary amount to smallest unit of coinage.
|
524
526
|
#
|
@@ -535,85 +537,46 @@ class Money
|
|
535
537
|
# @see
|
536
538
|
# Money.infinite_precision
|
537
539
|
#
|
538
|
-
def round(rounding_mode = self.class.rounding_mode)
|
539
|
-
|
540
|
-
|
541
|
-
else
|
542
|
-
self
|
543
|
-
end
|
544
|
-
end
|
545
|
-
|
546
|
-
private
|
547
|
-
|
548
|
-
def as_d(num)
|
549
|
-
if num.respond_to?(:to_d)
|
550
|
-
num.is_a?(Rational) ? num.to_d(self.class.conversion_precision) : num.to_d
|
551
|
-
else
|
552
|
-
BigDecimal.new(num.to_s)
|
553
|
-
end
|
540
|
+
def round(rounding_mode = self.class.rounding_mode, rounding_precision = 0)
|
541
|
+
rounded_amount = as_d(@fractional).round(rounding_precision, rounding_mode)
|
542
|
+
self.class.new(rounded_amount, currency, bank)
|
554
543
|
end
|
555
544
|
|
556
|
-
|
557
|
-
|
558
|
-
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
end
|
565
|
-
|
566
|
-
def strings_for_infinite_precision(unit, subunit)
|
567
|
-
subunit, fraction = subunit.divmod(BigDecimal("1"))
|
568
|
-
fraction = fraction.to_s("F")[2..-1] # want fractional part "0.xxx"
|
569
|
-
fraction = "" if fraction =~ /^0+$/
|
570
|
-
|
571
|
-
[unit.to_i.to_s, subunit.to_i.to_s, fraction]
|
572
|
-
end
|
573
|
-
|
574
|
-
def strings_for_base_precision(unit, subunit)
|
575
|
-
[unit.to_s, subunit.to_s, ""]
|
576
|
-
end
|
577
|
-
|
578
|
-
def pad_subunit(subunit)
|
579
|
-
cnt = currency.decimal_places
|
580
|
-
padding = "0" * cnt
|
581
|
-
"#{padding}#{subunit}"[-1 * cnt, cnt]
|
582
|
-
end
|
583
|
-
|
584
|
-
def allocations_from_splits(splits)
|
585
|
-
splits.inject(0) { |sum, n| sum + as_d(n) }
|
545
|
+
# Creates a formatted price string according to several rules.
|
546
|
+
#
|
547
|
+
# @param [Hash] See Money::Formatter for the list of formatting options
|
548
|
+
#
|
549
|
+
# @return [String]
|
550
|
+
#
|
551
|
+
def format(*rules)
|
552
|
+
Money::Formatter.new(self, *rules).to_s
|
586
553
|
end
|
587
554
|
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
(fractional * ratio / allocations).floor.tap do |frac|
|
596
|
-
left_over -= frac
|
597
|
-
end
|
598
|
-
end
|
599
|
-
end
|
600
|
-
|
601
|
-
[amounts, left_over]
|
555
|
+
# Returns a thousands separator according to the locale
|
556
|
+
#
|
557
|
+
# @return [String]
|
558
|
+
#
|
559
|
+
def thousands_separator
|
560
|
+
(locale_backend && locale_backend.lookup(:thousands_separator, currency)) ||
|
561
|
+
Money::Formatter::DEFAULTS[:thousands_separator]
|
602
562
|
end
|
603
563
|
|
604
|
-
|
605
|
-
|
606
|
-
|
564
|
+
# Returns a decimal mark according to the locale
|
565
|
+
#
|
566
|
+
# @return [String]
|
567
|
+
#
|
568
|
+
def decimal_mark
|
569
|
+
(locale_backend && locale_backend.lookup(:decimal_mark, currency)) ||
|
570
|
+
Money::Formatter::DEFAULTS[:decimal_mark]
|
607
571
|
end
|
608
572
|
|
609
|
-
|
610
|
-
low = self.class.new(fractional / num, currency)
|
611
|
-
high = self.class.new(low.fractional + 1, currency)
|
612
|
-
|
613
|
-
remainder = fractional % num
|
573
|
+
private
|
614
574
|
|
615
|
-
|
616
|
-
|
575
|
+
def as_d(num)
|
576
|
+
if num.respond_to?(:to_d)
|
577
|
+
num.is_a?(Rational) ? num.to_d(self.class.conversion_precision) : num.to_d
|
578
|
+
else
|
579
|
+
BigDecimal(num.to_s.empty? ? 0 : num.to_s)
|
617
580
|
end
|
618
581
|
end
|
619
582
|
|
@@ -624,4 +587,8 @@ class Money
|
|
624
587
|
value.round(0, self.class.rounding_mode).to_i
|
625
588
|
end
|
626
589
|
end
|
590
|
+
|
591
|
+
def locale_backend
|
592
|
+
self.class.locale_backend
|
593
|
+
end
|
627
594
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class Money
|
4
|
+
class Allocation
|
5
|
+
# Splits a given amount in parts without loosing pennies.
|
6
|
+
# The left-over pennies will be distributed round-robin amongst the parties. This means that
|
7
|
+
# parties listed first will likely receive more pennies than ones that are listed later.
|
8
|
+
#
|
9
|
+
# The results should always add up to the original amount.
|
10
|
+
#
|
11
|
+
# The parts can be specified as:
|
12
|
+
# Numeric — performs the split between a given number of parties evenely
|
13
|
+
# Array<Numeric> — allocates the amounts proportionally to the given array
|
14
|
+
#
|
15
|
+
def self.generate(amount, parts, whole_amounts = true)
|
16
|
+
parts = parts.is_a?(Numeric) ? Array.new(parts, 1) : parts.dup
|
17
|
+
|
18
|
+
raise ArgumentError, 'need at least one party' if parts.empty?
|
19
|
+
|
20
|
+
result = []
|
21
|
+
remaining_amount = amount
|
22
|
+
|
23
|
+
until parts.empty? do
|
24
|
+
parts_sum = parts.inject(0, :+)
|
25
|
+
part = parts.pop
|
26
|
+
|
27
|
+
current_split = remaining_amount * part / parts_sum
|
28
|
+
current_split = current_split.truncate if whole_amounts
|
29
|
+
|
30
|
+
result.unshift current_split
|
31
|
+
remaining_amount -= current_split
|
32
|
+
end
|
33
|
+
|
34
|
+
result
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -16,7 +16,7 @@ class Money
|
|
16
16
|
# @example
|
17
17
|
# - Money.new(100) #=> #<Money @fractional=-100>
|
18
18
|
def -@
|
19
|
-
self.class.new(-fractional, currency)
|
19
|
+
self.class.new(-fractional, currency, bank)
|
20
20
|
end
|
21
21
|
|
22
22
|
# Checks whether two Money objects have the same currency and the same
|
@@ -48,16 +48,17 @@ class Money
|
|
48
48
|
#
|
49
49
|
# @param [Money] other_money Value to compare with.
|
50
50
|
#
|
51
|
-
# @return [
|
51
|
+
# @return [Integer]
|
52
52
|
#
|
53
53
|
# @raise [TypeError] when other object is not Money
|
54
54
|
#
|
55
55
|
def <=>(other)
|
56
|
-
|
56
|
+
unless other.is_a?(Money)
|
57
|
+
return unless other.respond_to?(:zero?) && other.zero?
|
57
58
|
return other.is_a?(CoercedNumeric) ? 0 <=> fractional : fractional <=> 0
|
58
59
|
end
|
59
|
-
return
|
60
|
-
other = other.exchange_to(currency)
|
60
|
+
return 0 if zero? && other.zero?
|
61
|
+
other = other.exchange_to(currency)
|
61
62
|
fractional <=> other.fractional
|
62
63
|
rescue Money::Bank::UnknownRate
|
63
64
|
end
|
@@ -97,6 +98,7 @@ class Money
|
|
97
98
|
fractional < 0
|
98
99
|
end
|
99
100
|
|
101
|
+
# @method +(other)
|
100
102
|
# Returns a new Money object containing the sum of the two operands' monetary
|
101
103
|
# values. If +other_money+ has a different currency then its monetary value
|
102
104
|
# is automatically exchanged to this object's currency using +exchange_to+.
|
@@ -107,13 +109,8 @@ class Money
|
|
107
109
|
#
|
108
110
|
# @example
|
109
111
|
# Money.new(100) + Money.new(100) #=> #<Money @fractional=200>
|
110
|
-
|
111
|
-
|
112
|
-
raise TypeError unless other_money.is_a?(Money)
|
113
|
-
other_money = other_money.exchange_to(currency)
|
114
|
-
self.class.new(fractional + other_money.fractional, currency)
|
115
|
-
end
|
116
|
-
|
112
|
+
#
|
113
|
+
# @method -(other)
|
117
114
|
# Returns a new Money object containing the difference between the two
|
118
115
|
# operands' monetary values. If +other_money+ has a different currency then
|
119
116
|
# its monetary value is automatically exchanged to this object's currency
|
@@ -125,11 +122,17 @@ class Money
|
|
125
122
|
#
|
126
123
|
# @example
|
127
124
|
# Money.new(100) - Money.new(99) #=> #<Money @fractional=1>
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
125
|
+
[:+, :-].each do |op|
|
126
|
+
define_method(op) do |other|
|
127
|
+
unless other.is_a?(Money)
|
128
|
+
if other.zero?
|
129
|
+
return other.is_a?(CoercedNumeric) ? Money.empty(currency).public_send(op, self) : self
|
130
|
+
end
|
131
|
+
raise TypeError
|
132
|
+
end
|
133
|
+
other = other.exchange_to(currency)
|
134
|
+
self.class.new(fractional.public_send(op, other.fractional), currency, bank)
|
135
|
+
end
|
133
136
|
end
|
134
137
|
|
135
138
|
# Multiplies the monetary value with the given number and returns a new
|
@@ -149,7 +152,7 @@ class Money
|
|
149
152
|
def *(value)
|
150
153
|
value = value.value if value.is_a?(CoercedNumeric)
|
151
154
|
if value.is_a? Numeric
|
152
|
-
self.class.new(fractional * value, currency)
|
155
|
+
self.class.new(fractional * value, currency, bank)
|
153
156
|
else
|
154
157
|
raise TypeError, "Can't multiply a #{self.class.name} by a #{value.class.name}'s value"
|
155
158
|
end
|
@@ -175,7 +178,7 @@ class Money
|
|
175
178
|
fractional / as_d(value.exchange_to(currency).fractional).to_f
|
176
179
|
else
|
177
180
|
raise TypeError, 'Can not divide by Money' if value.is_a?(CoercedNumeric)
|
178
|
-
self.class.new(fractional / as_d(value), currency)
|
181
|
+
self.class.new(fractional / as_d(value), currency, bank)
|
179
182
|
end
|
180
183
|
end
|
181
184
|
|
@@ -195,9 +198,9 @@ class Money
|
|
195
198
|
# Divide money by money or fixnum and return array containing quotient and
|
196
199
|
# modulus.
|
197
200
|
#
|
198
|
-
# @param [Money,
|
201
|
+
# @param [Money, Integer] val Number to divmod by.
|
199
202
|
#
|
200
|
-
# @return [Array<Money,Money>,Array<
|
203
|
+
# @return [Array<Money,Money>,Array<Integer,Money>]
|
201
204
|
#
|
202
205
|
# @example
|
203
206
|
# Money.new(100).divmod(9) #=> [#<Money @fractional=11>, #<Money @fractional=1>]
|
@@ -213,19 +216,19 @@ class Money
|
|
213
216
|
def divmod_money(val)
|
214
217
|
cents = val.exchange_to(currency).cents
|
215
218
|
quotient, remainder = fractional.divmod(cents)
|
216
|
-
[quotient, self.class.new(remainder, currency)]
|
219
|
+
[quotient, self.class.new(remainder, currency, bank)]
|
217
220
|
end
|
218
221
|
private :divmod_money
|
219
222
|
|
220
223
|
def divmod_other(val)
|
221
224
|
quotient, remainder = fractional.divmod(as_d(val))
|
222
|
-
[self.class.new(quotient, currency), self.class.new(remainder, currency)]
|
225
|
+
[self.class.new(quotient, currency, bank), self.class.new(remainder, currency, bank)]
|
223
226
|
end
|
224
227
|
private :divmod_other
|
225
228
|
|
226
229
|
# Equivalent to +self.divmod(val)[1]+
|
227
230
|
#
|
228
|
-
# @param [Money,
|
231
|
+
# @param [Money, Integer] val Number take modulo with.
|
229
232
|
#
|
230
233
|
# @return [Money]
|
231
234
|
#
|
@@ -238,7 +241,7 @@ class Money
|
|
238
241
|
|
239
242
|
# Synonym for +#modulo+.
|
240
243
|
#
|
241
|
-
# @param [Money,
|
244
|
+
# @param [Money, Integer] val Number take modulo with.
|
242
245
|
#
|
243
246
|
# @return [Money]
|
244
247
|
#
|
@@ -249,7 +252,7 @@ class Money
|
|
249
252
|
|
250
253
|
# If different signs +self.modulo(val) - val+ otherwise +self.modulo(val)+
|
251
254
|
#
|
252
|
-
# @param [Money,
|
255
|
+
# @param [Money, Integer] val Number to rake remainder with.
|
253
256
|
#
|
254
257
|
# @return [Money]
|
255
258
|
#
|
@@ -263,7 +266,7 @@ class Money
|
|
263
266
|
if (fractional < 0 && val < 0) || (fractional > 0 && val > 0)
|
264
267
|
self.modulo(val)
|
265
268
|
else
|
266
|
-
self.modulo(val) - (val.is_a?(Money) ? val : self.class.new(val, currency))
|
269
|
+
self.modulo(val) - (val.is_a?(Money) ? val : self.class.new(val, currency, bank))
|
267
270
|
end
|
268
271
|
end
|
269
272
|
|
@@ -274,7 +277,7 @@ class Money
|
|
274
277
|
# @example
|
275
278
|
# Money.new(-100).abs #=> #<Money @fractional=100>
|
276
279
|
def abs
|
277
|
-
self.class.new(fractional.abs, currency)
|
280
|
+
self.class.new(fractional.abs, currency, bank)
|
278
281
|
end
|
279
282
|
|
280
283
|
# Test if the money amount is zero.
|