money 6.7.1 → 6.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|