money 6.2.1 → 6.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -25,6 +25,7 @@
25
25
  "html_entity": "£",
26
26
  "decimal_mark": ".",
27
27
  "thousands_separator": ",",
28
- "iso_numeric": ""
28
+ "iso_numeric": "",
29
+ "smallest_denomination": 1
29
30
  }
30
31
  }
@@ -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+
@@ -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
- if self.class.infinite_precision
51
- fractional
52
- else
53
- fractional.round(0, self.class.rounding_mode).to_i
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 loosing pennies.
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 loosing pennies.
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
@@ -1,3 +1,3 @@
1
1
  class Money
2
- VERSION = "6.2.1"
2
+ VERSION = "6.3.0"
3
3
  end
@@ -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 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 ","
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") }
@@ -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.2.1
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-08 00:00:00.000000000 Z
11
+ date: 2014-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n