money 6.13.0 → 6.13.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +49 -2
  3. data/README.md +93 -3
  4. data/config/currency_backwards_compatible.json +31 -0
  5. data/config/currency_iso.json +18 -18
  6. data/lib/money/bank/variable_exchange.rb +14 -6
  7. data/lib/money/currency.rb +4 -4
  8. data/lib/money/currency/loader.rb +15 -13
  9. data/lib/money/locale_backend/currency.rb +11 -0
  10. data/lib/money/locale_backend/i18n.rb +2 -1
  11. data/lib/money/locale_backend/legacy.rb +2 -2
  12. data/lib/money/money.rb +100 -52
  13. data/lib/money/money/allocation.rb +8 -5
  14. data/lib/money/money/arithmetic.rb +20 -10
  15. data/lib/money/money/formatter.rb +3 -0
  16. data/lib/money/money/formatting_rules.rb +15 -5
  17. data/lib/money/money/locale_backend.rb +3 -1
  18. data/lib/money/rates_store/memory.rb +24 -23
  19. data/lib/money/version.rb +1 -1
  20. data/money.gemspec +4 -4
  21. metadata +11 -51
  22. data/.coveralls.yml +0 -1
  23. data/.gitignore +0 -23
  24. data/.rspec +0 -2
  25. data/.travis.yml +0 -32
  26. data/AUTHORS +0 -131
  27. data/CONTRIBUTING.md +0 -17
  28. data/Gemfile +0 -16
  29. data/Rakefile +0 -17
  30. data/spec/bank/base_spec.rb +0 -79
  31. data/spec/bank/single_currency_spec.rb +0 -13
  32. data/spec/bank/variable_exchange_spec.rb +0 -265
  33. data/spec/currency/heuristics_spec.rb +0 -11
  34. data/spec/currency/loader_spec.rb +0 -19
  35. data/spec/currency_spec.rb +0 -400
  36. data/spec/locale_backend/i18n_spec.rb +0 -62
  37. data/spec/locale_backend/legacy_spec.rb +0 -74
  38. data/spec/money/allocation_spec.rb +0 -130
  39. data/spec/money/arithmetic_spec.rb +0 -756
  40. data/spec/money/constructors_spec.rb +0 -91
  41. data/spec/money/formatting_spec.rb +0 -855
  42. data/spec/money/locale_backend_spec.rb +0 -14
  43. data/spec/money_spec.rb +0 -880
  44. data/spec/rates_store/memory_spec.rb +0 -80
  45. data/spec/spec_helper.rb +0 -30
  46. data/spec/support/shared_examples/money_examples.rb +0 -14
