danconia 0.2.8 → 0.2.9
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/Gemfile.lock +11 -11
- data/bin/console +7 -0
- data/examples/currency_layer.rb +1 -2
- data/examples/fixed_rates.rb +1 -3
- data/examples/single_currency.rb +0 -2
- data/lib/danconia/exchanges/exchange.rb +25 -11
- data/lib/danconia/pair.rb +15 -0
- data/lib/danconia/stores/active_record.rb +0 -4
- data/lib/danconia/stores/in_memory.rb +1 -5
- data/lib/danconia/version.rb +1 -1
- data/spec/danconia/exchanges/currency_layer_spec.rb +8 -21
- data/spec/danconia/exchanges/exchange_spec.rb +42 -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/stores/active_record_spec.rb +4 -4
- metadata +12 -5
- data/spec/danconia/exchanges/fixed_rates_spec.rb +0 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a7427977cd93825975491eddf43bcd9e5fda9389102d2c2b32c16efdfb11f590
|
4
|
+
data.tar.gz: 15bccf8fc66c26679cc101d5f27dd9aabc5398b28fe1a04a00b167500993eedf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95d1edfd74b3388998c3251ac204a61a10a7454e0eda2fc2ca3b6105f95d9ead47c511a67790e82d34d4d54f934db83cc7326da53abef51c074846571b28a819
|
7
|
+
data.tar.gz: 033bb6ae1f171059bceb87495b9490b2d760f6371d71ec74f8a6a494f628ab52302f91f19f2a0e9f344c9c4775fecd0027c071e0cc21db51568e1efd80ea8c70
|
data/Gemfile.lock
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
danconia (0.2.
|
4
|
+
danconia (0.2.9)
|
5
5
|
activerecord (>= 3.0.0)
|
6
6
|
|
7
7
|
GEM
|
8
8
|
remote: https://rubygems.org/
|
9
9
|
specs:
|
10
|
-
activemodel (6.0.3.
|
11
|
-
activesupport (= 6.0.3.
|
12
|
-
activerecord (6.0.3.
|
13
|
-
activemodel (= 6.0.3.
|
14
|
-
activesupport (= 6.0.3.
|
15
|
-
activesupport (6.0.3.
|
10
|
+
activemodel (6.0.3.2)
|
11
|
+
activesupport (= 6.0.3.2)
|
12
|
+
activerecord (6.0.3.2)
|
13
|
+
activemodel (= 6.0.3.2)
|
14
|
+
activesupport (= 6.0.3.2)
|
15
|
+
activesupport (6.0.3.2)
|
16
16
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
17
|
i18n (>= 0.7, < 2)
|
18
18
|
minitest (~> 5.1)
|
@@ -21,7 +21,7 @@ GEM
|
|
21
21
|
addressable (2.5.2)
|
22
22
|
public_suffix (>= 2.0.2, < 4.0)
|
23
23
|
coderay (1.1.2)
|
24
|
-
concurrent-ruby (1.1.
|
24
|
+
concurrent-ruby (1.1.7)
|
25
25
|
crack (0.4.3)
|
26
26
|
safe_yaml (~> 1.0.0)
|
27
27
|
diff-lcs (1.3)
|
@@ -42,7 +42,7 @@ GEM
|
|
42
42
|
guard-compat (~> 1.1)
|
43
43
|
rspec (>= 2.99.0, < 4.0)
|
44
44
|
hashdiff (0.3.7)
|
45
|
-
i18n (1.8.
|
45
|
+
i18n (1.8.5)
|
46
46
|
concurrent-ruby (~> 1.0)
|
47
47
|
listen (3.1.5)
|
48
48
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
@@ -50,7 +50,7 @@ GEM
|
|
50
50
|
ruby_dep (~> 1.2)
|
51
51
|
lumberjack (1.0.13)
|
52
52
|
method_source (0.9.0)
|
53
|
-
minitest (5.14.
|
53
|
+
minitest (5.14.2)
|
54
54
|
nenv (0.3.0)
|
55
55
|
notiffany (0.1.1)
|
56
56
|
nenv (~> 0.1)
|
@@ -88,7 +88,7 @@ GEM
|
|
88
88
|
addressable (>= 2.3.6)
|
89
89
|
crack (>= 0.3.2)
|
90
90
|
hashdiff
|
91
|
-
zeitwerk (2.
|
91
|
+
zeitwerk (2.4.0)
|
92
92
|
|
93
93
|
PLATFORMS
|
94
94
|
ruby
|
data/bin/console
ADDED
data/examples/currency_layer.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# Remember to supply your CurrencyLayer key in the ACCESS_KEY environment variable to run this example
|
2
|
-
|
3
|
-
|
2
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
4
3
|
ActiveRecord::Base.establish_connection adapter: 'sqlite3', database: ':memory:'
|
5
4
|
|
6
5
|
ActiveRecord::Schema.define do
|
data/examples/fixed_rates.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
-
require 'danconia'
|
2
|
-
|
3
1
|
Danconia.configure do |config|
|
4
2
|
config.default_currency = 'ARS'
|
5
3
|
config.default_exchange = Danconia::Exchanges::FixedRates.new(rates: {'USDARS' => 27.5, 'USDEUR' => 0.86})
|
6
4
|
end
|
7
5
|
|
8
|
-
puts Money(10, 'ARS').exchange_to('EUR').inspect # => 0.31273 EUR
|
6
|
+
puts Money(10, 'ARS').exchange_to('EUR').inspect # => 0.31273 EUR
|
data/examples/single_currency.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'danconia/pair'
|
2
|
+
|
1
3
|
module Danconia
|
2
4
|
module Exchanges
|
3
5
|
class Exchange
|
@@ -8,17 +10,11 @@ module Danconia
|
|
8
10
|
end
|
9
11
|
|
10
12
|
def rate from, to
|
11
|
-
if from == to
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
13
|
+
return 1.0 if from == to
|
14
|
+
|
15
|
+
pair = Pair.new(from, to)
|
16
|
+
rates = direct_and_inverted_rates()
|
17
|
+
rates[pair] or indirect_rate(pair, rates) or raise Errors::ExchangeRateNotFound.new(from, to)
|
22
18
|
end
|
23
19
|
|
24
20
|
def rates
|
@@ -28,6 +24,24 @@ module Danconia
|
|
28
24
|
def update_rates!
|
29
25
|
@store.save_rates fetch_rates
|
30
26
|
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Returns the original rates plus the inverted ones, to simplify rate finding logic.
|
31
|
+
def direct_and_inverted_rates
|
32
|
+
rates.each_with_object({}) do |(pair_str, rate), rs|
|
33
|
+
pair = Pair.parse(pair_str)
|
34
|
+
rs[pair] = rate
|
35
|
+
rs[pair.invert] ||= 1.0 / rate
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def indirect_rate ind_pair, rates
|
40
|
+
if (from_pair = rates.keys.detect { |(pair, rate)| pair.from == ind_pair.from }) &&
|
41
|
+
(to_pair = rates.keys.detect { |(pair, rate)| pair.to == ind_pair.to })
|
42
|
+
rates[from_pair] * rates[to_pair]
|
43
|
+
end
|
44
|
+
end
|
31
45
|
end
|
32
46
|
end
|
33
47
|
end
|
@@ -3,18 +3,14 @@ module Danconia
|
|
3
3
|
class InMemory
|
4
4
|
attr_reader :rates
|
5
5
|
|
6
|
+
# `rates` should be of a map of pair->rate like {'USDEUR' => 1.25}
|
6
7
|
def initialize rates: {}
|
7
8
|
save_rates rates
|
8
9
|
end
|
9
10
|
|
10
|
-
# @rates should be of a map of pair->rate like {'USDEUR' => 1.25}
|
11
11
|
def save_rates rates
|
12
12
|
@rates = rates
|
13
13
|
end
|
14
|
-
|
15
|
-
def direct_rate from, to
|
16
|
-
@rates[[from, to].join]
|
17
|
-
end
|
18
14
|
end
|
19
15
|
end
|
20
16
|
end
|
data/lib/danconia/version.rb
CHANGED
@@ -7,29 +7,12 @@ module Danconia
|
|
7
7
|
|
8
8
|
context 'fetch_rates' do
|
9
9
|
it 'uses the API to retrive the rates' do
|
10
|
-
stub_request(:get, 'http://www.apilayer.net/api/live?access_key=[KEY]').to_return body:
|
11
|
-
{
|
12
|
-
"success": true,
|
13
|
-
"source": "USD",
|
14
|
-
"quotes": {
|
15
|
-
"USDARS": 27.110001,
|
16
|
-
"USDAUD": 1.346196
|
17
|
-
}
|
18
|
-
}
|
19
|
-
END
|
10
|
+
stub_request(:get, 'http://www.apilayer.net/api/live?access_key=[KEY]').to_return body: fixture('success.json')
|
20
11
|
expect(subject.fetch_rates).to eq 'USDARS' => 27.110001, 'USDAUD' => 1.346196
|
21
12
|
end
|
22
13
|
|
23
14
|
it 'when the API returns an error' do
|
24
|
-
stub_request(:get, 'http://www.apilayer.net/api/live?access_key=[KEY]').to_return body:
|
25
|
-
{
|
26
|
-
"success": false,
|
27
|
-
"error": {
|
28
|
-
"code": 104,
|
29
|
-
"info": "Your monthly usage limit has been reached. Please upgrade your subscription plan."
|
30
|
-
}
|
31
|
-
}
|
32
|
-
END
|
15
|
+
stub_request(:get, 'http://www.apilayer.net/api/live?access_key=[KEY]').to_return body: fixture('failure.json')
|
33
16
|
expect { subject.fetch_rates }.to raise_error Errors::APIError
|
34
17
|
end
|
35
18
|
end
|
@@ -38,7 +21,7 @@ module Danconia
|
|
38
21
|
it 'fetches the rates and stores them' do
|
39
22
|
expect(subject).to receive(:fetch_rates) { {'USDARS' => 3, 'USDAUD' => 4} }
|
40
23
|
subject.update_rates!
|
41
|
-
expect(subject.rates.size).to eq 2
|
24
|
+
expect(subject.store.rates.size).to eq 2
|
42
25
|
expect(subject.rate('USD', 'ARS')).to eq 3
|
43
26
|
expect(subject.rate('USD', 'AUD')).to eq 4
|
44
27
|
end
|
@@ -50,6 +33,10 @@ module Danconia
|
|
50
33
|
expect(subject.rate('USD', 'ARS')).to eq 3.1
|
51
34
|
end
|
52
35
|
end
|
36
|
+
|
37
|
+
def fixture file
|
38
|
+
File.read("#{__dir__}/fixtures/currency_layer/#{file}")
|
39
|
+
end
|
53
40
|
end
|
54
41
|
end
|
55
|
-
end
|
42
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Danconia
|
4
|
+
module Exchanges
|
5
|
+
describe Exchange do
|
6
|
+
context 'rate' do
|
7
|
+
it 'returns the exchange rate value for the supplied currencies' do
|
8
|
+
exchange = fake_exchange('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 'if the direct conversion is not found, tries to find the inverse' do
|
14
|
+
exchange = fake_exchange('USDEUR' => 3)
|
15
|
+
expect(exchange.rate('EUR', 'USD')).to be_within(0.00001).of(1.0 / 3)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'if not direct nor inverse conversion is found, tries to convert through USD' do
|
19
|
+
exchange = fake_exchange('USDEUR' => 3, 'USDARS' => 6)
|
20
|
+
expect(exchange.rate('EUR', 'ARS')).to be_within(0.00001).of 2
|
21
|
+
expect(exchange.rate('ARS', 'EUR')).to be_within(0.00001).of 0.5
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'pairs can have a different common currency' do
|
25
|
+
exchange = fake_exchange('EURARS' => 3, 'BRLARS' => 1.5)
|
26
|
+
expect(exchange.rate('EUR', 'ARS')).to eq 3
|
27
|
+
expect(exchange.rate('ARS', 'EUR')).to be_within(0.00001).of(1.0 / 3)
|
28
|
+
expect(exchange.rate('BRL', 'ARS')).to eq 1.5
|
29
|
+
expect(exchange.rate('EUR', 'BRL')).to eq 3 / 1.5
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'raises an error if the conversion cannot be made' do
|
33
|
+
expect { subject.rate('USD', 'EUR') }.to raise_error Errors::ExchangeRateNotFound
|
34
|
+
end
|
35
|
+
|
36
|
+
def fake_exchange(rates)
|
37
|
+
FixedRates.new(rates: rates)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -11,11 +11,11 @@ module Danconia
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
context '
|
15
|
-
it '
|
14
|
+
context 'rates' do
|
15
|
+
it 'returns a hash with rate by pair' do
|
16
16
|
ExchangeRate.create! pair: 'USDEUR', rate: 2
|
17
|
-
|
18
|
-
expect(subject.
|
17
|
+
ExchangeRate.create! pair: 'USDARS', rate: 40
|
18
|
+
expect(subject.rates).to eq('USDEUR' => 2, 'USDARS' => 40)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: danconia
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Emmanuel Nicolau
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -110,7 +110,8 @@ dependencies:
|
|
110
110
|
version: '0'
|
111
111
|
description: Multi-currency money library backed by BigDecimal
|
112
112
|
email: emmanicolau@gmail.com
|
113
|
-
executables:
|
113
|
+
executables:
|
114
|
+
- console
|
114
115
|
extensions: []
|
115
116
|
extra_rdoc_files: []
|
116
117
|
files:
|
@@ -124,6 +125,7 @@ files:
|
|
124
125
|
- LICENSE.txt
|
125
126
|
- README.md
|
126
127
|
- Rakefile
|
128
|
+
- bin/console
|
127
129
|
- danconia.gemspec
|
128
130
|
- examples/currency_layer.rb
|
129
131
|
- examples/fixed_rates.rb
|
@@ -139,12 +141,15 @@ files:
|
|
139
141
|
- lib/danconia/integrations/active_record.rb
|
140
142
|
- lib/danconia/kernel.rb
|
141
143
|
- lib/danconia/money.rb
|
144
|
+
- lib/danconia/pair.rb
|
142
145
|
- lib/danconia/stores/active_record.rb
|
143
146
|
- lib/danconia/stores/in_memory.rb
|
144
147
|
- lib/danconia/test_helpers.rb
|
145
148
|
- lib/danconia/version.rb
|
146
149
|
- spec/danconia/exchanges/currency_layer_spec.rb
|
147
|
-
- spec/danconia/exchanges/
|
150
|
+
- spec/danconia/exchanges/exchange_spec.rb
|
151
|
+
- spec/danconia/exchanges/fixtures/currency_layer/failure.json
|
152
|
+
- spec/danconia/exchanges/fixtures/currency_layer/success.json
|
148
153
|
- spec/danconia/integrations/active_record_spec.rb
|
149
154
|
- spec/danconia/money_spec.rb
|
150
155
|
- spec/danconia/stores/active_record_spec.rb
|
@@ -176,7 +181,9 @@ specification_version: 4
|
|
176
181
|
summary: Multi-currency money library backed by BigDecimal
|
177
182
|
test_files:
|
178
183
|
- spec/danconia/exchanges/currency_layer_spec.rb
|
179
|
-
- spec/danconia/exchanges/
|
184
|
+
- spec/danconia/exchanges/exchange_spec.rb
|
185
|
+
- spec/danconia/exchanges/fixtures/currency_layer/failure.json
|
186
|
+
- spec/danconia/exchanges/fixtures/currency_layer/success.json
|
180
187
|
- spec/danconia/integrations/active_record_spec.rb
|
181
188
|
- spec/danconia/money_spec.rb
|
182
189
|
- spec/danconia/stores/active_record_spec.rb
|
@@ -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
|