danconia 0.2.8 → 0.4.0
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 +4 -4
- data/.envrc +2 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +104 -0
- data/.ruby-version +1 -1
- data/.travis.yml +1 -1
- data/Gemfile.lock +63 -43
- data/README.md +17 -3
- data/bin/console +7 -0
- data/danconia.gemspec +5 -1
- data/examples/bna.rb +35 -0
- data/examples/currency_layer.rb +7 -3
- data/examples/fixed_rates.rb +1 -3
- data/examples/single_currency.rb +2 -3
- data/lib/danconia/currency.rb +2 -2
- data/lib/danconia/exchange.rb +47 -0
- data/lib/danconia/exchanges/bna.rb +61 -0
- data/lib/danconia/exchanges/currency_layer.rb +14 -2
- data/lib/danconia/exchanges/fixed_rates.rb +2 -4
- data/lib/danconia/integrations/active_record.rb +13 -14
- data/lib/danconia/money.rb +30 -24
- data/lib/danconia/pair.rb +15 -0
- data/lib/danconia/serializable.rb +17 -0
- data/lib/danconia/stores/active_record.rb +29 -6
- data/lib/danconia/stores/in_memory.rb +4 -9
- data/lib/danconia/version.rb +1 -1
- data/lib/danconia.rb +7 -4
- data/shell.nix +10 -0
- data/spec/danconia/exchanges/bna_spec.rb +52 -0
- data/spec/danconia/exchanges/currency_layer_spec.rb +28 -33
- data/spec/danconia/exchanges/exchange_spec.rb +54 -0
- data/spec/danconia/exchanges/fixtures/bna/home.html +124 -0
- data/spec/danconia/exchanges/fixtures/currency_layer/failure.json +7 -0
- data/spec/danconia/exchanges/fixtures/currency_layer/success.json +8 -0
- data/spec/danconia/integrations/active_record_spec.rb +25 -5
- data/spec/danconia/money_spec.rb +41 -17
- data/spec/danconia/serializable_spec.rb +16 -0
- data/spec/danconia/stores/active_record_spec.rb +81 -15
- data/spec/danconia/stores/in_memory_spec.rb +18 -0
- data/spec/spec_helper.rb +2 -1
- metadata +76 -14
- data/lib/danconia/exchanges/exchange.rb +0 -33
- data/spec/danconia/exchanges/fixed_rates_spec.rb +0 -30
data/spec/danconia/money_spec.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
1
|
module Danconia
|
4
2
|
describe Money do
|
5
3
|
context 'instantiation' do
|
@@ -39,16 +37,15 @@ module Danconia
|
|
39
37
|
end
|
40
38
|
|
41
39
|
it 'should exchange the other currency if it is different' do
|
42
|
-
|
40
|
+
m1 = Money(1, 'ARS', exchange_opts: {exchange: fake_exchange(rate: 4)})
|
41
|
+
expect(m1 + Money(1, 'USD')).to eq Money(5, 'ARS')
|
43
42
|
end
|
44
43
|
|
45
44
|
it 'should return a new object with the same options' do
|
46
|
-
|
47
|
-
m1 = Money(4, decimals: 3, exchange: e)
|
45
|
+
m1 = Money(4, decimals: 3)
|
48
46
|
m2 = m1 * 2
|
49
47
|
expect(m2).to_not eql m1
|
50
48
|
expect(m2.decimals).to eq 3
|
51
|
-
expect(m2.exchange).to eq e
|
52
49
|
end
|
53
50
|
|
54
51
|
it 'round should return a money object with the same currency' do
|
@@ -72,7 +69,7 @@ module Danconia
|
|
72
69
|
end
|
73
70
|
|
74
71
|
it 'should exchange to the source currency if they differ' do
|
75
|
-
TestHelpers.with_rates 'USDARS' => 4 do |
|
72
|
+
TestHelpers.with_rates 'USDARS' => 4 do |_config|
|
76
73
|
expect(Money(3, 'ARS') < Money(1, 'USD')).to be true
|
77
74
|
expect(Money(4, 'ARS') < Money(1, 'USD')).to be false
|
78
75
|
end
|
@@ -129,7 +126,8 @@ module Danconia
|
|
129
126
|
end
|
130
127
|
|
131
128
|
it 'should allow to pass the exchange to the instance' do
|
132
|
-
|
129
|
+
m = Money(2, 'USD', exchange_opts: {exchange: fake_exchange(rate: 3)})
|
130
|
+
expect(m.exchange_to('ARS')).to eq Money(6, 'ARS')
|
133
131
|
end
|
134
132
|
|
135
133
|
it 'should allow to pass the exchange when converting' do
|
@@ -153,8 +151,8 @@ module Danconia
|
|
153
151
|
end
|
154
152
|
|
155
153
|
it 'should return a new object with the same opts' do
|
156
|
-
m1 = Money(1, 'USD', decimals: 0
|
157
|
-
m2 = m1.exchange_to('ARS')
|
154
|
+
m1 = Money(1, 'USD', decimals: 0)
|
155
|
+
m2 = m1.exchange_to('ARS', exchange: fake_exchange(rate: 3))
|
158
156
|
expect(m2).to_not eql m1
|
159
157
|
expect(m2.decimals).to eq 0
|
160
158
|
expect(m1).to eq Money(1, 'USD')
|
@@ -164,6 +162,38 @@ module Danconia
|
|
164
162
|
expect(Money(1, 'USD').exchange_to('')).to eq Money(1, 'USD')
|
165
163
|
expect(Money(1, 'ARS').exchange_to('')).to eq Money(1, 'ARS')
|
166
164
|
end
|
165
|
+
|
166
|
+
context 'opts' do
|
167
|
+
let(:exchange) do
|
168
|
+
Class.new(Exchange) do
|
169
|
+
def rates opts
|
170
|
+
case opts[:type]
|
171
|
+
when 'divisa' then {'USDARS' => 7}
|
172
|
+
when 'billete' then {'USDARS' => 8}
|
173
|
+
else {}
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end.new
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'allows to specify opts to pass to the exchange (filters for example)' do
|
180
|
+
expect(Money(1, 'USD').exchange_to('ARS', type: 'divisa', exchange: exchange)).to eq Money(7, 'ARS')
|
181
|
+
expect { Money(1, 'USD').exchange_to('ARS', exchange: exchange) }.to raise_error Errors::ExchangeRateNotFound
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'should be preserved after operations' do
|
185
|
+
m1 = Money(1, 'USD').exchange_to('ARS', type: 'divisa', exchange: exchange)
|
186
|
+
m2 = m1 + Money(2, 'USD')
|
187
|
+
|
188
|
+
expect(m2).to eq Money(21, 'ARS')
|
189
|
+
expect(m1 < Money(2, 'USD')).to eq true
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'should use the instance exchange_opts by default' do
|
193
|
+
m = Money(1, 'USD', exchange_opts: {exchange: exchange, type: 'billete'})
|
194
|
+
expect(m.exchange_to('ARS')).to eq Money(8, 'ARS')
|
195
|
+
end
|
196
|
+
end
|
167
197
|
end
|
168
198
|
|
169
199
|
context 'default_currency?' do
|
@@ -183,14 +213,8 @@ module Danconia
|
|
183
213
|
end
|
184
214
|
end
|
185
215
|
|
186
|
-
context 'to_json' do
|
187
|
-
it 'should delegate to the amount' do
|
188
|
-
expect(Money(1).to_json).to eq '"1.0"'
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
216
|
def fake_exchange args = {}
|
193
|
-
double 'Danconia::
|
217
|
+
double 'Danconia::Exchange', args.reverse_merge(rate: nil)
|
194
218
|
end
|
195
219
|
end
|
196
220
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Danconia
|
2
|
+
describe Serializable do
|
3
|
+
context 'marshalling' do
|
4
|
+
it 'money objects support dump and load' do
|
5
|
+
expect(Marshal.load(Marshal.dump(Money(5, 'USD')))).to eq Money(5, 'USD')
|
6
|
+
expect(Marshal.load(Marshal.dump(Money(3.2, 'ARS')))).to eq Money(3.2, 'ARS')
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'to_json' do
|
11
|
+
it 'should delegate to the amount' do
|
12
|
+
expect(Money(1).to_json).to eq %({"amount":"1.0","currency":"USD"})
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -1,31 +1,97 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
1
|
module Danconia
|
4
2
|
module Stores
|
5
3
|
describe ActiveRecord, active_record: true do
|
4
|
+
before do
|
5
|
+
::ActiveRecord::Schema.define do
|
6
|
+
create_table :exchange_rates do |t|
|
7
|
+
t.string :pair, limit: 6
|
8
|
+
t.decimal :rate, precision: 12, scale: 6
|
9
|
+
t.string :rate_type
|
10
|
+
t.date :date
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
6
15
|
context 'save_rates' do
|
7
16
|
it 'should create or update the rates' do
|
8
17
|
ExchangeRate.create! pair: 'USDEUR', rate: 2
|
9
|
-
|
10
|
-
expect
|
18
|
+
|
19
|
+
expect do
|
20
|
+
subject.save_rates [{pair: 'USDEUR', rate: 3}, {pair: 'USDARS', rate: 4}]
|
21
|
+
end.to change { ExchangeRate.count }.by 1
|
22
|
+
|
23
|
+
expect(subject.rates).to match [
|
24
|
+
include(pair: 'USDEUR', rate: 3),
|
25
|
+
include(pair: 'USDARS', rate: 4)
|
26
|
+
]
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'allows to specify other keys to use as unique' do
|
30
|
+
store = ActiveRecord.new(unique_keys: %i[pair rate_type])
|
31
|
+
store.save_rates [
|
32
|
+
{pair: 'USDARS', rate: 3, rate_type: 'billetes'},
|
33
|
+
{pair: 'USDARS', rate: 4, rate_type: 'divisas'}
|
34
|
+
]
|
35
|
+
store.save_rates [
|
36
|
+
{pair: 'USDARS', rate: 33, rate_type: 'billetes'}
|
37
|
+
]
|
38
|
+
expect(subject.rates).to match [
|
39
|
+
include(pair: 'USDARS', rate: 33, rate_type: 'billetes'),
|
40
|
+
include(pair: 'USDARS', rate: 4, rate_type: 'divisas')
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'ignores fields not present in the database table' do
|
45
|
+
subject.save_rates [{pair: 'USDEUR', rate: 3, non_existant: 'ignoreme'}]
|
11
46
|
end
|
12
47
|
end
|
13
48
|
|
14
|
-
context '
|
15
|
-
it '
|
49
|
+
context 'rates' do
|
50
|
+
it 'returns an array like the one it received' do
|
16
51
|
ExchangeRate.create! pair: 'USDEUR', rate: 2
|
17
|
-
|
18
|
-
|
52
|
+
ExchangeRate.create! pair: 'USDARS', rate: 40
|
53
|
+
|
54
|
+
expect(subject.rates).to match [
|
55
|
+
include(pair: 'USDEUR', rate: 2),
|
56
|
+
include(pair: 'USDARS', rate: 40)
|
57
|
+
]
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'allows to pass filters' do
|
61
|
+
store = ActiveRecord.new(unique_keys: %i[pair date])
|
62
|
+
store.save_rates [
|
63
|
+
{pair: 'USDEUR', rate: 10, date: Date.new(2020, 1, 1)},
|
64
|
+
{pair: 'USDEUR', rate: 20, date: Date.new(2020, 1, 2)},
|
65
|
+
{pair: 'USDEUR', rate: 30, date: Date.new(2020, 1, 3)}
|
66
|
+
]
|
67
|
+
|
68
|
+
expect(store.rates.size).to eq 3
|
69
|
+
expect(store.rates(date: Date.new(2020, 1, 2))).to match [include(rate: 20)]
|
19
70
|
end
|
20
71
|
end
|
21
72
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
73
|
+
context 'special date field' do
|
74
|
+
let(:store) do
|
75
|
+
store = ActiveRecord.new(unique_keys: %i[pair date], date_field: :date)
|
76
|
+
store.save_rates [
|
77
|
+
{pair: 'USDEUR', rate: 10, date: Date.new(2000, 1, 1)},
|
78
|
+
{pair: 'USDEUR', rate: 20, date: Date.new(2000, 1, 2)},
|
79
|
+
{pair: 'USDEUR', rate: 30, date: Date.new(2000, 1, 4)}
|
80
|
+
]
|
81
|
+
store
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'calling #rates with a particular date when there are rates for that date' do
|
85
|
+
expect(store.rates(date: Date.new(2000, 1, 2))).to match [include(rate: 20)]
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'calling #rates with a particular date when there are not rates for that date should return the previous' do
|
89
|
+
expect(store.rates(date: Date.new(2000, 1, 3))).to match [include(rate: 20)]
|
90
|
+
expect(store.rates(date: Date.new(1999, 12, 31))).to eq []
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'calling #rates without a particular date, uses today' do
|
94
|
+
expect(store.rates).to match [include(rate: 30)]
|
29
95
|
end
|
30
96
|
end
|
31
97
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Danconia
|
2
|
+
module Stores
|
3
|
+
describe InMemory do
|
4
|
+
context 'rates' do
|
5
|
+
it 'filters rates by type' do
|
6
|
+
subject.save_rates [
|
7
|
+
{pair: 'USDARS', rate: 3, rate_type: 'divisas'},
|
8
|
+
{pair: 'USDARS', rate: 4, rate_type: 'billetes'}
|
9
|
+
]
|
10
|
+
|
11
|
+
expect(subject.rates(rate_type: 'divisas')).to match [include(rate: 3)]
|
12
|
+
expect(subject.rates(rate_type: 'billetes')).to match [include(rate: 4)]
|
13
|
+
expect(subject.rates).to match [include(rate: 3), include(rate: 4)]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
require 'danconia'
|
2
2
|
require 'danconia/test_helpers'
|
3
|
+
require 'danconia/integrations/active_record'
|
3
4
|
require 'webmock/rspec'
|
4
5
|
|
5
|
-
Dir["#{__dir__}/support/*.rb"].each { |f| require f }
|
6
|
+
Dir["#{__dir__}/support/*.rb"].sort.each { |f| require f }
|
6
7
|
|
7
8
|
RSpec.configure do |config|
|
8
9
|
config.filter_run_when_matching :focus
|
metadata
CHANGED
@@ -1,29 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: danconia
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Emmanuel Nicolau
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 1980-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: '4.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: '4.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: sqlite3
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -108,14 +122,45 @@ dependencies:
|
|
108
122
|
- - ">="
|
109
123
|
- !ruby/object:Gem::Version
|
110
124
|
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: nokogiri
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: rubocop
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.80.0
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.80.0
|
111
153
|
description: Multi-currency money library backed by BigDecimal
|
112
154
|
email: emmanicolau@gmail.com
|
113
|
-
executables:
|
155
|
+
executables:
|
156
|
+
- console
|
114
157
|
extensions: []
|
115
158
|
extra_rdoc_files: []
|
116
159
|
files:
|
160
|
+
- ".envrc"
|
117
161
|
- ".gitignore"
|
118
162
|
- ".rspec"
|
163
|
+
- ".rubocop.yml"
|
119
164
|
- ".ruby-version"
|
120
165
|
- ".travis.yml"
|
121
166
|
- Gemfile
|
@@ -124,7 +169,9 @@ files:
|
|
124
169
|
- LICENSE.txt
|
125
170
|
- README.md
|
126
171
|
- Rakefile
|
172
|
+
- bin/console
|
127
173
|
- danconia.gemspec
|
174
|
+
- examples/bna.rb
|
128
175
|
- examples/currency_layer.rb
|
129
176
|
- examples/fixed_rates.rb
|
130
177
|
- examples/single_currency.rb
|
@@ -133,28 +180,38 @@ files:
|
|
133
180
|
- lib/danconia/currency.rb
|
134
181
|
- lib/danconia/errors/api_error.rb
|
135
182
|
- lib/danconia/errors/exchange_rate_not_found.rb
|
183
|
+
- lib/danconia/exchange.rb
|
184
|
+
- lib/danconia/exchanges/bna.rb
|
136
185
|
- lib/danconia/exchanges/currency_layer.rb
|
137
|
-
- lib/danconia/exchanges/exchange.rb
|
138
186
|
- lib/danconia/exchanges/fixed_rates.rb
|
139
187
|
- lib/danconia/integrations/active_record.rb
|
140
188
|
- lib/danconia/kernel.rb
|
141
189
|
- lib/danconia/money.rb
|
190
|
+
- lib/danconia/pair.rb
|
191
|
+
- lib/danconia/serializable.rb
|
142
192
|
- lib/danconia/stores/active_record.rb
|
143
193
|
- lib/danconia/stores/in_memory.rb
|
144
194
|
- lib/danconia/test_helpers.rb
|
145
195
|
- lib/danconia/version.rb
|
196
|
+
- shell.nix
|
197
|
+
- spec/danconia/exchanges/bna_spec.rb
|
146
198
|
- spec/danconia/exchanges/currency_layer_spec.rb
|
147
|
-
- spec/danconia/exchanges/
|
199
|
+
- spec/danconia/exchanges/exchange_spec.rb
|
200
|
+
- spec/danconia/exchanges/fixtures/bna/home.html
|
201
|
+
- spec/danconia/exchanges/fixtures/currency_layer/failure.json
|
202
|
+
- spec/danconia/exchanges/fixtures/currency_layer/success.json
|
148
203
|
- spec/danconia/integrations/active_record_spec.rb
|
149
204
|
- spec/danconia/money_spec.rb
|
205
|
+
- spec/danconia/serializable_spec.rb
|
150
206
|
- spec/danconia/stores/active_record_spec.rb
|
207
|
+
- spec/danconia/stores/in_memory_spec.rb
|
151
208
|
- spec/spec_helper.rb
|
152
209
|
- spec/support/database.rb
|
153
210
|
homepage: https://github.com/eeng/danconia
|
154
211
|
licenses:
|
155
212
|
- MIT
|
156
213
|
metadata: {}
|
157
|
-
post_install_message:
|
214
|
+
post_install_message:
|
158
215
|
rdoc_options: []
|
159
216
|
require_paths:
|
160
217
|
- lib
|
@@ -169,16 +226,21 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
226
|
- !ruby/object:Gem::Version
|
170
227
|
version: '0'
|
171
228
|
requirements: []
|
172
|
-
|
173
|
-
|
174
|
-
signing_key:
|
229
|
+
rubygems_version: 3.2.26
|
230
|
+
signing_key:
|
175
231
|
specification_version: 4
|
176
232
|
summary: Multi-currency money library backed by BigDecimal
|
177
233
|
test_files:
|
234
|
+
- spec/danconia/exchanges/bna_spec.rb
|
178
235
|
- spec/danconia/exchanges/currency_layer_spec.rb
|
179
|
-
- spec/danconia/exchanges/
|
236
|
+
- spec/danconia/exchanges/exchange_spec.rb
|
237
|
+
- spec/danconia/exchanges/fixtures/bna/home.html
|
238
|
+
- spec/danconia/exchanges/fixtures/currency_layer/failure.json
|
239
|
+
- spec/danconia/exchanges/fixtures/currency_layer/success.json
|
180
240
|
- spec/danconia/integrations/active_record_spec.rb
|
181
241
|
- spec/danconia/money_spec.rb
|
242
|
+
- spec/danconia/serializable_spec.rb
|
182
243
|
- spec/danconia/stores/active_record_spec.rb
|
244
|
+
- spec/danconia/stores/in_memory_spec.rb
|
183
245
|
- spec/spec_helper.rb
|
184
246
|
- spec/support/database.rb
|
@@ -1,33 +0,0 @@
|
|
1
|
-
module Danconia
|
2
|
-
module Exchanges
|
3
|
-
class Exchange
|
4
|
-
attr_reader :store
|
5
|
-
|
6
|
-
def initialize store: Stores::InMemory.new
|
7
|
-
@store = store
|
8
|
-
end
|
9
|
-
|
10
|
-
def rate from, to
|
11
|
-
if from == to
|
12
|
-
1.0
|
13
|
-
elsif from == 'USD' and direct_rate = @store.direct_rate(from, to)
|
14
|
-
direct_rate
|
15
|
-
elsif to == 'USD' and inverse_rate = @store.direct_rate(to, from)
|
16
|
-
(1.0 / inverse_rate).round 6
|
17
|
-
elsif from != 'USD' and to != 'USD' and from_in_usd = rate(from, 'USD') and to_per_usd = rate('USD', to)
|
18
|
-
(from_in_usd * to_per_usd).round 6
|
19
|
-
else
|
20
|
-
raise Errors::ExchangeRateNotFound.new(from, to)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def rates
|
25
|
-
@store.rates
|
26
|
-
end
|
27
|
-
|
28
|
-
def update_rates!
|
29
|
-
@store.save_rates fetch_rates
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Danconia
|
4
|
-
module Exchanges
|
5
|
-
describe FixedRates do
|
6
|
-
context 'rate' do
|
7
|
-
it 'returns the exchange rate value for the supplied currencies' do
|
8
|
-
exchange = FixedRates.new rates: {'USDEUR' => 3, 'USDARS' => 4}
|
9
|
-
expect(exchange.rate 'USD', 'EUR').to eq 3
|
10
|
-
expect(exchange.rate 'USD', 'ARS').to eq 4
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'returns nil if not found' do
|
14
|
-
expect { subject.rate 'USD', 'EUR' }.to raise_error Errors::ExchangeRateNotFound
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'if the direct conversion is not found, tries to find the inverse' do
|
18
|
-
exchange = FixedRates.new rates: {'USDEUR' => 3}
|
19
|
-
expect(exchange.rate 'EUR', 'USD').to eq (1.0 / 3).round 6
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'if not direct nor inverse conversion is found and both are different than USD, tries to convert through USD' do
|
23
|
-
exchange = FixedRates.new rates: {'USDEUR' => 3, 'USDARS' => 6}
|
24
|
-
expect(exchange.rate 'EUR', 'ARS').to be_within(0.00001).of 2
|
25
|
-
expect(exchange.rate 'ARS', 'EUR').to be_within(0.00001).of 0.5
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|