money 6.7.0 → 6.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -1
  3. data/.travis.yml +22 -5
  4. data/AUTHORS +5 -0
  5. data/CHANGELOG.md +109 -3
  6. data/Gemfile +13 -4
  7. data/LICENSE +2 -0
  8. data/README.md +69 -49
  9. data/config/currency_backwards_compatible.json +30 -0
  10. data/config/currency_iso.json +139 -62
  11. data/config/currency_non_iso.json +66 -2
  12. data/lib/money.rb +0 -13
  13. data/lib/money/bank/variable_exchange.rb +9 -22
  14. data/lib/money/currency.rb +35 -38
  15. data/lib/money/currency/heuristics.rb +1 -144
  16. data/lib/money/currency/loader.rb +1 -1
  17. data/lib/money/locale_backend/base.rb +7 -0
  18. data/lib/money/locale_backend/errors.rb +6 -0
  19. data/lib/money/locale_backend/i18n.rb +24 -0
  20. data/lib/money/locale_backend/legacy.rb +28 -0
  21. data/lib/money/money.rb +120 -151
  22. data/lib/money/money/allocation.rb +37 -0
  23. data/lib/money/money/arithmetic.rb +57 -52
  24. data/lib/money/money/constructors.rb +1 -2
  25. data/lib/money/money/formatter.rb +397 -0
  26. data/lib/money/money/formatting_rules.rb +120 -0
  27. data/lib/money/money/locale_backend.rb +20 -0
  28. data/lib/money/rates_store/memory.rb +1 -2
  29. data/lib/money/version.rb +1 -1
  30. data/money.gemspec +10 -16
  31. data/spec/bank/variable_exchange_spec.rb +7 -3
  32. data/spec/currency/heuristics_spec.rb +2 -153
  33. data/spec/currency_spec.rb +45 -4
  34. data/spec/locale_backend/i18n_spec.rb +62 -0
  35. data/spec/locale_backend/legacy_spec.rb +74 -0
  36. data/spec/money/allocation_spec.rb +130 -0
  37. data/spec/money/arithmetic_spec.rb +217 -104
  38. data/spec/money/constructors_spec.rb +0 -12
  39. data/spec/money/formatting_spec.rb +320 -179
  40. data/spec/money/locale_backend_spec.rb +14 -0
  41. data/spec/money_spec.rb +159 -26
  42. data/spec/rates_store/memory_spec.rb +13 -2
  43. data/spec/spec_helper.rb +2 -0
  44. data/spec/support/shared_examples/money_examples.rb +14 -0
  45. metadata +32 -41
  46. data/lib/money/money/formatting.rb +0 -417
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+
3
+ describe Money::LocaleBackend do
4
+ describe '.find' do
5
+ it 'returns an initialized backend' do
6
+ expect(described_class.find(:legacy)).to be_a(Money::LocaleBackend::Legacy)
7
+ expect(described_class.find(:i18n)).to be_a(Money::LocaleBackend::I18n)
8
+ end
9
+
10
+ it 'raises an error if a backend is unknown' do
11
+ expect { described_class.find(:foo) }.to raise_error(Money::LocaleBackend::Unknown)
12
+ end
13
+ end
14
+ end
@@ -1,6 +1,22 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  describe Money do
4
+ describe '.locale_backend' do
5
+ after { Money.locale_backend = :legacy }
6
+
7
+ it 'sets the locale_backend' do
8
+ Money.locale_backend = :i18n
9
+
10
+ expect(Money.locale_backend).to be_a(Money::LocaleBackend::I18n)
11
+ end
12
+
13
+ it 'sets the locale_backend to nil' do
14
+ Money.locale_backend = nil
15
+
16
+ expect(Money.locale_backend).to eq(nil)
17
+ end
18
+ end
19
+
4
20
  describe ".new" do
5
21
  let(:initializing_value) { 1 }
6
22
  subject(:money) { Money.new(initializing_value) }
@@ -169,6 +185,16 @@ describe Money do
169
185
  Money.from_amount(1.999).to_d
170
186
  end).to eq 1.99
