money 6.9.0 → 6.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +131 -3
- data/LICENSE +17 -17
- data/README.md +181 -71
- data/config/currency_backwards_compatible.json +65 -0
- data/config/currency_iso.json +119 -56
- data/config/currency_non_iso.json +35 -2
- data/lib/money/bank/variable_exchange.rb +22 -12
- data/lib/money/currency/loader.rb +15 -13
- data/lib/money/currency.rb +38 -39
- data/lib/money/locale_backend/base.rb +7 -0
- data/lib/money/locale_backend/currency.rb +11 -0
- data/lib/money/locale_backend/errors.rb +6 -0
- data/lib/money/locale_backend/i18n.rb +25 -0
- data/lib/money/locale_backend/legacy.rb +28 -0
- data/lib/money/money/allocation.rb +46 -0
- data/lib/money/money/arithmetic.rb +33 -15
- data/lib/money/money/constructors.rb +1 -2
- data/lib/money/money/formatter.rb +399 -0
- data/lib/money/money/formatting_rules.rb +142 -0
- data/lib/money/money/locale_backend.rb +22 -0
- data/lib/money/money.rb +235 -187
- data/lib/money/rates_store/memory.rb +24 -24
- data/lib/money/version.rb +1 -1
- data/money.gemspec +14 -8
- metadata +36 -56
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -23
- data/.rspec +0 -1
- data/.travis.yml +0 -26
- data/AUTHORS +0 -126
- data/CONTRIBUTING.md +0 -17
- data/Gemfile +0 -16
- data/Rakefile +0 -17
- data/lib/money/money/formatting.rb +0 -426
- data/spec/bank/base_spec.rb +0 -79
- data/spec/bank/single_currency_spec.rb +0 -13
- data/spec/bank/variable_exchange_spec.rb +0 -265
- data/spec/currency/heuristics_spec.rb +0 -11
- data/spec/currency/loader_spec.rb +0 -19
- data/spec/currency_spec.rb +0 -359
- data/spec/money/arithmetic_spec.rb +0 -693
- data/spec/money/constructors_spec.rb +0 -103
- data/spec/money/formatting_spec.rb +0 -757
- data/spec/money_spec.rb +0 -778
- data/spec/rates_store/memory_spec.rb +0 -69
- data/spec/spec_helper.rb +0 -28
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
|
@@ -90,50 +91,71 @@ class Money
|
|
90
91
|
class << self
|
91
92
|
|
92
93
|
# @!attribute [rw] default_bank
|
93
|
-
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
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]
|
98
103
|
#
|
99
104
|
# @!attribute default_formatting_rules
|
100
|
-
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
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+.
|
104
109
|
#
|
105
|
-
# @see
|
110
|
+
# @see Money::Formatter#initialize Money::Formatter for more details
|
106
111
|
#
|
107
112
|
# @example
|
108
|
-
# Money.default_formatting_rules = { :
|
113
|
+
# Money.default_formatting_rules = { display_free: true }
|
109
114
|
# Money.new(0, "USD").format # => "free"
|
110
|
-
# Money.new(0, "USD").format(:
|
115
|
+
# Money.new(0, "USD").format(display_free: false) # => "$0.00"
|
116
|
+
#
|
117
|
+
# @return [Hash]
|
111
118
|
#
|
112
119
|
# @!attribute [rw] use_i18n
|
113
|
-
#
|
114
|
-
#
|
120
|
+
# Used to disable i18n even if it's used by other components of your app.
|
121
|
+
#
|
122
|
+
# @return [Boolean]
|
115
123
|
#
|
116
|
-
# @!attribute [rw]
|
117
|
-
# @return [Boolean] Use this to enable infinite precision cents
|
124
|
+
# @!attribute [rw] default_infinite_precision
|
125
|
+
# @return [Boolean] Use this to enable infinite precision cents as the
|
126
|
+
# global default
|
118
127
|
#
|
119
128
|
# @!attribute [rw] conversion_precision
|
120
|
-
#
|
121
|
-
# to BigDecimal
|
122
|
-
attr_accessor :default_bank, :default_formatting_rules,
|
123
|
-
:use_i18n, :infinite_precision, :conversion_precision
|
124
|
-
|
125
|
-
# @attr_writer rounding_mode Use this to specify the rounding mode
|
129
|
+
# Used to specify precision for converting Rational to BigDecimal
|
126
130
|
#
|
127
|
-
#
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
133
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
|
134
145
|
end
|
135
146
|
|
147
|
+
# @!attribute default_currency
|
148
|
+
# @return [Money::Currency] The default currency, which is used when
|
149
|
+
# +Money.new+ is called without an explicit currency argument. The
|
150
|
+
# default value is Currency.new("USD"). The value must be a valid
|
151
|
+
# +Money::Currency+ instance.
|
136
152
|
def self.default_currency
|
153
|
+
if @using_deprecated_default_currency
|
154
|
+
warn '[WARNING] The default currency will change from `USD` to `nil` in the next major release. Make ' \
|
155
|
+
'sure to set it explicitly using `Money.default_currency=` to avoid potential issues'
|
156
|
+
@using_deprecated_default_currency = false
|
157
|
+
end
|
158
|
+
|
137
159
|
if @default_currency.respond_to?(:call)
|
138
160
|
Money::Currency.new(@default_currency.call)
|
139
161
|
else
|
@@ -141,21 +163,59 @@ class Money
|
|
141
163
|
end
|
142
164
|
end
|
143
165
|
|
166
|
+
def self.default_currency=(currency)
|
167
|
+
@using_deprecated_default_currency = false
|
168
|
+
@default_currency = currency
|
169
|
+
end
|
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
|
+
|
179
|
+
def self.locale_backend=(value)
|
180
|
+
@locale_backend = value ? LocaleBackend.find(value) : nil
|
181
|
+
end
|
182
|
+
|
183
|
+
# @attr_writer rounding_mode Use this to specify the rounding mode
|
184
|
+
def self.rounding_mode=(new_rounding_mode)
|
185
|
+
@using_deprecated_default_rounding_mode = false
|
186
|
+
@rounding_mode = new_rounding_mode
|
187
|
+
end
|
188
|
+
|
189
|
+
def self.use_i18n=(value)
|
190
|
+
if value
|
191
|
+
warn '[DEPRECATION] `use_i18n` is deprecated - use `Money.locale_backend = :i18n` instead for locale based formatting'
|
192
|
+
else
|
193
|
+
warn '[DEPRECATION] `use_i18n` is deprecated - use `Money.locale_backend = :currency` instead for currency based formatting'
|
194
|
+
end
|
195
|
+
|
196
|
+
@use_i18n = value
|
197
|
+
end
|
198
|
+
|
144
199
|
def self.setup_defaults
|
145
200
|
# Set the default bank for creating new +Money+ objects.
|
146
201
|
self.default_bank = Bank::VariableExchange.instance
|
147
202
|
|
148
203
|
# Set the default currency for creating new +Money+ object.
|
149
204
|
self.default_currency = Currency.new("USD")
|
205
|
+
@using_deprecated_default_currency = true
|
150
206
|
|
151
207
|
# Default to using i18n
|
152
|
-
|
208
|
+
@use_i18n = true
|
209
|
+
|
210
|
+
# Default to using legacy locale backend
|
211
|
+
self.locale_backend = :legacy
|
153
212
|
|
154
213
|
# Default to not using infinite precision cents
|
155
|
-
self.
|
214
|
+
self.default_infinite_precision = false
|
156
215
|
|
157
216
|
# Default to bankers rounding
|
158
217
|
self.rounding_mode = BigDecimal::ROUND_HALF_EVEN
|
218
|
+
@using_deprecated_default_rounding_mode = true
|
159
219
|
|
160
220
|
# Default the conversion of Rationals precision to 16
|
161
221
|
self.conversion_precision = 16
|
@@ -167,32 +227,48 @@ class Money
|
|
167
227
|
|
168
228
|
setup_defaults
|
169
229
|
|
170
|
-
# Use this to return the rounding mode.
|
171
|
-
#
|
172
|
-
#
|
230
|
+
# Use this to return the rounding mode.
|
231
|
+
#
|
232
|
+
# @param [BigDecimal::ROUND_MODE] mode
|
233
|
+
#
|
234
|
+
# @return [BigDecimal::ROUND_MODE] rounding mode
|
235
|
+
def self.rounding_mode(mode = nil)
|
236
|
+
if mode
|
237
|
+
warn "[DEPRECATION] calling `rounding_mode` with a block is deprecated. Please use `.with_rounding_mode` instead."
|
238
|
+
return with_rounding_mode(mode) { yield }
|
239
|
+
end
|
240
|
+
|
241
|
+
return Thread.current[:money_rounding_mode] if Thread.current[:money_rounding_mode]
|
242
|
+
|
243
|
+
if @using_deprecated_default_rounding_mode
|
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
|
247
|
+
end
|
248
|
+
|
249
|
+
@rounding_mode
|
250
|
+
end
|
251
|
+
|
252
|
+
# Temporarily changes the rounding mode in a given block.
|
173
253
|
#
|
174
254
|
# @param [BigDecimal::ROUND_MODE] mode
|
175
255
|
#
|
176
|
-
# @
|
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
|
177
260
|
#
|
178
261
|
# @example
|
179
|
-
# fee = Money.
|
180
|
-
# Money.new(1200) * BigDecimal
|
262
|
+
# fee = Money.with_rounding_mode(BigDecimal::ROUND_HALF_UP) do
|
263
|
+
# Money.new(1200) * BigDecimal('0.029')
|
181
264
|
# end
|
182
|
-
def self.
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
Thread.current[:money_rounding_mode] = mode
|
188
|
-
yield
|
189
|
-
ensure
|
190
|
-
Thread.current[:money_rounding_mode] = nil
|
191
|
-
end
|
192
|
-
end
|
265
|
+
def self.with_rounding_mode(mode)
|
266
|
+
Thread.current[:money_rounding_mode] = mode
|
267
|
+
yield
|
268
|
+
ensure
|
269
|
+
Thread.current[:money_rounding_mode] = nil
|
193
270
|
end
|
194
271
|
|
195
|
-
|
196
272
|
# Adds a new exchange rate to the default bank and return the rate.
|
197
273
|
#
|
198
274
|
# @param [Currency, String, Symbol] from_currency Currency to exchange from.
|
@@ -218,7 +294,8 @@ class Money
|
|
218
294
|
#
|
219
295
|
# @param [Numeric] amount The numerical value of the money.
|
220
296
|
# @param [Currency, String, Symbol] currency The currency format.
|
221
|
-
# @param [
|
297
|
+
# @param [Hash] options Optional settings for the new Money instance
|
298
|
+
# @option [Money::Bank::*] :bank The exchange bank to use.
|
222
299
|
#
|
223
300
|
# @example
|
224
301
|
# Money.from_amount(23.45, "USD") # => #<Money fractional:2345 currency:USD>
|
@@ -227,12 +304,16 @@ class Money
|
|
227
304
|
# @return [Money]
|
228
305
|
#
|
229
306
|
# @see #initialize
|
230
|
-
def self.from_amount(amount, currency = default_currency,
|
231
|
-
|
307
|
+
def self.from_amount(amount, currency = default_currency, options = {})
|
308
|
+
raise ArgumentError, "'amount' must be numeric" unless Numeric === amount
|
309
|
+
|
232
310
|
currency = Currency.wrap(currency) || Money.default_currency
|
233
311
|
value = amount.to_d * currency.subunit_to_unit
|
234
|
-
value
|
235
|
-
|
312
|
+
new(value, currency, options)
|
313
|
+
end
|
314
|
+
|
315
|
+
class << self
|
316
|
+
alias_method :from_cents, :new
|
236
317
|
end
|
237
318
|
|
238
319
|
# Creates a new Money object of value given in the
|
@@ -246,7 +327,8 @@ class Money
|
|
246
327
|
# argument, a Money will be created in that currency with fractional value
|
247
328
|
# = 0.
|
248
329
|
# @param [Currency, String, Symbol] currency The currency format.
|
249
|
-
# @param [
|
330
|
+
# @param [Hash] options Optional settings for the new Money instance
|
331
|
+
# @option [Money::Bank::*] :bank The exchange bank to use.
|
250
332
|
#
|
251
333
|
# @return [Money]
|
252
334
|
#
|
@@ -255,11 +337,20 @@ class Money
|
|
255
337
|
# Money.new(100, "USD") #=> #<Money @fractional=100 @currency="USD">
|
256
338
|
# Money.new(100, "EUR") #=> #<Money @fractional=100 @currency="EUR">
|
257
339
|
#
|
258
|
-
def initialize(obj, currency = Money.default_currency,
|
259
|
-
|
340
|
+
def initialize( obj, currency = Money.default_currency, options = {})
|
341
|
+
# For backwards compatability, if options is not a Hash, treat it as a bank parameter
|
342
|
+
unless options.is_a?(Hash)
|
343
|
+
options = { bank: options }
|
344
|
+
end
|
345
|
+
|
346
|
+
@fractional = as_d(obj.respond_to?(:fractional) ? obj.fractional : obj)
|
260
347
|
@currency = obj.respond_to?(:currency) ? obj.currency : Currency.wrap(currency)
|
261
348
|
@currency ||= Money.default_currency
|
262
|
-
@bank = obj.respond_to?(:bank) ? obj.bank : bank
|
349
|
+
@bank = obj.respond_to?(:bank) ? obj.bank : options[:bank]
|
350
|
+
@bank ||= Money.default_bank
|
351
|
+
|
352
|
+
# BigDecimal can be Infinity and NaN, money of that amount does not make sense
|
353
|
+
raise ArgumentError, 'must be initialized with a finite value' unless @fractional.finite?
|
263
354
|
end
|
264
355
|
|
265
356
|
# Assuming using a currency using dollars:
|
@@ -271,7 +362,7 @@ class Money
|
|
271
362
|
# @return [BigDecimal]
|
272
363
|
#
|
273
364
|
# @example
|
274
|
-
# Money.new(1_00, "USD").dollars # => BigDecimal
|
365
|
+
# Money.new(1_00, "USD").dollars # => BigDecimal("1.00")
|
275
366
|
#
|
276
367
|
# @see #amount
|
277
368
|
# @see #to_d
|
@@ -286,7 +377,7 @@ class Money
|
|
286
377
|
# @return [BigDecimal]
|
287
378
|
#
|
288
379
|
# @example
|
289
|
-
# Money.new(1_00, "USD").amount # => BigDecimal
|
380
|
+
# Money.new(1_00, "USD").amount # => BigDecimal("1.00")
|
290
381
|
#
|
291
382
|
# @see #to_d
|
292
383
|
# @see #fractional
|
@@ -302,6 +393,7 @@ class Money
|
|
302
393
|
# @example
|
303
394
|
# Money.new(100, :USD).currency_as_string #=> "USD"
|
304
395
|
def currency_as_string
|
396
|
+
warn "[DEPRECATION] `currency_as_string` is deprecated. Please use `.currency.to_s` instead."
|
305
397
|
currency.to_s
|
306
398
|
end
|
307
399
|
|
@@ -314,6 +406,8 @@ class Money
|
|
314
406
|
# @example
|
315
407
|
# Money.new(100).currency_as_string("CAD") #=> #<Money::Currency id: cad>
|
316
408
|
def currency_as_string=(val)
|
409
|
+
warn "[DEPRECATION] `currency_as_string=` is deprecated - Money instances are immutable." \
|
410
|
+
" Please use `with_currency` instead."
|
317
411
|
@currency = Currency.wrap(val)
|
318
412
|
end
|
319
413
|
|
@@ -352,19 +446,10 @@ class Money
|
|
352
446
|
# @example
|
353
447
|
# Money.ca_dollar(100).to_s #=> "1.00"
|
354
448
|
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
|
449
|
+
format thousands_separator: '',
|
450
|
+
no_cents_if_whole: currency.decimal_places == 0,
|
451
|
+
symbol: false,
|
452
|
+
ignore_defaults: true
|
368
453
|
end
|
369
454
|
|
370
455
|
# Return the amount of money as a BigDecimal.
|
@@ -372,7 +457,7 @@ class Money
|
|
372
457
|
# @return [BigDecimal]
|
373
458
|
#
|
374
459
|
# @example
|
375
|
-
# Money.us_dollar(1_00).to_d #=> BigDecimal
|
460
|
+
# Money.us_dollar(1_00).to_d #=> BigDecimal("1.00")
|
376
461
|
def to_d
|
377
462
|
as_d(fractional) / as_d(currency.subunit_to_unit)
|
378
463
|
end
|
@@ -400,7 +485,22 @@ class Money
|
|
400
485
|
to_d.to_f
|
401
486
|
end
|
402
487
|
|
403
|
-
#
|
488
|
+
# Returns a new Money instance in a given currency leaving the amount intact
|
489
|
+
# and not performing currency conversion.
|
490
|
+
#
|
491
|
+
# @param [Currency, String, Symbol] new_currency Currency of the new object.
|
492
|
+
#
|
493
|
+
# @return [self]
|
494
|
+
def with_currency(new_currency)
|
495
|
+
new_currency = Currency.wrap(new_currency)
|
496
|
+
if !new_currency || currency == new_currency
|
497
|
+
self
|
498
|
+
else
|
499
|
+
dup_with(currency: new_currency)
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
# Conversion to +self+.
|
404
504
|
#
|
405
505
|
# @return [self]
|
406
506
|
def to_money(given_currency = nil)
|
@@ -473,54 +573,29 @@ class Money
|
|
473
573
|
exchange_to("EUR")
|
474
574
|
end
|
475
575
|
|
476
|
-
#
|
477
|
-
#
|
478
|
-
#
|
479
|
-
# listed first will likely receive more pennies than ones that are listed later
|
480
|
-
#
|
481
|
-
# @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.
|
482
|
-
#
|
483
|
-
# @return [Array<Money>]
|
576
|
+
# Splits a given amount in parts without losing pennies. The left-over pennies will be
|
577
|
+
# distributed round-robin amongst the parties. This means that parts listed first will likely
|
578
|
+
# receive more pennies than ones listed later.
|
484
579
|
#
|
485
|
-
#
|
486
|
-
#
|
487
|
-
#
|
580
|
+
# Pass [2, 1, 1] as input to give twice as much to part1 as part2 or
|
581
|
+
# part3 which results in 50% of the cash to party1, 25% to part2, and 25% to part3. Passing a
|
582
|
+
# number instead of an array will split the amount evenly (without losing pennies when rounding).
|
488
583
|
#
|
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
|
-
delta = left_over > 0 ? 1 : -1
|
500
|
-
# Distribute left over pennies amongst allocations
|
501
|
-
left_over.to_i.abs.times { |i| amounts[i % amounts.length] += delta }
|
502
|
-
end
|
503
|
-
|
504
|
-
amounts.collect { |fractional| self.class.new(fractional, currency) }
|
505
|
-
end
|
506
|
-
|
507
|
-
# Split money amongst parties evenly without losing pennies.
|
508
|
-
#
|
509
|
-
# @param [Numeric] num number of parties.
|
584
|
+
# @param [Array<Numeric>, Numeric] parts how amount should be distributed to parts
|
510
585
|
#
|
511
586
|
# @return [Array<Money>]
|
512
587
|
#
|
513
588
|
# @example
|
514
|
-
# Money.new(
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
end
|
589
|
+
# Money.new(5, "USD").allocate([3, 7]) #=> [Money.new(2), Money.new(3)]
|
590
|
+
# Money.new(100, "USD").allocate([1, 1, 1]) #=> [Money.new(34), Money.new(33), Money.new(33)]
|
591
|
+
# Money.new(100, "USD").allocate(2) #=> [Money.new(50), Money.new(50)]
|
592
|
+
# Money.new(100, "USD").allocate(3) #=> [Money.new(34), Money.new(33), Money.new(33)]
|
593
|
+
#
|
594
|
+
def allocate(parts)
|
595
|
+
amounts = Money::Allocation.generate(fractional, parts, !Money.default_infinite_precision)
|
596
|
+
amounts.map { |amount| dup_with(fractional: amount) }
|
523
597
|
end
|
598
|
+
alias_method :split, :allocate
|
524
599
|
|
525
600
|
# Round the monetary amount to smallest unit of coinage.
|
526
601
|
#
|
@@ -535,95 +610,68 @@ class Money
|
|
535
610
|
# Money.new(10.1, 'USD').round #=> Money.new(10, 'USD')
|
536
611
|
#
|
537
612
|
# @see
|
538
|
-
# Money.
|
613
|
+
# Money.default_infinite_precision
|
539
614
|
#
|
540
|
-
def round(rounding_mode = self.class.rounding_mode)
|
541
|
-
|
542
|
-
|
543
|
-
else
|
544
|
-
self
|
545
|
-
end
|
546
|
-
end
|
547
|
-
|
548
|
-
private
|
549
|
-
|
550
|
-
def as_d(num)
|
551
|
-
if num.respond_to?(:to_d)
|
552
|
-
num.is_a?(Rational) ? num.to_d(self.class.conversion_precision) : num.to_d
|
553
|
-
else
|
554
|
-
BigDecimal.new(num.to_s.empty? ? 0 : num.to_s)
|
555
|
-
end
|
556
|
-
end
|
557
|
-
|
558
|
-
def strings_from_fractional
|
559
|
-
unit, subunit = fractional().abs.divmod(currency.subunit_to_unit)
|
560
|
-
|
561
|
-
if self.class.infinite_precision
|
562
|
-
strings_for_infinite_precision(unit, subunit)
|
563
|
-
else
|
564
|
-
strings_for_base_precision(unit, subunit)
|
565
|
-
end
|
566
|
-
end
|
567
|
-
|
568
|
-
def strings_for_infinite_precision(unit, subunit)
|
569
|
-
subunit, fraction = subunit.divmod(BigDecimal("1"))
|
570
|
-
fraction = fraction.to_s("F")[2..-1] # want fractional part "0.xxx"
|
571
|
-
fraction = "" if fraction =~ /^0+$/
|
572
|
-
|
573
|
-
[unit.to_i.to_s, subunit.to_i.to_s, fraction]
|
574
|
-
end
|
575
|
-
|
576
|
-
def strings_for_base_precision(unit, subunit)
|
577
|
-
[unit.to_s, subunit.to_s, ""]
|
615
|
+
def round(rounding_mode = self.class.rounding_mode, rounding_precision = 0)
|
616
|
+
rounded_amount = as_d(@fractional).round(rounding_precision, rounding_mode)
|
617
|
+
dup_with(fractional: rounded_amount)
|
578
618
|
end
|
579
619
|
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
620
|
+
# Creates a formatted price string according to several rules.
|
621
|
+
#
|
622
|
+
# @param [Hash] rules See {Money::Formatter Money::Formatter} for the list of formatting options
|
623
|
+
#
|
624
|
+
# @return [String]
|
625
|
+
#
|
626
|
+
def format(*rules)
|
627
|
+
Money::Formatter.new(self, *rules).to_s
|
584
628
|
end
|
585
629
|
|
586
|
-
|
587
|
-
|
630
|
+
# Returns a thousands separator according to the locale
|
631
|
+
#
|
632
|
+
# @return [String]
|
633
|
+
#
|
634
|
+
def thousands_separator
|
635
|
+
(locale_backend && locale_backend.lookup(:thousands_separator, currency)) ||
|
636
|
+
Money::Formatter::DEFAULTS[:thousands_separator]
|
588
637
|
end
|
589
638
|
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
(fractional * ratio / allocations).truncate.tap do |frac|
|
598
|
-
left_over -= frac
|
599
|
-
end
|
600
|
-
end
|
601
|
-
end
|
602
|
-
|
603
|
-
[amounts, left_over]
|
639
|
+
# Returns a decimal mark according to the locale
|
640
|
+
#
|
641
|
+
# @return [String]
|
642
|
+
#
|
643
|
+
def decimal_mark
|
644
|
+
(locale_backend && locale_backend.lookup(:decimal_mark, currency)) ||
|
645
|
+
Money::Formatter::DEFAULTS[:decimal_mark]
|
604
646
|
end
|
605
647
|
|
606
|
-
def
|
607
|
-
|
608
|
-
|
648
|
+
def dup_with(options = {})
|
649
|
+
self.class.new(
|
650
|
+
options[:fractional] || fractional,
|
651
|
+
options[:currency] || currency,
|
652
|
+
bank: options[:bank] || bank
|
653
|
+
)
|
609
654
|
end
|
610
655
|
|
611
|
-
|
612
|
-
low = self.class.new(fractional / num, currency)
|
613
|
-
high = self.class.new(low.fractional + 1, currency)
|
614
|
-
|
615
|
-
remainder = fractional % num
|
656
|
+
private
|
616
657
|
|
617
|
-
|
618
|
-
|
658
|
+
def as_d(num)
|
659
|
+
if num.respond_to?(:to_d)
|
660
|
+
num.is_a?(Rational) ? num.to_d(self.class.conversion_precision) : num.to_d
|
661
|
+
else
|
662
|
+
BigDecimal(num.to_s.empty? ? 0 : num.to_s)
|
619
663
|
end
|
620
664
|
end
|
621
665
|
|
622
666
|
def return_value(value)
|
623
|
-
if self.class.
|
667
|
+
if self.class.default_infinite_precision
|
624
668
|
value
|
625
669
|
else
|
626
670
|
value.round(0, self.class.rounding_mode).to_i
|
627
671
|
end
|
628
672
|
end
|
673
|
+
|
674
|
+
def locale_backend
|
675
|
+
self.class.locale_backend
|
676
|
+
end
|
629
677
|
end
|