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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +49 -2
- data/README.md +93 -3
- data/config/currency_backwards_compatible.json +31 -0
- data/config/currency_iso.json +18 -18
- data/lib/money/bank/variable_exchange.rb +14 -6
- data/lib/money/currency.rb +4 -4
- data/lib/money/currency/loader.rb +15 -13
- data/lib/money/locale_backend/currency.rb +11 -0
- data/lib/money/locale_backend/i18n.rb +2 -1
- data/lib/money/locale_backend/legacy.rb +2 -2
- data/lib/money/money.rb +100 -52
- data/lib/money/money/allocation.rb +8 -5
- data/lib/money/money/arithmetic.rb +20 -10
- data/lib/money/money/formatter.rb +3 -0
- data/lib/money/money/formatting_rules.rb +15 -5
- data/lib/money/money/locale_backend.rb +3 -1
- data/lib/money/rates_store/memory.rb +24 -23
- data/lib/money/version.rb +1 -1
- data/money.gemspec +4 -4
- metadata +11 -51
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -23
- data/.rspec +0 -2
- data/.travis.yml +0 -32
- data/AUTHORS +0 -131
- data/CONTRIBUTING.md +0 -17
- data/Gemfile +0 -16
- data/Rakefile +0 -17
- 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 -400
- data/spec/locale_backend/i18n_spec.rb +0 -62
- data/spec/locale_backend/legacy_spec.rb +0 -74
- data/spec/money/allocation_spec.rb +0 -130
- data/spec/money/arithmetic_spec.rb +0 -756
- data/spec/money/constructors_spec.rb +0 -91
- data/spec/money/formatting_spec.rb +0 -855
- data/spec/money/locale_backend_spec.rb +0 -14
- data/spec/money_spec.rb +0 -880
- data/spec/rates_store/memory_spec.rb +0 -80
- data/spec/spec_helper.rb +0 -30
- data/spec/support/shared_examples/money_examples.rb +0 -14
data/CONTRIBUTING.md
DELETED
@@ -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
|
data/spec/bank/base_spec.rb
DELETED
@@ -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
|
data/spec/currency_spec.rb
DELETED
@@ -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
|