171
187
  end
188
+
189
+ context 'given a currency is provided' do
190
+ context 'and the currency is nil' do
191
+ let(:currency) { nil }
192
+
193
+ it "should have the default currency" do
194
+ expect(Money.from_amount(1, currency).currency).to eq Money.default_currency
195
+ end
196
+ end
197
+ end
172
198
  end
173
199
 
174
200
  %w[cents pence].each do |units|
@@ -191,7 +217,7 @@ describe Money do
191
217
  it "stores fractional as an integer regardless of what is passed into the constructor" do
192
218
  m = Money.new(100)
193
219
  expect(m.fractional).to eq 100
194
- expect(m.fractional).to be_a(Fixnum)
220
+ expect(m.fractional).to be_a(Integer)
195
221
  end
196
222
 
197
223
  context "loading a serialized Money via YAML" do
@@ -349,9 +375,9 @@ YAML
349
375
  expect {money.round_to_nearest_cash_value}.to raise_error(Money::UndefinedSmallestDenomination)
350
376
  end
351
377
 
352
- it "returns a Fixnum when infinite_precision is not set" do
378
+ it "returns a Integer when infinite_precision is not set" do
353
379
  money = Money.new(100, "USD")
354
- expect(money.round_to_nearest_cash_value).to be_a Fixnum
380
+ expect(money.round_to_nearest_cash_value).to be_a Integer
355
381
  end
356
382
 
357
383
  it "returns a BigDecimal when infinite_precision is set", :infinite_precision do
@@ -408,12 +434,16 @@ YAML
408
434
  end
409
435
 
410
436
  describe "#currency_as_string=" do
411
- it "sets the currency object using the provided string" do
437
+ it "sets the currency object using the provided string leaving cents intact" do
412
438
  money = Money.new(100_00, "USD")
439
+
413
440
  money.currency_as_string = "EUR"
414
441
  expect(money.currency).to eq Money::Currency.new("EUR")
442
+ expect(money.cents).to eq 100_00
443
+
415
444
  money.currency_as_string = "YEN"
416
445
  expect(money.currency).to eq Money::Currency.new("YEN")
446
+ expect(money.cents).to eq 100_00
417
447
  end
418
448
  end
419
449
 
@@ -468,6 +498,24 @@ YAML
468
498
  expect(Money.new(10_00, "BRL").to_s).to eq "10,00"
469
499
  end
470
500
 
501
+ context "using i18n" do
502
+ before { I18n.backend.store_translations(:en, number: { format: { separator: "." } }) }
503
+ after { reset_i18n }
504
+
505
+ it "respects decimal mark" do
506
+ expect(Money.new(10_00, "BRL").to_s).to eq "10.00"
507
+ end
508
+ end
509
+
510
+ context "with defaults set" do
511
+ before { Money.default_formatting_rules = { with_currency: true } }
512
+ after { Money.default_formatting_rules = nil }
513
+
514
+ it "ignores defaults" do
515
+ expect(Money.new(10_00, 'USD').to_s).to eq '10.00'
516
+ end
517
+ end
518
+
471
519
  context "with infinite_precision", :infinite_precision do
472
520
  it "shows fractional cents" do
473
521
  expect(Money.new(1.05, "USD").to_s).to eq "0.0105"
@@ -536,6 +584,25 @@ YAML
536
584
  end
537
585
  end
538
586
 
587
+ describe "#with_currency" do
588
+ it 'returns self if currency is the same' do
589
+ money = Money.new(10_00, 'USD')
590
+
591
+ expect(money.with_currency('USD')).to eq(money)
592
+ expect(money.with_currency('USD').object_id).to eq(money.object_id)
593
+ end
594
+
595
+ it 'returns a new instance in a given currency' do
596
+ money = Money.new(10_00, 'USD')
597
+ new_money = money.with_currency('EUR')
598
+
599
+ expect(new_money).to eq(Money.new(10_00, 'EUR'))
600
+ expect(money.fractional).to eq(new_money.fractional)
601
+ expect(money.bank).to eq(new_money.bank)
602
+ expect(money.object_id).not_to eq(new_money.object_id)
603
+ end
604
+ end
605
+
539
606
  describe "#exchange_to" do