@@ -1,17 +0,0 @@
1
- # Contribution Guidelines
2
-
3
- ## Steps
4
-
5
- 1. Fork [the repo](https://github.com/RubyMoney/money)
6
- 2. Grab dependencies: `bundle install`
7
- 3. Make sure everything is working: `bundle exec rake spec`
8
- 4. Make your changes
9
- 5. Test your changes
10
- 5. Create a Pull Request
11
- 6. Celebrate!!!!!
12
-
13
- ## Notes
14
-
15
- When contributing, please make sure to update the CHANGELOG and AUTHORS files
16
- when you submit your pull request. Upon merging of your first pull request,
17
- you will be given commit access to the repository.
data/Gemfile DELETED
@@ -1,16 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- gem 'coveralls', '>= 0.8.17', require: false
4
- gem 'pry', require: false
5
-
6
- # JSON gem no longer supports ruby < 2.0.0
7
- if defined?(JRUBY_VERSION)
8
- gem 'json'
9
- elsif RUBY_VERSION =~ /^1/
10
- # Legacy gem locks for ruby 1.9.x
11
- gem 'json', '~> 1.8.3'
12
- gem 'tins', '~> 1.6.0'
13
- gem 'term-ansicolor', '< 1.4'
14
- end
15
-
16
- gemspec
data/Rakefile DELETED
@@ -1,17 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rake/clean"
3
- require "rspec/core/rake_task"
4
-
5
- CLOBBER.include('doc', '.yardoc')
6
-
7
- require "yard"
8
-
9
- YARD::Rake::YardocTask.new do |t|
10
- t.options << "--files" << "CHANGELOG.md,LICENSE"
11
- end
12
-
13
- RSpec::Core::RakeTask.new(:spec) do |t|
14
- t.fail_on_error = false
15
- end
16
-
17
- task default: :spec
@@ -1,79 +0,0 @@
1
- class Money
2
- module Bank
3
- describe Base do
4
-
5
- describe ".instance" do
6
- it "is local to one class" do
7
- klass = Base
8
- subclass = Class.new(Base)
9
- expect(klass.instance).not_to eq subclass.instance
10
- end
11
- end
12
-
13
- describe "#initialize" do
14
- it "accepts a block and stores @rounding_method" do
15
- proc = Proc.new { |n| n.ceil }
16
- bank = Base.new(&proc)
17
- expect(bank.rounding_method).to eq proc
18
- end
19
- end
20
-
21
- describe "#setup" do
22
- it "calls #setup after #initialize" do
23
- class MyBank < Base
24
- attr_reader :setup_called
25
-
26
- def setup
27
- @setup_called = true
28
- end
29
- end
30
-
31
- bank = MyBank.new
32
- expect(bank.setup_called).to eq true
33
- end
34
- end
35
-
36
- describe "#exchange_with" do
37
- it "is not implemented" do
38
- expect { subject.exchange_with(Money.new(100, 'USD'), 'EUR') }.to raise_exception(NotImplementedError)
39
- end
40
- end
41
-
42
- describe "#same_currency?" do
43
- it "accepts str/str" do
44
- expect { subject.send(:same_currency?, 'USD', 'EUR') }.to_not raise_exception
45
- end
46
-
47
- it "accepts currency/str" do
48
- expect { subject.send(:same_currency?, Currency.wrap('USD'), 'EUR') }.to_not raise_exception
49
- end
50
-
51
- it "accepts str/currency" do
52
- expect { subject.send(:same_currency?, 'USD', Currency.wrap('EUR')) }.to_not raise_exception
53
- end
54
-
55
- it "accepts currency/currency" do
56
- expect { subject.send(:same_currency?, Currency.wrap('USD'), Currency.wrap('EUR')) }.to_not raise_exception
57
- end
58
-
59
- it "returns true when currencies match" do
60
- expect(subject.send(:same_currency?, 'USD', 'USD')).to be true
61
- expect(subject.send(:same_currency?, Currency.wrap('USD'), 'USD')).to be true
62
- expect(subject.send(:same_currency?, 'USD', Currency.wrap('USD'))).to be true
63
- expect(subject.send(:same_currency?, Currency.wrap('USD'), Currency.wrap('USD'))).to be true
64
- end
65
-
66
- it "returns false when currencies do not match" do
67
- expect(subject.send(:same_currency?, 'USD', 'EUR')).to be false
68
- expect(subject.send(:same_currency?, Currency.wrap('USD'), 'EUR')).to be false
69
- expect(subject.send(:same_currency?, 'USD', Currency.wrap('EUR'))).to be false
70
- expect(subject.send(:same_currency?, Currency.wrap('USD'), Currency.wrap('EUR'))).to be false
71
- end
72
-
73
- it "raises an UnknownCurrency exception when an unknown currency is passed" do
74
- expect { subject.send(:same_currency?, 'AAA', 'BBB') }.to raise_exception(Currency::UnknownCurrency)
75
- end
76
- end
77
- end
78
- end
79
- end
@@ -1,13 +0,0 @@
1
- class Money
2
- module Bank
3
- describe SingleCurrency do
4
- describe "#exchange_with" do
5
- it "raises when called" do
6
- expect {
7
- subject.exchange_with(Money.new(100, 'USD'), 'EUR')
8
- }.to raise_exception(DifferentCurrencyError, "No exchanging of currencies allowed: 1.00 USD to EUR")
9
- end
10
- end
11
- end
12
- end
13
- end
@@ -1,265 +0,0 @@
1
- require 'json'
2
- require 'yaml'
3
-
4
- class Money
5
- module Bank
6
- describe VariableExchange do
7
-
8
- describe "#initialize" do
9
- context "without &block" do
10
- let(:bank) {
11
- VariableExchange.new.tap do |bank|
12
- bank.add_rate('USD', 'EUR', 1.33)
13
- end
14
- }
15
-
16
- describe '#store' do
17
- it 'defaults to Memory store' do
18
- expect(bank.store).to be_a(Money::RatesStore::Memory)
19
- end
20
- end
21
-
22
- describe 'custom store' do
23
- let(:custom_store) { Object.new }
24
-
25
- let(:bank) { VariableExchange.new(custom_store) }
26
-
27
- it 'sets #store to be custom store' do
28
- expect(bank.store).to eql(custom_store)
29
- end
30
- end
31
-
32
- describe "#exchange_with" do
33
- it "accepts str" do
34
- expect { bank.exchange_with(Money.new(100, 'USD'), 'EUR') }.to_not raise_exception
35
- end
36
-
37
- it "accepts currency" do
38
- expect { bank.exchange_with(Money.new(100, 'USD'), Currency.wrap('EUR')) }.to_not raise_exception
39
- end
40
-
41
- it "exchanges one currency to another" do
42
- expect(bank.exchange_with(Money.new(100, 'USD'), 'EUR')).to eq Money.new(133, 'EUR')
43
- end
44
-
45
- it "truncates extra digits" do
46
- expect(bank.exchange_with(Money.new(10, 'USD'), 'EUR')).to eq Money.new(13, 'EUR')
47
- end
48
-
49
- it "raises an UnknownCurrency exception when an unknown currency is requested" do
50
- expect { bank.exchange_with(Money.new(100, 'USD'), 'BBB') }.to raise_exception(Currency::UnknownCurrency)
51
- end
52
-
53
- it "raises an UnknownRate exception when an unknown rate is requested" do
54
- expect { bank.exchange_with(Money.new(100, 'USD'), 'JPY') }.to raise_exception(UnknownRate)
55
- end
56
-
57
- #it "rounds the exchanged result down" do
58
- # bank.add_rate("USD", "EUR", 0.788332676)
59
- # bank.add_rate("EUR", "YEN", 122.631477)
60
- # expect(bank.exchange_with(Money.new(10_00, "USD"), "EUR")).to eq Money.new(788, "EUR")
61
- # expect(bank.exchange_with(Money.new(500_00, "EUR"), "YEN")).to eq Money.new(6131573, "YEN")
62
- #end
63
-
64
- it "accepts a custom truncation method" do
65
- proc = Proc.new { |n| n.ceil }
66
- expect(bank.exchange_with(Money.new(10, 'USD'), 'EUR', &proc)).to eq Money.new(14, 'EUR')
67
- end
68
-
69
- it 'works with big numbers' do
70
- amount = 10**20
71
- expect(bank.exchange_with(Money.usd(amount), :EUR)).to eq Money.eur(1.33 * amount)
72
- end
73
-
74
- it "preserves the class in the result when given a subclass of Money" do
75
- special_money_class = Class.new(Money)
76
- expect(bank.exchange_with(special_money_class.new(100, 'USD'), 'EUR')).to be_a special_money_class
77
- end
78
-
79
- it "doesn't loose precision when handling larger amounts" do
80
- expect(bank.exchange_with(Money.new(100_000_000_000_000_01, 'USD'), 'EUR')).to eq Money.new(133_000_000_000_000_01, 'EUR')
81
- end
82
- end
83
- end
84
-
85
- context "with &block" do
86
- let(:bank) {
87
- proc = Proc.new { |n| n.ceil }
88
- VariableExchange.new(&proc).tap do |bank|
89
- bank.add_rate('USD', 'EUR', 1.33)
90
- end
91
- }
92
-
93
- describe "#exchange_with" do
94
- it "uses the stored truncation method" do
95
- expect(bank.exchange_with(Money.new(10, 'USD'), 'EUR')).to eq Money.new(14, 'EUR')
96
- end
97
-
98
- it "accepts a custom truncation method" do
99
- proc = Proc.new { |n| n.ceil + 1 }
100
- expect(bank.exchange_with(Money.new(10, 'USD'), 'EUR', &proc)).to eq Money.new(15, 'EUR')
101
- end
102
- end
103
- end
104
- end
105
-
106
- describe "#add_rate" do
107
- it 'delegates to store#add_rate' do
108
- expect(subject.store).to receive(:add_rate).with('USD', 'EUR', 1.25).and_return 1.25
109
- expect(subject.add_rate('USD', 'EUR', 1.25)).to eql 1.25
110
- end
111
-
112
- it "adds rates with correct ISO codes" do
113
- expect(subject.store).to receive(:add_rate).with('USD', 'EUR', 0.788332676)
114
- subject.add_rate("USD", "EUR", 0.788332676)
115
-
116
- expect(subject.store).to receive(:add_rate).with('EUR', 'JPY', 122.631477)
117
- subject.add_rate("EUR", "YEN", 122.631477)
118
- end
119
-
120
- it "treats currency names case-insensitively" do
121
- subject.add_rate("usd", "eur", 1)
122
- expect(subject.get_rate('USD', 'EUR')).to eq 1
123
- end
124
- end
125
-
126
- describe "#set_rate" do
127
- it 'delegates to store#add_rate' do
128
- expect(subject.store).to receive(:add_rate).with('USD', 'EUR', 1.25).and_return 1.25
129
- expect(subject.set_rate('USD', 'EUR', 1.25)).to eql 1.25
130
- end
131
-
132
- it "sets a rate" do
133
- subject.set_rate('USD', 'EUR', 1.25)
134
- expect(subject.store.get_rate('USD', 'EUR')).to eq 1.25
135
- end
136
-
137
- it "raises an UnknownCurrency exception when an unknown currency is passed" do
138
- expect { subject.set_rate('AAA', 'BBB', 1.25) }.to raise_exception(Currency::UnknownCurrency)
139
- end
140
- end
141
-
142
- describe "#get_rate" do
143
- it "returns a rate" do
144
- subject.set_rate('USD', 'EUR', 1.25)
145
- expect(subject.get_rate('USD', 'EUR')).to eq 1.25
146
- end
147
-
148
- it "raises an UnknownCurrency exception when an unknown currency is passed" do
149
- expect { subject.get_rate('AAA', 'BBB') }.to raise_exception(Currency::UnknownCurrency)
150
- end
151
-
152
- it "delegates options to store, options are a no-op" do
153
- expect(subject.store).to receive(:get_rate).with('USD', 'EUR')
154
- subject.get_rate('USD', 'EUR', without_mutex: true)
155
- end
156
- end
157
-
158
- describe "#export_rates" do
159
- before :each do
160
- subject.set_rate('USD', 'EUR', 1.25)
161
- subject.set_rate('USD', 'JPY', 2.55)
162
-
163
- @rates = { "USD_TO_EUR" => 1.25, "USD_TO_JPY" => 2.55 }
164
- end
165
-
166
- context "with format == :json" do
167
- it "should return rates formatted as json" do
168
- json = subject.export_rates(:json)
169
- expect(JSON.load(json)).to eq @rates
170
- end
171
- end
172
-
173
- context "with format == :ruby" do
174
- it "should return rates formatted as ruby objects" do
175
- expect(Marshal.load(subject.export_rates(:ruby))).to eq @rates
176
- end
177
- end
178
-
179
- context "with format == :yaml" do
180
- it "should return rates formatted as yaml" do
181
- yaml = subject.export_rates(:yaml)
182
- expect(YAML.load(yaml)).to eq @rates
183
- end
184
- end
185
-
186
- context "with unknown format" do
187
- it "raises Money::Bank::UnknownRateFormat" do
188
- expect { subject.export_rates(:foo)}.to raise_error UnknownRateFormat
189
- end
190
- end
191
-
192
- context "with :file provided" do
193
- it "writes rates to file" do
194
- f = double('IO')
195
- expect(File).to receive(:open).with('null', 'w').and_yield(f)
196
- expect(f).to receive(:write).with(JSON.dump(@rates))
197
-
198
- subject.export_rates(:json, 'null')
199
- end
200
- end
201
-
202
- it "delegates execution to store, options are a no-op" do
203
- expect(subject.store).to receive(:transaction)
204
- subject.export_rates(:yaml, nil, foo: 1)
205
- end
206
-
207
- end
208
-
209
- describe "#import_rates" do
210
- context "with format == :json" do
211
- it "loads the rates provided" do
212
- s = '{"USD_TO_EUR":1.25,"USD_TO_JPY":2.55}'
213
- subject.import_rates(:json, s)
214
- expect(subject.get_rate('USD', 'EUR')).to eq 1.25
215
- expect(subject.get_rate('USD', 'JPY')).to eq 2.55
216
- end
217
- end
218
-
219
- context "with format == :ruby" do
220
- it "loads the rates provided" do
221
- s = Marshal.dump({"USD_TO_EUR"=>1.25,"USD_TO_JPY"=>2.55})
222
- subject.import_rates(:ruby, s)
223
- expect(subject.get_rate('USD', 'EUR')).to eq 1.25
224
- expect(subject.get_rate('USD', 'JPY')).to eq 2.55
225
- end
226
- end
227
-
228
- context "with format == :yaml" do
229
- it "loads the rates provided" do
230
- s = "--- \nUSD_TO_EUR: 1.25\nUSD_TO_JPY: 2.55\n"
231
- subject.import_rates(:yaml, s)
232
- expect(subject.get_rate('USD', 'EUR')).to eq 1.25
233
- expect(subject.get_rate('USD', 'JPY')).to eq 2.55
234
- end
235
- end
236
-
237
- context "with unknown format" do
238
- it "raises Money::Bank::UnknownRateFormat" do
239
- expect { subject.import_rates(:foo, "")}.to raise_error UnknownRateFormat
240
- end
241
- end
242
-
243
- it "delegates execution to store#transaction" do
244
- expect(subject.store).to receive(:transaction)
245
- s = "--- \nUSD_TO_EUR: 1.25\nUSD_TO_JPY: 2.55\n"
246
- subject.import_rates(:yaml, s, foo: 1)
247
- end
248
-
249
- end
250
-
251
- describe "#marshal_dump" do
252
- it "does not raise an error" do
253
- expect { Marshal.dump(subject) }.to_not raise_error
254
- end
255
-
256
- it "works with Marshal.load" do
257
- bank = Marshal.load(Marshal.dump(subject))
258
-
259
- expect(bank.rates).to eq subject.rates
260
- expect(bank.rounding_method).to eq subject.rounding_method
261
- end
262
- end
263
- end
264
- end
265
- end
@@ -1,11 +0,0 @@
1
- # encoding: utf-8
2
-
3
- describe Money::Currency::Heuristics do
4
- describe "#analyze_string" do
5
- let(:it) { Money::Currency }
6
-
7
- it "it raises deprecation error" do
8
- expect{ it.analyze('123') }.to raise_error(StandardError, 'Heuristics deprecated, add `gem "money-heuristics"` to Gemfile')
9
- end
10
- end
11
- end
@@ -1,19 +0,0 @@
1
- # encoding: utf-8
2
-
3
- describe Money::Currency::Loader do
4
- class CurrencyLoader
5
- include Money::Currency::Loader
6
- end
7
-
8
- let(:loader) { CurrencyLoader.new }
9
-
10
- it "returns a currency table hash" do
11
- expect(loader.load_currencies).to be_a Hash
12
- end
13
-
14
- it "parse currency_iso.json & currency_non_iso.json & currency_backwards_compatible.json" do
15
- expect(loader).to receive(:parse_currency_file).exactly(3).times.and_return({})
16
-
17
- loader.load_currencies
18
- end
19
- end
@@ -1,400 +0,0 @@
1
- # encoding: utf-8
2
-
3
- class Money
4
- describe Currency do
5
-
6
- FOO = '{ "priority": 1, "iso_code": "FOO", "iso_numeric": "840", "name": "United States Dollar", "symbol": "$", "subunit": "Cent", "subunit_to_unit": 1000, "symbol_first": true, "html_entity": "$", "decimal_mark": ".", "thousands_separator": ",", "smallest_denomination": 1 }'
7
-
8
- def register_foo(opts={})
9
- foo_attrs = JSON.parse(FOO, symbolize_names: true)
10
- # Pass an array of attribute names to 'skip' to remove them from the 'FOO'
11
- # json before registering foo as a currency.
12
- Array(opts[:skip]).each { |attr| foo_attrs.delete(attr) }
13
- Money::Currency.register(foo_attrs)
14
- end
15
-
16
- def unregister_foo
17
- Currency.unregister(JSON.parse(FOO, symbolize_names: true))
18
- end
19
-
20
- describe "UnknownCurrency" do
21
- it "is a subclass of ArgumentError" do
22
- expect(Currency::UnknownCurrency < ArgumentError).to be true
23
- end
24
- end
25
-
26
- describe ".find" do
27
- before { register_foo }
28
- after { unregister_foo }
29
-
30
- it "returns currency matching given id" do
31
- expected = Currency.new(:foo)
32
- expect(Currency.find(:foo)).to be expected
33
- expect(Currency.find(:FOO)).to be expected
34
- expect(Currency.find("foo")).to be expected
35
- expect(Currency.find("FOO")).to be expected
36
- end
37
-
38
- it "returns nil unless currency matching given id" do
39
- expect(Currency.find("ZZZ")).to be_nil
40
- end
41
- end
42
-
43
- describe ".find_by_iso_numeric" do
44
- it "returns currency matching given numeric code" do
45
- expect(Currency.find_by_iso_numeric(978)).to eq Currency.new(:eur)
46
- expect(Currency.find_by_iso_numeric(208)).not_to eq Currency.new(:eur)
47
- expect(Currency.find_by_iso_numeric('840')).to eq Currency.new(:usd)
48
-
49
- class Mock
50
- def to_s
51
- '208'
52
- end
53
- end
54
- expect(Currency.find_by_iso_numeric(Mock.new)).to eq Currency.new(:dkk)
55
- expect(Currency.find_by_iso_numeric(Mock.new)).not_to eq Currency.new(:usd)
56
- end
57
-
58
- it "returns nil if no currency has the given numeric code" do
59
- expect(Currency.find_by_iso_numeric('non iso 4217 numeric code')).to be_nil
60
- expect(Currency.find_by_iso_numeric(0)).to be_nil
61
- end
62
-
63
- it "returns nil when given empty input" do
64
- expect(Currency.find_by_iso_numeric('')).to be_nil
65
- expect(Currency.find_by_iso_numeric(nil)).to be_nil
66
- end
67
- end
68
-
69
- describe ".wrap" do
70
- it "returns nil if object is nil" do
71
- expect(Currency.wrap(nil)).to be_nil
72
- expect(Currency.wrap(Currency.new(:usd))).to eq Currency.new(:usd)
73
- expect(Currency.wrap(:usd)).to eq Currency.new(:usd)
74
- end
75
- end
76
-
77
- describe ".all" do
78
- it "returns an array of currencies" do
79
- expect(Currency.all).to include Currency.new(:usd)
80
- end
81
- it "includes registered currencies" do
82
- register_foo
83
- expect(Currency.all).to include Currency.new(:foo)
84
- unregister_foo
85
- end
86
- it 'is sorted by priority' do
87
- expect(Currency.all.first.priority).to eq 1
88
- end
89
- it "raises a MissingAttributeError if any currency has no priority" do
90
- register_foo(skip: :priority)
91
-
92
- expect{Money::Currency.all}.to \
93
- raise_error(Money::Currency::MissingAttributeError, /foo.*priority/)
94
- unregister_foo
95
- end
96
- end
97
-
98
-
99
- describe ".register" do
100
- after { Currency.unregister(iso_code: "XXX") if Currency.find("XXX") }
101
-
102
- it "registers a new currency" do
103
- Currency.register(
104
- iso_code: "XXX",
105
- name: "Golden Doubloon",
106
- symbol: "%",
107
- subunit_to_unit: 100
108
- )
109
- new_currency = Currency.find("XXX")
110
- expect(new_currency).not_to be_nil
111
- expect(new_currency.name).to eq "Golden Doubloon"
112
- expect(new_currency.symbol).to eq "%"
113
- end
114
-
115
- specify ":iso_code must be present" do
116
- expect {
117
- Currency.register(name: "New Currency")
118
- }.to raise_error(KeyError)
119
- end
120
- end
121
-
122
-
123
- describe ".inherit" do
124
- after do
125
- Currency.unregister(iso_code: "XXX") if Currency.find("XXX")
126
- Currency.unregister(iso_code: "YYY") if Currency.find("YYY")
127
- end
128
-
129
- it "inherit a new currency" do
130
- Currency.register(
131
- iso_code: "XXX",
132
- name: "Golden Doubloon",
133
- symbol: "%",
134
- subunit_to_unit: 100
135
- )
136
- Currency.inherit("XXX",
137
- iso_code: "YYY",
138
- symbol: "@"
139
- )
140
- new_currency = Currency.find("YYY")
141
- expect(new_currency).not_to be_nil
142
- expect(new_currency.name).to eq "Golden Doubloon"
143
- expect(new_currency.symbol).to eq "@"
144
- expect(new_currency.subunit_to_unit).to eq 100
145
- end
146
- end
147
-
148
-
149
- describe ".unregister" do
150
- it "unregisters a currency" do
151
- Currency.register(iso_code: "XXX")
152
- expect(Currency.find("XXX")).not_to be_nil # Sanity check
153
- Currency.unregister(iso_code: "XXX")
154
- expect(Currency.find("XXX")).to be_nil
155
- end
156
-
157
- it "returns true iff the currency existed" do
158
- Currency.register(iso_code: "XXX")
159
- expect(Currency.unregister(iso_code: "XXX")).to be_truthy
160
- expect(Currency.unregister(iso_code: "XXX")).to be_falsey
161
- end
162
-
163
- it "can be passed an ISO code" do
164
- Currency.register(iso_code: "XXX")
165
- Currency.register(iso_code: "YYZ")
166
- # Test with string:
167
- Currency.unregister("XXX")
168
- expect(Currency.find("XXX")).to be_nil
169
- # Test with symbol:
170
- Currency.unregister(:yyz)
171
- expect(Currency.find(:yyz)).to be_nil
172
- end
173
- end
174
-
175
-
176
- describe ".each" do
177
- it "yields each currency to the block" do
178
- expect(Currency).to respond_to(:each)
179
- currencies = []
180
- Currency.each do |currency|
181
- currencies.push(currency)
182
- end
183
-
184
- # Don't bother testing every single currency
185
- expect(currencies[0]).to eq Currency.all[0]
186
- expect(currencies[1]).to eq Currency.all[1]
187
- expect(currencies[-1]).to eq Currency.all[-1]
188
- end
189
- end
190
-
191
-
192
- it "implements Enumerable" do
193
- expect(Currency).to respond_to(:all?)
194
- expect(Currency).to respond_to(:each_with_index)
195
- expect(Currency).to respond_to(:map)
196
- expect(Currency).to respond_to(:select)
197
- expect(Currency).to respond_to(:reject)
198
- end
199
-
200
-
201
- describe "#initialize" do
202
- before { Currency._instances.clear }
203
-
204
- it "lookups data from loaded config" do
205
- currency = Currency.new("USD")
206
- expect(currency.id).to eq :usd
207
- expect(currency.priority).to eq 1
208
- expect(currency.iso_code).to eq "USD"
209
- expect(currency.iso_numeric).to eq "840"
210
- expect(currency.name).to eq "United States Dollar"
211
- expect(currency.decimal_mark).to eq "."
212
- expect(currency.separator).to eq "."
213
- expect(currency.thousands_separator).to eq ","
214
- expect(currency.delimiter).to eq ","
215
- expect(currency.smallest_denomination).to eq 1
216
- end
217
-
218
- it 'caches instances' do
219
- currency = Currency.new("USD")
220
-
221
- expect(Currency._instances.length).to eq 1
222
- expect(Currency._instances["usd"].object_id).to eq currency.object_id
223
- end
224
-
225
- it "raises UnknownCurrency with unknown currency" do
226
- expect { Currency.new("xxx") }.to raise_error(Currency::UnknownCurrency, /xxx/)
227
- end
228
-
229
- it 'returns old object for the same :key' do
230
- expect(Currency.new("USD")).to be(Currency.new("USD"))
231
- expect(Currency.new("USD")).to be(Currency.new(:usd))
232
- expect(Currency.new("USD")).to be(Currency.new(:USD))
233
- expect(Currency.new("USD")).to be(Currency.new('usd'))
234
- expect(Currency.new("USD")).to be(Currency.new('Usd'))
235
- end
236
-
237
- it 'returns new object for the different :key' do
238
- expect(Currency.new("USD")).to_not be(Currency.new("EUR"))
239
- end
240
-
241
- it 'is thread safe' do
242
- ids = []
243
- 2.times.map{ Thread.new{ ids << Currency.new("USD").object_id }}.each(&:join)
244
- expect(ids.uniq.length).to eq(1)
245
- end
246
- end
247
-
248
- describe "#<=>" do
249
- it "compares objects by priority" do
250
- expect(Currency.new(:cad)).to be > Currency.new(:usd)
251
- expect(Currency.new(:usd)).to be < Currency.new(:eur)
252
- end
253
-
254
- it "compares by id when priority is the same" do
255
- Currency.register(iso_code: "ABD", priority: 15)
256
- Currency.register(iso_code: "ABC", priority: 15)
257
- Currency.register(iso_code: "ABE", priority: 15)
258
- abd = Currency.find("ABD")
259
- abc = Currency.find("ABC")
260
- abe = Currency.find("ABE")
261
- expect(abd).to be > abc
262
- expect(abe).to be > abd
263
- Currency.unregister("ABD")
264
- Currency.unregister("ABC")
265
- Currency.unregister("ABE")
266
- end
267
-
268
- context "when one of the currencies has no 'priority' set" do
269
- it "compares by id" do
270
- Currency.register(iso_code: "ABD") # No priority
271
- abd = Currency.find(:abd)
272
- usd = Currency.find(:usd)
273
- expect(abd).to be < usd
274
- Currency.unregister(iso_code: "ABD")
275
- end
276
- end
277
- end
278
-
279
- describe "#==" do
280
- it "returns true if self === other" do
281
- currency = Currency.new(:eur)
282
- expect(currency).to eq currency
283
- end
284
-
285
- it "returns true if the id is equal ignorning case" do
286
- expect(Currency.new(:eur)).to eq Currency.new(:eur)
287
- expect(Currency.new(:eur)).to eq Currency.new(:EUR)
288
- expect(Currency.new(:eur)).not_to eq Currency.new(:usd)
289
- end
290
-
291
- it "allows direct comparison of currencies and symbols/strings" do
292
- expect(Currency.new(:eur)).to eq 'eur'
293
- expect(Currency.new(:eur)).to eq 'EUR'
294
- expect(Currency.new(:eur)).to eq :eur
295
- expect(Currency.new(:eur)).to eq :EUR
296
- expect(Currency.new(:eur)).not_to eq 'usd'
297
- end
298
-
299
- it "allows comparison with nil and returns false" do
300
- expect(Currency.new(:eur)).not_to be_nil
301
- end
302
- end
303
-
304
- describe "#eql?" do
305
- it "returns true if #== returns true" do
306
- expect(Currency.new(:eur).eql?(Currency.new(:eur))).to be true
307
- expect(Currency.new(:eur).eql?(Currency.new(:usd))).to be false
308
- end
309
- end
310
-
311
- describe "#hash" do
312
- it "returns the same value for equal objects" do
313
- expect(Currency.new(:eur).hash).to eq Currency.new(:eur).hash
314
- expect(Currency.new(:eur).hash).not_to eq Currency.new(:usd).hash
315
- end
316
-
317
- it "can be used to return the intersection of Currency object arrays" do
318
- intersection = [Currency.new(:eur), Currency.new(:usd)] & [Currency.new(:eur)]
319
- expect(intersection).to eq [Currency.new(:eur)]
320
- end
321
- end
322
-
323
- describe "#inspect" do
324
- it "works as documented" do
325
- expect(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, iso_code: USD, iso_numeric: 840, subunit: Cent, smallest_denomination: 1>}
326
- end
327
- end
328
-
329
- describe "#iso?" do
330
- it "returns true for iso currency" do
331
- expect(Money::Currency.new(:eur).iso?).to be true
332
- end
333
-
334
- it "returns false if the currency is not iso" do
335
- expect(Money::Currency.new(:btc).iso?).to be false
336
- end
337
- end
338
-
339
- describe "#to_s" do
340
- it "works as documented" do
341
- expect(Currency.new(:usd).to_s).to eq("USD")
342
- expect(Currency.new(:eur).to_s).to eq("EUR")
343
- end
344
- end
345
-
346
- describe "#to_str" do
347
- it "works as documented" do
348
- expect(Currency.new(:usd).to_str).to eq("USD")
349
- expect(Currency.new(:eur).to_str).to eq("EUR")
350
- end
351
- end
352
-
353
- describe "#to_sym" do
354
- it "works as documented" do
355
- expect(Currency.new(:usd).to_sym).to eq(:USD)
356
- expect(Currency.new(:eur).to_sym).to eq(:EUR)
357
- end
358
- end
359
-
360
- describe "#to_currency" do
361
- it "works as documented" do
362
- usd = Currency.new(:usd)
363
- expect(usd.to_currency).to eq usd
364
- end
365
-
366
- it "doesn't create new symbols indefinitely" do
367
- expect { Currency.new("bogus") }.to raise_exception(Currency::UnknownCurrency)
368
- expect(Symbol.all_symbols.map{|s| s.to_s}).not_to include("bogus")
369
- end
370
- end
371
-
372
- describe "#code" do
373
- it "works as documented" do
374
- expect(Currency.new(:usd).code).to eq "$"
375
- expect(Currency.new(:azn).code).to eq "\u20BC"
376
- end
377
- end
378
-
379
- describe "#exponent" do
380
- it "conforms to iso 4217" do
381
- expect(Currency.new(:jpy).exponent).to eq 0
382
- expect(Currency.new(:usd).exponent).to eq 2
383
- expect(Currency.new(:iqd).exponent).to eq 3
384
- end
385
- end
386
-
387
- describe "#decimal_places" do
388
- it "proper places for known currency" do
389
- expect(Currency.new(:mro).decimal_places).to eq 1
390
- expect(Currency.new(:usd).decimal_places).to eq 2
391
- end
392
-
393
- it "proper places for custom currency" do
394
- register_foo
395
- expect(Currency.new(:foo).decimal_places).to eq 3
396
- unregister_foo
397
- end
398
- end
399
- end
400
- end