money 6.2.1 → 6.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/AUTHORS +2 -1
- data/CHANGELOG.md +4 -1
- data/config/currency_backwards_compatible.json +18 -9
- data/config/currency_iso.json +320 -160
- data/config/currency_non_iso.json +2 -1
- data/lib/money/currency.rb +5 -2
- data/lib/money/money.rb +34 -6
- data/lib/money/version.rb +1 -1
- data/spec/currency_spec.rb +12 -11
- data/spec/money/formatting_spec.rb +3 -3
- data/spec/money_spec.rb +83 -0
- metadata +2 -2
data/lib/money/currency.rb
CHANGED
@@ -151,10 +151,12 @@ class Money
|
|
151
151
|
# of the whole unit.
|
152
152
|
# @attr_reader [Boolean] symbol_first Should the currency symbol precede
|
153
153
|
# the amount, or should it come after?
|
154
|
+
# @attr_reader [Integer] smallest_denomination Smallest amount of cash
|
155
|
+
# possible (in the subunit of this currency)
|
154
156
|
|
155
157
|
attr_reader :id, :priority, :iso_code, :iso_numeric, :name, :symbol,
|
156
158
|
:html_entity, :subunit, :subunit_to_unit, :decimal_mark,
|
157
|
-
:thousands_separator, :symbol_first
|
159
|
+
:thousands_separator, :symbol_first, :smallest_denomination
|
158
160
|
|
159
161
|
alias_method :separator, :decimal_mark
|
160
162
|
alias_method :delimiter, :thousands_separator
|
@@ -186,6 +188,7 @@ class Money
|
|
186
188
|
@decimal_mark = data[:decimal_mark]
|
187
189
|
@thousands_separator = data[:thousands_separator]
|
188
190
|
@iso_numeric = data[:iso_numeric]
|
191
|
+
@smallest_denomination = data[:smallest_denomination]
|
189
192
|
else
|
190
193
|
raise UnknownCurrency, "Unknown currency '#{id}'"
|
191
194
|
end
|
@@ -268,7 +271,7 @@ class Money
|
|
268
271
|
# @example
|
269
272
|
# Money::Currency.new(:usd) #=> #<Currency id: usd ...>
|
270
273
|
def inspect
|
271
|
-
"#<#{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}>"
|
274
|
+
"#<#{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}>"
|
272
275
|
end
|
273
276
|
|
274
277
|
# Returns a string representation corresponding to the upcase +id+
|
data/lib/money/money.rb
CHANGED
@@ -15,6 +15,9 @@ require "money/money/formatting"
|
|
15
15
|
# @see http://en.wikipedia.org/wiki/Money
|
16
16
|
class Money
|
17
17
|
include Comparable, Money::Arithmetic, Money::Formatting
|
18
|
+
|
19
|
+
# Raised when smallest denomination of a currency is not defined
|
20
|
+
class UndefinedSmallestDenomination < StandardError; end
|
18
21
|
|
19
22
|
# Convenience method for fractional part of the amount. Synonym of #fractional
|
20
23
|
#
|
@@ -47,11 +50,28 @@ class Money
|
|
47
50
|
# from YAML, @fractional can end up being set to a Float.
|
48
51
|
fractional = as_d(@fractional)
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
53
|
+
return_value(fractional)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Round a given amount of money to the nearest possible amount in cash value. For
|
57
|
+
# example, in Swiss francs (CHF), the smallest possible amount of cash value is
|
58
|
+
# CHF 0.05. Therefore, this method rounds CHF 0.07 to CHF 0.05, and CHF 0.08 to
|
59
|
+
# CHF 0.10.
|
60
|
+
#
|
61
|
+
# @return [Integer] when infinite_precision is false
|
62
|
+
# @return [BigDecimal] when infinite_precision is true
|
63
|
+
#
|
64
|
+
# @see infinite_precision
|
65
|
+
def round_to_nearest_cash_value
|
66
|
+
unless self.currency.smallest_denomination
|
67
|
+
raise UndefinedSmallestDenomination, 'Smallest denomination of this currency is not defined'
|
54
68
|
end
|
69
|
+
|
70
|
+
fractional = as_d(@fractional)
|
71
|
+
smallest_denomination = as_d(self.currency.smallest_denomination)
|
72
|
+
rounded_value = (fractional / smallest_denomination).round(0, self.class.rounding_mode) * smallest_denomination
|
73
|
+
|
74
|
+
return_value(rounded_value)
|
55
75
|
end
|
56
76
|
|
57
77
|
# @attr_reader [Currency] currency The currency the money is in.
|
@@ -455,7 +475,7 @@ class Money
|
|
455
475
|
exchange_to("EUR")
|
456
476
|
end
|
457
477
|
|
458
|
-
# Allocates money between different parties without
|
478
|
+
# Allocates money between different parties without losing pennies.
|
459
479
|
# After the mathmatically split has been performed, left over pennies will
|
460
480
|
# be distributed round-robin amongst the parties. This means that parties
|
461
481
|
# listed first will likely recieve more pennies then ones that are listed later
|
@@ -484,7 +504,7 @@ class Money
|
|
484
504
|
amounts.collect { |fractional| Money.new(fractional, currency) }
|
485
505
|
end
|
486
506
|
|
487
|
-
# Split money amongst parties evenly without
|
507
|
+
# Split money amongst parties evenly without losing pennies.
|
488
508
|
#
|
489
509
|
# @param [Numeric] num number of parties.
|
490
510
|
#
|
@@ -598,4 +618,12 @@ class Money
|
|
598
618
|
index < remainder ? high : low
|
599
619
|
end
|
600
620
|
end
|
621
|
+
|
622
|
+
def return_value(value)
|
623
|
+
if self.class.infinite_precision
|
624
|
+
value
|
625
|
+
else
|
626
|
+
value.round(0, self.class.rounding_mode).to_i
|
627
|
+
end
|
628
|
+
end
|
601
629
|
end
|
data/lib/money/version.rb
CHANGED
data/spec/currency_spec.rb
CHANGED
@@ -4,7 +4,7 @@ require "spec_helper"
|
|
4
4
|
|
5
5
|
describe Money::Currency do
|
6
6
|
|
7
|
-
FOO = '{ "priority": 1, "iso_code": "FOO", "iso_numeric": "840", "name": "United States Dollar", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 450, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": "," }'
|
7
|
+
FOO = '{ "priority": 1, "iso_code": "FOO", "iso_numeric": "840", "name": "United States Dollar", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 450, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": ",", "smallest_denomination": 1 }'
|
8
8
|
|
9
9
|
describe ".find" do
|
10
10
|
before :each do
|
@@ -75,15 +75,16 @@ describe Money::Currency do
|
|
75
75
|
describe "#initialize" do
|
76
76
|
it "lookups data from loaded config" do
|
77
77
|
currency = Money::Currency.new("USD")
|
78
|
-
expect(currency.id).to
|
79
|
-
expect(currency.priority).to
|
80
|
-
expect(currency.iso_code).to
|
81
|
-
expect(currency.iso_numeric).to
|
82
|
-
expect(currency.name).to
|
83
|
-
expect(currency.decimal_mark).to
|
84
|
-
expect(currency.separator).to
|
85
|
-
expect(currency.thousands_separator).to
|
86
|
-
expect(currency.delimiter).to
|
78
|
+
expect(currency.id).to eq :usd
|
79
|
+
expect(currency.priority).to eq 1
|
80
|
+
expect(currency.iso_code).to eq "USD"
|
81
|
+
expect(currency.iso_numeric).to eq "840"
|
82
|
+
expect(currency.name).to eq "United States Dollar"
|
83
|
+
expect(currency.decimal_mark).to eq "."
|
84
|
+
expect(currency.separator).to eq "."
|
85
|
+
expect(currency.thousands_separator).to eq ","
|
86
|
+
expect(currency.delimiter).to eq ","
|
87
|
+
expect(currency.smallest_denomination).to eq 1
|
87
88
|
end
|
88
89
|
|
89
90
|
it "raises UnknownMoney::Currency with unknown currency" do
|
@@ -144,7 +145,7 @@ describe Money::Currency do
|
|
144
145
|
|
145
146
|
describe "#inspect" do
|
146
147
|
it "works as documented" do
|
147
|
-
expect(Money::Currency.new(:usd).inspect).to eq %Q{#<Money::Currency id: usd, priority: 1, symbol_first: true, thousands_separator: ,, html_entity: $, decimal_mark: ., name: United States Dollar, symbol: $, subunit_to_unit: 100, exponent: 2.0, iso_code: USD, iso_numeric: 840, subunit: Cent>}
|
148
|
+
expect(Money::Currency.new(:usd).inspect).to eq %Q{#<Money::Currency id: usd, priority: 1, symbol_first: true, thousands_separator: ,, html_entity: $, decimal_mark: ., name: United States Dollar, symbol: $, subunit_to_unit: 100, exponent: 2.0, iso_code: USD, iso_numeric: 840, subunit: Cent, smallest_denomination: 1>}
|
148
149
|
end
|
149
150
|
end
|
150
151
|
|
@@ -4,9 +4,9 @@ require "spec_helper"
|
|
4
4
|
|
5
5
|
describe Money, "formatting" do
|
6
6
|
|
7
|
-
BAR = '{ "priority": 1, "iso_code": "BAR", "iso_numeric": "840", "name": "Dollar with 4 decimal places", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 10000, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": "," }'
|
8
|
-
INDIAN_BAR = '{ "priority": 1, "iso_code": "INDIAN_BAR", "iso_numeric": "840", "name": "Dollar with 4 decimal places", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 10000, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": ",", "south_asian_number_formatting": true}'
|
9
|
-
EU4 = '{ "priority": 1, "iso_code": "EU4", "iso_numeric": "841", "name": "Euro with 4 decimal places", "symbol": "€", "subunit": "Cent", "subunit_to_unit": 10000, "symbol_first": true, "html_entity": "€", "decimal_mark": ",", "thousands_separator": "." }'
|
7
|
+
BAR = '{ "priority": 1, "iso_code": "BAR", "iso_numeric": "840", "name": "Dollar with 4 decimal places", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 10000, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": ",", "smallest_denomination": 1 }'
|
8
|
+
INDIAN_BAR = '{ "priority": 1, "iso_code": "INDIAN_BAR", "iso_numeric": "840", "name": "Dollar with 4 decimal places", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 10000, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": ",", "south_asian_number_formatting": true, "smallest_denomination": 1}'
|
9
|
+
EU4 = '{ "priority": 1, "iso_code": "EU4", "iso_numeric": "841", "name": "Euro with 4 decimal places", "symbol": "€", "subunit": "Cent", "subunit_to_unit": 10000, "symbol_first": true, "html_entity": "€", "decimal_mark": ",", "thousands_separator": ".", "smallest_denomination": 1 }'
|
10
10
|
|
11
11
|
context "without i18n" do
|
12
12
|
subject(:money) { Money.empty("USD") }
|
data/spec/money_spec.rb
CHANGED
@@ -293,6 +293,89 @@ YAML
|
|
293
293
|
end
|
294
294
|
end
|
295
295
|
end
|
296
|
+
|
297
|
+
describe "#round_to_nearest_cash_value" do
|
298
|
+
it "rounds to the nearest possible cash value" do
|
299
|
+
money = Money.new(2350, "AED")
|
300
|
+
expect(money.round_to_nearest_cash_value).to eq 2350
|
301
|
+
|
302
|
+
money = Money.new(-2350, "AED")
|
303
|
+
expect(money.round_to_nearest_cash_value).to eq -2350
|
304
|
+
|
305
|
+
money = Money.new(2213, "AED")
|
306
|
+
expect(money.round_to_nearest_cash_value).to eq 2225
|
307
|
+
|
308
|
+
money = Money.new(-2213, "AED")
|
309
|
+
expect(money.round_to_nearest_cash_value).to eq -2225
|
310
|
+
|
311
|
+
money = Money.new(2212, "AED")
|
312
|
+
expect(money.round_to_nearest_cash_value).to eq 2200
|
313
|
+
|
314
|
+
money = Money.new(-2212, "AED")
|
315
|
+
expect(money.round_to_nearest_cash_value).to eq -2200
|
316
|
+
|
317
|
+
money = Money.new(178, "CHF")
|
318
|
+
expect(money.round_to_nearest_cash_value).to eq 180
|
319
|
+
|
320
|
+
money = Money.new(-178, "CHF")
|
321
|
+
expect(money.round_to_nearest_cash_value).to eq -180
|
322
|
+
|
323
|
+
money = Money.new(177, "CHF")
|
324
|
+
expect(money.round_to_nearest_cash_value).to eq 175
|
325
|
+
|
326
|
+
money = Money.new(-177, "CHF")
|
327
|
+
expect(money.round_to_nearest_cash_value).to eq -175
|
328
|
+
|
329
|
+
money = Money.new(175, "CHF")
|
330
|
+
expect(money.round_to_nearest_cash_value).to eq 175
|
331
|
+
|
332
|
+
money = Money.new(-175, "CHF")
|
333
|
+
expect(money.round_to_nearest_cash_value).to eq -175
|
334
|
+
|
335
|
+
money = Money.new(299, "USD")
|
336
|
+
expect(money.round_to_nearest_cash_value).to eq 299
|
337
|
+
|
338
|
+
money = Money.new(-299, "USD")
|
339
|
+
expect(money.round_to_nearest_cash_value).to eq -299
|
340
|
+
|
341
|
+
money = Money.new(300, "USD")
|
342
|
+
expect(money.round_to_nearest_cash_value).to eq 300
|
343
|
+
|
344
|
+
money = Money.new(-300, "USD")
|
345
|
+
expect(money.round_to_nearest_cash_value).to eq -300
|
346
|
+
|
347
|
+
money = Money.new(301, "USD")
|
348
|
+
expect(money.round_to_nearest_cash_value).to eq 301
|
349
|
+
|
350
|
+
money = Money.new(-301, "USD")
|
351
|
+
expect(money.round_to_nearest_cash_value).to eq -301
|
352
|
+
end
|
353
|
+
|
354
|
+
it "raises an exception if smallest denomination is not defined" do
|
355
|
+
money = Money.new(100, "XAG")
|
356
|
+
expect {money.round_to_nearest_cash_value}.to raise_error(Money::UndefinedSmallestDenomination)
|
357
|
+
end
|
358
|
+
|
359
|
+
it "returns a Fixnum when infinite_precision is not set" do
|
360
|
+
money = Money.new(100, "USD")
|
361
|
+
expect(money.round_to_nearest_cash_value).to be_a Fixnum
|
362
|
+
end
|
363
|
+
|
364
|
+
context "with infinite_precision" do
|
365
|
+
before do
|
366
|
+
Money.infinite_precision = true
|
367
|
+
end
|
368
|
+
|
369
|
+
after do
|
370
|
+
Money.infinite_precision = false
|
371
|
+
end
|
372
|
+
|
373
|
+
it "returns a BigDecimal" do
|
374
|
+
money = Money.new(100, "EUR")
|
375
|
+
expect(money.round_to_nearest_cash_value).to be_a BigDecimal
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|
296
379
|
|
297
380
|
describe "#amount" do
|
298
381
|
it "returns the amount of cents as dollars" do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: money
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shane Emmons
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: i18n
|