540
607
  it "exchanges the amount via its exchange bank" do
541
608
  money = Money.new(100_00, "USD")
@@ -577,6 +644,18 @@ YAML
577
644
  expect(moneys[1]).to eq Money.us_dollar(3)
578
645
  end
579
646
 
647
+ it "handles small splits" do
648
+ moneys = Money.us_dollar(5).allocate([0.03, 0.07])
649
+ expect(moneys[0]).to eq Money.us_dollar(2)
650
+ expect(moneys[1]).to eq Money.us_dollar(3)
651
+ end
652
+
653
+ it "handles large splits" do
654
+ moneys = Money.us_dollar(5).allocate([3, 7])
655
+ expect(moneys[0]).to eq Money.us_dollar(2)
656
+ expect(moneys[1]).to eq Money.us_dollar(3)
657
+ end
658
+
580
659
  it "does not lose pennies" do
581
660
  moneys = Money.us_dollar(100).allocate([0.333, 0.333, 0.333])
582
661
  expect(moneys[0].cents).to eq 34
@@ -584,8 +663,37 @@ YAML
584
663
  expect(moneys[2].cents).to eq 33
585
664
  end
586
665
 
587
- it "requires total to be less then 1" do
588
- expect { Money.us_dollar(0.05).allocate([0.5, 0.6]) }.to raise_error(ArgumentError)
666
+ it "does not round rationals" do
667
+ splits = 7.times.map { Rational(950, 6650) }
668
+ moneys = Money.us_dollar(6650).allocate(splits)
669
+ moneys.each do |money|
670
+ expect(money.cents).to eq 950
671
+ end
672
+ end
673
+
674
+ it "handles mixed split types" do
675
+ splits = [Rational(1, 4), 0.25, 0.25, BigDecimal('0.25')]
676
+ moneys = Money.us_dollar(100).allocate(splits)
677
+ moneys.each do |money|
678
+ expect(money.cents).to eq 25
679
+ end
680
+ end
681
+
682
+ context "negative amount" do
683
+ it "does not lose pennies" do
684
+ moneys = Money.us_dollar(-100).allocate([0.333, 0.333, 0.333])
685
+
686
+ expect(moneys[0].cents).to eq(-34)
687
+ expect(moneys[1].cents).to eq(-33)
688
+ expect(moneys[2].cents).to eq(-33)
689
+ end
690
+
691
+ it "allocates the same way as positive amounts" do
692
+ ratios = [0.6667, 0.3333]
693
+
694
+ expect(Money.us_dollar(10_00).allocate(ratios).map(&:fractional)).to eq([6_67, 3_33])
695
+ expect(Money.us_dollar(-10_00).allocate(ratios).map(&:fractional)).to eq([-6_67, -3_33])
696
+ end
589
697
  end
590
698
 
591
699
  it "keeps subclasses intact" do
@@ -595,12 +703,8 @@ YAML
595
703
 
596
704
  context "with infinite_precision", :infinite_precision do
597
705
  it "allows for fractional cents allocation" do
598
- one_third = BigDecimal("1") / BigDecimal("3")
599
-
600
- moneys = Money.new(100).allocate([one_third, one_third, one_third])
601
- expect(moneys[0].cents).to eq one_third * BigDecimal("100")
602
- expect(moneys[1].cents).to eq one_third * BigDecimal("100")
603
- expect(moneys[2].cents).to eq one_third * BigDecimal("100")
706
+ moneys = Money.new(100).allocate([1, 1, 1])
707
+ expect(moneys.inject(0, :+)).to eq(Money.new(100))
604
708
  end
605
709
  end
606
710
  end
@@ -637,27 +741,40 @@ YAML
637
741
 
638
742
  context "with infinite_precision", :infinite_precision do
639
743
  it "allows for splitting by fractional cents" do
640
- thirty_three_and_one_third = BigDecimal("100") / BigDecimal("3")
641
-
642
744
  moneys = Money.new(100).split(3)
643
- expect(moneys[0].cents).to eq thirty_three_and_one_third
644
- expect(moneys[1].cents).to eq thirty_three_and_one_third
645
- expect(moneys[2].cents).to eq thirty_three_and_one_third
745
+ expect(moneys.inject(0, :+)).to eq(Money.new(100))
646
746
  end
647
747
  end
648
748
  end
649
749
 
650
750
  describe "#round" do
651
-
652
751
  let(:money) { Money.new(15.75, 'NZD') }
653
752
  subject(:rounded) { money.round }
654
753
 
655
754
  context "without infinite_precision" do
656
- it "returns self (as it is already rounded)" do
657
- rounded = money.round
658
- expect(rounded).to be money
755
+ it "returns a different money" do
756
+ expect(rounded).not_to be money
757
+ end
758
+
759
+ it "rounds the cents" do
659
760
  expect(rounded.cents).to eq 16
660
761
  end
762
+
763
+ it "maintains the currency" do
764
+ expect(rounded.currency).to eq Money::Currency.new('NZD')
765
+ end
766
+
767
+ it "uses a provided rounding strategy" do
768
+ rounded = money.round(BigDecimal::ROUND_DOWN)
769
+ expect(rounded.cents).to eq 15
770
+ end
771
+
772
+ it "does not accumulate rounding error" do
773
+ money_1 = Money.new(10.9).round(BigDecimal::ROUND_DOWN)
774
+ money_2 = Money.new(10.9).round(BigDecimal::ROUND_DOWN)
775
+
776
+ expect(money_1 + money_2).to eq(Money.new(20))
777
+ end
661
778
  end
662
779
 
663
780
  context "with infinite_precision", :infinite_precision do
@@ -678,15 +795,31 @@ YAML
678
795
  expect(rounded.cents).to eq 15
679
796
  end
680
797
 
681
- context "when using a subclass of Money" do
682
- let(:special_money_class) { Class.new(Money) }
683
- let(:money) { special_money_class.new(15.75, 'NZD') }
798
+ context "when using a specific rounding precision" do
799
+ let(:money) { Money.new(15.7526, 'NZD') }
684
800
 
685
- it "preserves the class in the result" do
686
- expect(rounded).to be_a special_money_class
801
+ it "uses the provided rounding precision" do
802
+ rounded = money.round(BigDecimal::ROUND_DOWN, 3)
803
+ expect(rounded.fractional).to eq 15.752
687
804
  end
688
805
  end
689
806
  end
807
+
808
+ it 'preserves assigned bank' do
809
+ bank = Money::Bank::VariableExchange.new
810
+ rounded = Money.new(1_00, 'USD', bank).round
811
+
812
+ expect(rounded.bank).to eq(bank)
813
+ end
814
+
815
+ context "when using a subclass of Money" do
816
+ let(:special_money_class) { Class.new(Money) }
817
+ let(:money) { special_money_class.new(15.75, 'NZD') }
818
+
819
+ it "preserves the class in the result" do
820
+ expect(rounded).to be_a special_money_class
821
+ end
822
+ end
690
823
  end
691
824
 
692
825
  describe "#inspect" do
@@ -15,7 +15,7 @@ describe Money::RatesStore::Memory do
15
15
  end
16
16
 
17
17
  context ':without_mutex' do
18
- let(:subject) { Money::RatesStore::Memory.new(:without_mutex => true) }
18
+ let(:subject) { Money::RatesStore::Memory.new(without_mutex: true) }
19
19
 
20
20
  it "doesn't use mutex if requested not to" do
21
21
  expect(subject.instance_variable_get(:@mutex)).not_to receive(:synchronize)
@@ -58,7 +58,7 @@ describe Money::RatesStore::Memory do
58
58
  end
59
59
 
60
60
  context 'no mutex' do
61
- let(:subject) { Money::RatesStore::Memory.new(:without_mutex => true) }
61
+ let(:subject) { Money::RatesStore::Memory.new(without_mutex: true) }
62
62
 
63
63
  it 'does not use mutex' do
64
64
  expect(subject.instance_variable_get('@mutex')).not_to receive(:synchronize)
@@ -66,4 +66,15 @@ describe Money::RatesStore::Memory do
66
66
  end
67
67
  end
68
68
  end
69
+
70
+ describe '#marshal_dump' do
71
+ let(:subject) { Money::RatesStore::Memory.new(optional: true) }
72
+
73
+ it 'can reload' do
74
+ bank = Money::Bank::VariableExchange.new(subject)
75
+ bank = Marshal.load(Marshal.dump(bank))
76
+ expect(bank.store.instance_variable_get(:@options)).to eq subject.instance_variable_get(:@options)
77
+ expect(bank.store.instance_variable_get(:@index)).to eq subject.instance_variable_get(:@index)
78
+ end
79
+ end
69
80
  end
@@ -5,6 +5,8 @@ $LOAD_PATH.unshift File.dirname(__FILE__)
5
5
  require "rspec"
6
6
  require "money"
7
7
 
8
+ Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
9
+
8
10
  I18n.enforce_available_locales = false
9
11
 
10
12
  RSpec.configure do |c|
@@ -0,0 +1,14 @@
1
+ RSpec.shared_examples 'instance with custom bank' do |operation, value|
2
+ let(:custom_bank) { Money::Bank::VariableExchange.new }
3
+ let(:instance) { Money.new(1, :usd, custom_bank) }
4
+
5
+ subject { value ? instance.send(operation, value) : instance.send(operation) }
6
+
7
+ it "returns custom bank from new instance" do
8
+ new_money_instances = Array(subject).select { |el| el.is_a?(Money) }
9
+
10
+ new_money_instances.each do |money_instance|
11
+ expect(money_instance.bank).to eq(custom_bank)
12
+ end
13
+ end
14
+ end
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.7.0
4
+ version: 6.13.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: 2016-01-15 00:00:00.000000000 Z
11
+ date: 2018-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: 0.6.4
20
20
  - - "<="
21
21
  - !ruby/object:Gem::Version
22
- version: 0.7.0
22
+ version: '2'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -28,26 +28,6 @@ dependencies:
28
28
  - !ruby/object:Gem::Version
29
29
  version: 0.6.4
30
30
  - - "<="
31
- - !ruby/object:Gem::Version
32
- version: 0.7.0
33
- - !ruby/object:Gem::Dependency
34
- name: sixarm_ruby_unaccent
35
- requirement: !ruby/object:Gem::Requirement
36
- requirements:
37
- - - ">="
38
- - !ruby/object:Gem::Version
39
- version: 1.1.1
40
- - - "<"
41
- - !ruby/object:Gem::Version
42
- version: '2'
43
- type: :runtime
44
- prerelease: false
45
- version_requirements: !ruby/object:Gem::Requirement
46
- requirements:
47
- - - ">="
48
- - !ruby/object:Gem::Version
49
- version: 1.1.1
50
- - - "<"
51
31
  - !ruby/object:Gem::Version
52
32
  version: '2'
53
33
  - !ruby/object:Gem::Dependency
@@ -84,28 +64,28 @@ dependencies:
84
64
  requirements:
85
65
  - - "~>"
86
66
  - !ruby/object:Gem::Version
87
- version: 3.4.0
67
+ version: '3.4'
88
68
  type: :development
89
69
  prerelease: false
90
70
  version_requirements: !ruby/object:Gem::Requirement
91
71
  requirements:
92
72
  - - "~>"
93
73
  - !ruby/object:Gem::Version
94
- version: 3.4.0
74
+ version: '3.4'
95
75
  - !ruby/object:Gem::Dependency
96
76
  name: yard
97
77
  requirement: !ruby/object:Gem::Requirement
98
78
  requirements:
99
79
  - - "~>"
100
80
  - !ruby/object:Gem::Version
101
- version: '0.8'
81
+ version: 0.9.11
102
82
  type: :development
103
83
  prerelease: false
104
84
  version_requirements: !ruby/object:Gem::Requirement
105
85
  requirements:
106
86
  - - "~>"
107
87
  - !ruby/object:Gem::Version
108
- version: '0.8'
88
+ version: 0.9.11
109
89
  - !ruby/object:Gem::Dependency
110
90
  name: kramdown
111
91
  requirement: !ruby/object:Gem::Requirement
@@ -148,10 +128,17 @@ files:
148
128
  - lib/money/currency.rb
149
129
  - lib/money/currency/heuristics.rb
150
130
  - lib/money/currency/loader.rb
131
+ - lib/money/locale_backend/base.rb
132
+ - lib/money/locale_backend/errors.rb
133
+ - lib/money/locale_backend/i18n.rb
134
+ - lib/money/locale_backend/legacy.rb
151
135
  - lib/money/money.rb
136
+ - lib/money/money/allocation.rb
152
137
  - lib/money/money/arithmetic.rb
153
138
  - lib/money/money/constructors.rb
154
- - lib/money/money/formatting.rb
139
+ - lib/money/money/formatter.rb
140
+ - lib/money/money/formatting_rules.rb
141
+ - lib/money/money/locale_backend.rb
155
142
  - lib/money/rates_store/memory.rb
156
143
  - lib/money/version.rb
157
144
  - money.gemspec
@@ -161,25 +148,25 @@ files:
161
148
  - spec/currency/heuristics_spec.rb
162
149
  - spec/currency/loader_spec.rb
163
150
  - spec/currency_spec.rb
151
+ - spec/locale_backend/i18n_spec.rb
152
+ - spec/locale_backend/legacy_spec.rb
153
+ - spec/money/allocation_spec.rb
164
154
  - spec/money/arithmetic_spec.rb
165
155
  - spec/money/constructors_spec.rb
166
156
  - spec/money/formatting_spec.rb
157
+ - spec/money/locale_backend_spec.rb
167
158
  - spec/money_spec.rb
168
159
  - spec/rates_store/memory_spec.rb
169
160
  - spec/spec_helper.rb
170
- homepage: http://rubymoney.github.io/money
161
+ - spec/support/shared_examples/money_examples.rb
162
+ homepage: https://rubymoney.github.io/money
171
163
  licenses:
172
164
  - MIT
173
- metadata: {}
174
- post_install_message: |
175
- Please note the following API changes in Money version 6
176
-
177
- - Money#amount, Money#dollars methods now return instances of BigDecimal (rather than Float).
178
-
179
- Please read the migration notes at https://github.com/RubyMoney/money#migration-notes
180
- and choose the migration that best suits your application.
181
-
182
- Test responsibly :-)
165
+ metadata:
166
+ changelog_uri: https://github.com/RubyMoney/money/blob/master/CHANGELOG.md
167
+ source_code_uri: https://github.com/RubyMoney/money/
168
+ bug_tracker_uri: https://github.com/RubyMoney/money/issues
169
+ post_install_message:
183
170
  rdoc_options: []
184
171
  require_paths:
185
172
  - lib
@@ -195,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
195
182
  version: '0'
196
183
  requirements: []
197
184
  rubyforge_project:
198
- rubygems_version: 2.4.5.1
185
+ rubygems_version: 2.6.8
199
186
  signing_key:
200
187
  specification_version: 4
201
188
  summary: A Ruby Library for dealing with money and currency conversion.
@@ -206,10 +193,14 @@ test_files:
206
193
  - spec/currency/heuristics_spec.rb
207
194
  - spec/currency/loader_spec.rb
208
195
  - spec/currency_spec.rb
196
+ - spec/locale_backend/i18n_spec.rb
197
+ - spec/locale_backend/legacy_spec.rb
198
+ - spec/money/allocation_spec.rb
209
199
  - spec/money/arithmetic_spec.rb
210
200
  - spec/money/constructors_spec.rb
211
201
  - spec/money/formatting_spec.rb
202
+ - spec/money/locale_backend_spec.rb
212
203
  - spec/money_spec.rb
213
204
  - spec/rates_store/memory_spec.rb
214
205
  - spec/spec_helper.rb
215
- has_rdoc:
206
+ - spec/support/shared_examples/money_examples.rb