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/lib/danconia/money.rb
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
require 'bigdecimal'
|
2
2
|
require 'danconia/errors/exchange_rate_not_found'
|
3
|
+
require 'danconia/serializable'
|
3
4
|
|
4
5
|
module Danconia
|
5
6
|
class Money
|
6
7
|
include Comparable
|
7
|
-
|
8
|
+
include Serializable
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
attr_reader :amount, :currency, :decimals
|
11
|
+
|
12
|
+
def initialize(amount, currency_code = nil, decimals: 2, exchange_opts: {})
|
11
13
|
@amount = parse amount
|
12
|
-
@
|
13
|
-
@
|
14
|
+
@decimals = decimals
|
15
|
+
@currency = Currency.find(currency_code || Danconia.config.default_currency)
|
16
|
+
@exchange_opts = exchange_opts.reverse_merge(exchange: Danconia.config.default_exchange)
|
14
17
|
end
|
15
18
|
|
16
19
|
def format decimals: @decimals, **other_options
|
@@ -41,23 +44,22 @@ module Danconia
|
|
41
44
|
end
|
42
45
|
|
43
46
|
def <=> other
|
44
|
-
|
45
|
-
amount <=> other
|
47
|
+
amount <=> amount_exchanged_to_this_currency(other)
|
46
48
|
end
|
47
49
|
|
48
|
-
def exchange_to other_currency,
|
49
|
-
|
50
|
-
|
51
|
-
|
50
|
+
def exchange_to other_currency, **opts
|
51
|
+
opts = @exchange_opts.merge(opts)
|
52
|
+
other_currency = other_currency.presence && Currency.find(other_currency) || currency
|
53
|
+
rate = opts[:exchange].rate currency.code, other_currency.code, opts.except(:exchange)
|
54
|
+
clone_with amount * rate, other_currency, opts
|
52
55
|
end
|
53
56
|
|
54
|
-
%w
|
55
|
-
class_eval <<-
|
57
|
+
%w[+ - * /].each do |op|
|
58
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
56
59
|
def #{op} other
|
57
|
-
|
58
|
-
clone_with(amount #{op} other)
|
60
|
+
clone_with(amount #{op} amount_exchanged_to_this_currency(other))
|
59
61
|
end
|
60
|
-
|
62
|
+
RUBY
|
61
63
|
end
|
62
64
|
|
63
65
|
def round *args
|
@@ -68,10 +70,6 @@ module Danconia
|
|
68
70
|
(self * 100).round
|
69
71
|
end
|
70
72
|
|
71
|
-
def as_json *args
|
72
|
-
amount.as_json *args
|
73
|
-
end
|
74
|
-
|
75
73
|
def default_currency?
|
76
74
|
currency.code == Danconia.config.default_currency
|
77
75
|
end
|
@@ -84,8 +82,8 @@ module Danconia
|
|
84
82
|
end
|
85
83
|
end
|
86
84
|
|
87
|
-
def
|
88
|
-
|
85
|
+
def respond_to_missing? method, *args
|
86
|
+
@amount.respond_to?(method, *args) || super
|
89
87
|
end
|
90
88
|
|
91
89
|
private
|
@@ -94,8 +92,16 @@ module Danconia
|
|
94
92
|
BigDecimal(object.to_s) rescue BigDecimal(0)
|
95
93
|
end
|
96
94
|
|
97
|
-
def clone_with amount, currency = @currency,
|
98
|
-
Money.new amount, currency, decimals: decimals,
|
95
|
+
def clone_with amount, currency = @currency, exchange_opts = @exchange_opts
|
96
|
+
Money.new amount, currency, decimals: @decimals, exchange_opts: exchange_opts
|
97
|
+
end
|
98
|
+
|
99
|
+
def amount_exchanged_to_this_currency other
|
100
|
+
if other.is_a? Money
|
101
|
+
other.exchange_to(currency, @exchange_opts).amount
|
102
|
+
else
|
103
|
+
other
|
104
|
+
end
|
99
105
|
end
|
100
106
|
end
|
101
107
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Danconia
|
2
|
+
module Serializable
|
3
|
+
def marshal_dump
|
4
|
+
{amount: @amount, currency: @currency.code, decimals: @decimals}
|
5
|
+
end
|
6
|
+
|
7
|
+
def marshal_load serialized_money
|
8
|
+
@amount = serialized_money[:amount]
|
9
|
+
@currency = Currency.find(serialized_money[:currency])
|
10
|
+
@decimals = serialized_money[:decimals]
|
11
|
+
end
|
12
|
+
|
13
|
+
def as_json _options = {}
|
14
|
+
{amount: @amount, currency: @currency.code}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,20 +1,43 @@
|
|
1
1
|
module Danconia
|
2
2
|
module Stores
|
3
|
+
# Store implementation that persist rates using ActiveRecord.
|
3
4
|
class ActiveRecord
|
5
|
+
# @param unique_keys [Array] each save_rates will update records with this keys' values
|
6
|
+
# @param date_field [Symbol] used when storing daily rates
|
7
|
+
def initialize unique_keys: %i[pair], date_field: nil
|
8
|
+
@unique_keys = unique_keys
|
9
|
+
@date_field = date_field
|
10
|
+
end
|
11
|
+
|
12
|
+
# Creates or updates the rates by the `unique_keys` provided in the constructor.
|
13
|
+
#
|
14
|
+
# @param rates [Array] must be an array of maps.
|
4
15
|
def save_rates rates
|
5
16
|
ExchangeRate.transaction do
|
6
|
-
rates.each do |
|
7
|
-
ExchangeRate
|
17
|
+
rates.each do |fields|
|
18
|
+
ExchangeRate
|
19
|
+
.where(fields.slice(*@unique_keys))
|
20
|
+
.first_or_initialize
|
21
|
+
.update(fields.slice(*ExchangeRate.column_names.map(&:to_sym)))
|
8
22
|
end
|
9
23
|
end
|
10
24
|
end
|
11
25
|
|
12
|
-
|
13
|
-
|
26
|
+
# Returns an array of maps like the one it received.
|
27
|
+
def rates **filters
|
28
|
+
ExchangeRate.where(process_filters(filters)).map { |er| er.attributes.symbolize_keys }
|
14
29
|
end
|
15
30
|
|
16
|
-
|
17
|
-
|
31
|
+
private
|
32
|
+
|
33
|
+
def process_filters filters
|
34
|
+
if @date_field
|
35
|
+
param = filters.delete(@date_field) || Date.today
|
36
|
+
last_record = ExchangeRate.where(filters).where("#{@date_field} <= ?", param).order(@date_field => :desc).first
|
37
|
+
filters.merge(@date_field => (last_record[@date_field] if last_record))
|
38
|
+
else
|
39
|
+
filters
|
40
|
+
end
|
18
41
|
end
|
19
42
|
end
|
20
43
|
|
@@ -1,19 +1,14 @@
|
|
1
1
|
module Danconia
|
2
2
|
module Stores
|
3
3
|
class InMemory
|
4
|
-
attr_reader :rates
|
5
|
-
|
6
|
-
def initialize rates: {}
|
7
|
-
save_rates rates
|
8
|
-
end
|
9
|
-
|
10
|
-
# @rates should be of a map of pair->rate like {'USDEUR' => 1.25}
|
11
4
|
def save_rates rates
|
12
5
|
@rates = rates
|
13
6
|
end
|
14
7
|
|
15
|
-
def
|
16
|
-
@rates
|
8
|
+
def rates **filters
|
9
|
+
@rates.select do |r|
|
10
|
+
filters.all? { |k, v| r[k] == v }
|
11
|
+
end
|
17
12
|
end
|
18
13
|
end
|
19
14
|
end
|
data/lib/danconia/version.rb
CHANGED
data/lib/danconia.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
require 'active_support'
|
4
|
+
require 'active_support/core_ext/object/blank'
|
5
|
+
require 'active_support/core_ext/hash/reverse_merge'
|
6
|
+
|
1
7
|
require 'danconia/version'
|
2
8
|
require 'danconia/config'
|
3
9
|
require 'danconia/currency'
|
4
10
|
require 'danconia/money'
|
5
11
|
require 'danconia/kernel'
|
6
|
-
require 'danconia/
|
7
|
-
require 'danconia/exchanges/exchange'
|
12
|
+
require 'danconia/exchange'
|
8
13
|
require 'danconia/exchanges/fixed_rates'
|
9
|
-
require 'danconia/exchanges/currency_layer'
|
10
14
|
require 'danconia/stores/in_memory'
|
11
|
-
require 'danconia/stores/active_record'
|
data/shell.nix
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'danconia/exchanges/bna'
|
2
|
+
|
3
|
+
module Danconia
|
4
|
+
module Exchanges
|
5
|
+
describe BNA do
|
6
|
+
subject { BNA.new }
|
7
|
+
|
8
|
+
context 'fetch_rates' do
|
9
|
+
it 'extracts the rates from the html' do
|
10
|
+
stub_request(:get, 'https://www.bna.com.ar/Personas').to_return body: fixture('home.html')
|
11
|
+
rates = subject.fetch_rates
|
12
|
+
|
13
|
+
expect(rates.select { |r| r[:rate_type] == 'billetes' }).to eq [
|
14
|
+
{pair: 'USDARS', rate: 78.25, date: Date.new(2020, 9, 1), rate_type: 'billetes'},
|
15
|
+
{pair: 'EURARS', rate: 89, date: Date.new(2020, 9, 1), rate_type: 'billetes'},
|
16
|
+
{pair: 'BRLARS', rate: 14.5, date: Date.new(2020, 9, 1), rate_type: 'billetes'}
|
17
|
+
]
|
18
|
+
|
19
|
+
expect(rates.select { |r| r[:rate_type] == 'divisas' }).to eq [
|
20
|
+
{pair: 'USDARS', rate: 74.18, date: Date.new(2020, 8, 31), rate_type: 'divisas'},
|
21
|
+
{pair: 'EURARS', rate: 88.6822, date: Date.new(2020, 8, 31), rate_type: 'divisas'}
|
22
|
+
]
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'raise error if cannot parse the document' do
|
26
|
+
stub_request(:get, 'https://www.bna.com.ar/Personas').to_return body: 'some invalid html'
|
27
|
+
expect { subject.fetch_rates }.to raise_error Errors::APIError
|
28
|
+
end
|
29
|
+
|
30
|
+
def fixture file
|
31
|
+
File.read("#{__dir__}/fixtures/bna/#{file}")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'rates' do
|
36
|
+
it 'pass the params to the store and converts the array of rates back to hash' do
|
37
|
+
store = double('store')
|
38
|
+
expect(store).to receive(:rates)
|
39
|
+
.with(rate_type: 'billetes', date: nil)
|
40
|
+
.and_return([{pair: 'USDARS', rate: 3}])
|
41
|
+
|
42
|
+
exchange = BNA.new(store: store)
|
43
|
+
expect(exchange.rates(rate_type: 'billetes')).to eq 'USDARS' => 3
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'rate_type is required' do
|
47
|
+
expect { BNA.new.rates }.to raise_error ArgumentError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -1,55 +1,50 @@
|
|
1
|
-
require '
|
1
|
+
require 'danconia/exchanges/currency_layer'
|
2
2
|
|
3
3
|
module Danconia
|
4
4
|
module Exchanges
|
5
5
|
describe CurrencyLayer do
|
6
|
-
subject { CurrencyLayer.new access_key: '[KEY]' }
|
7
|
-
|
8
6
|
context 'fetch_rates' do
|
7
|
+
subject { CurrencyLayer.new access_key: '[KEY]' }
|
8
|
+
|
9
9
|
it 'uses the API to retrive the rates' do
|
10
|
-
stub_request(:get, 'http://www.apilayer.net/api/live?access_key=[KEY]')
|
11
|
-
|
12
|
-
|
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]')
|
11
|
+
.to_return(body: fixture('success.json'))
|
12
|
+
|
20
13
|
expect(subject.fetch_rates).to eq 'USDARS' => 27.110001, 'USDAUD' => 1.346196
|
21
14
|
end
|
22
15
|
|
23
16
|
it 'when the API returns an error' do
|
24
|
-
stub_request(:get, 'http://www.apilayer.net/api/live?access_key=[KEY]')
|
25
|
-
|
26
|
-
|
27
|
-
"error": {
|
28
|
-
"code": 104,
|
29
|
-
"info": "Your monthly usage limit has been reached. Please upgrade your subscription plan."
|
30
|
-
}
|
31
|
-
}
|
32
|
-
END
|
17
|
+
stub_request(:get, 'http://www.apilayer.net/api/live?access_key=[KEY]')
|
18
|
+
.to_return(body: fixture('failure.json'))
|
19
|
+
|
33
20
|
expect { subject.fetch_rates }.to raise_error Errors::APIError
|
34
21
|
end
|
22
|
+
|
23
|
+
def fixture file
|
24
|
+
File.read("#{__dir__}/fixtures/currency_layer/#{file}")
|
25
|
+
end
|
35
26
|
end
|
36
27
|
|
37
28
|
context 'update_rates!' do
|
38
29
|
it 'fetches the rates and stores them' do
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
30
|
+
store = double('store')
|
31
|
+
expect(store).to receive(:save_rates).with([{pair: 'USDARS', rate: 3}, {pair: 'USDAUD', rate: 4}])
|
32
|
+
|
33
|
+
exchange = CurrencyLayer.new(access_key: '...', store: store)
|
34
|
+
allow(exchange).to receive(:fetch_rates).and_return('USDARS' => 3, 'USDAUD' => 4)
|
35
|
+
exchange.update_rates!
|
44
36
|
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'rates' do
|
40
|
+
it 'converts the array from the store back to a map of pair to rates' do
|
41
|
+
store = double('store')
|
42
|
+
expect(store).to receive(:rates).and_return([{pair: 'USDARS', rate: 3}, {pair: 'USDAUD', rate: 4}])
|
45
43
|
|
46
|
-
|
47
|
-
|
48
|
-
expect(subject).to receive(:fetch_rates) { {'USDARS' => 3.1} }
|
49
|
-
subject.update_rates!
|
50
|
-
expect(subject.rate('USD', 'ARS')).to eq 3.1
|
44
|
+
exchange = CurrencyLayer.new(access_key: '...', store: store)
|
45
|
+
expect(exchange.rates).to eq 'USDARS' => 3, 'USDAUD' => 4
|
51
46
|
end
|
52
47
|
end
|
53
48
|
end
|
54
49
|
end
|
55
|
-
end
|
50
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Danconia
|
2
|
+
module Exchanges
|
3
|
+
describe Exchange do
|
4
|
+
context 'rate' do
|
5
|
+
it 'returns the exchange rate value for the supplied currencies' do
|
6
|
+
exchange = fake_exchange('USDEUR' => 3, 'USDARS' => 4)
|
7
|
+
expect(exchange.rate('USD', 'EUR')).to eq 3
|
8
|
+
expect(exchange.rate('USD', 'ARS')).to eq 4
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'if the direct conversion is not found, tries to find the inverse' do
|
12
|
+
exchange = fake_exchange('USDEUR' => 3)
|
13
|
+
expect(exchange.rate('EUR', 'USD')).to be_within(0.00001).of(1.0 / 3)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'if not direct nor inverse conversion is found, tries to convert through USD' do
|
17
|
+
exchange = fake_exchange('USDEUR' => 3, 'USDARS' => 6)
|
18
|
+
expect(exchange.rate('EUR', 'ARS')).to be_within(0.00001).of 2
|
19
|
+
expect(exchange.rate('ARS', 'EUR')).to be_within(0.00001).of 0.5
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'pairs can have a different common currency' do
|
23
|
+
exchange = fake_exchange('EURARS' => 3, 'BRLARS' => 1.5)
|
24
|
+
expect(exchange.rate('EUR', 'ARS')).to eq 3
|
25
|
+
expect(exchange.rate('ARS', 'EUR')).to be_within(0.00001).of(1.0 / 3)
|
26
|
+
expect(exchange.rate('BRL', 'ARS')).to eq 1.5
|
27
|
+
expect(exchange.rate('EUR', 'BRL')).to eq 3 / 1.5
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'raises an error if the conversion cannot be made' do
|
31
|
+
expect { fake_exchange({}).rate('USD', 'EUR') }.to raise_error Errors::ExchangeRateNotFound
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should allow to pass options to filter the rates' do
|
35
|
+
exchange = Class.new(Exchange) do
|
36
|
+
def rates type:
|
37
|
+
case type
|
38
|
+
when 'divisa' then {'USDARS' => 7}
|
39
|
+
when 'billete' then {'USDARS' => 8}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end.new
|
43
|
+
|
44
|
+
expect(exchange.rate('USD', 'ARS', type: 'divisa')).to eq 7
|
45
|
+
expect(exchange.rate('USD', 'ARS', type: 'billete')).to eq 8
|
46
|
+
end
|
47
|
+
|
48
|
+
def fake_exchange(rates)
|
49
|
+
FixedRates.new(rates: rates)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="es" class="pc">
|
3
|
+
|
4
|
+
<body>
|
5
|
+
<div id="rightHome">
|
6
|
+
<div class="col-md-3">
|
7
|
+
<div class="tabSmall">
|
8
|
+
<ul class="nav nav-tabs">
|
9
|
+
|
10
|
+
<li class="active"><a href="#billetes" data-toggle="tab">Cotización Billetes</a>
|
11
|
+
<div class="arrow"></div>
|
12
|
+
</li>
|
13
|
+
<li><a href="#divisas" data-toggle="tab">Cotización Divisas</a>
|
14
|
+
<div class="arrow"></div>
|
15
|
+
</li>
|
16
|
+
</ul>
|
17
|
+
|
18
|
+
<div class="tab-content">
|
19
|
+
|
20
|
+
<div class="tab-pane fade in active" id="billetes">
|
21
|
+
|
22
|
+
<table class="table cotizacion">
|
23
|
+
<thead>
|
24
|
+
<tr>
|
25
|
+
<th class="fechaCot">1/9/2020</th>
|
26
|
+
<th>Compra</th>
|
27
|
+
<th>Venta</th>
|
28
|
+
</tr>
|
29
|
+
</thead>
|
30
|
+
<tbody>
|
31
|
+
|
32
|
+
<tr>
|
33
|
+
<td class="tit">Dolar U.S.A</td>
|
34
|
+
<td>73,2500</td>
|
35
|
+
<td>78,2500</td>
|
36
|
+
</tr>
|
37
|
+
<tr>
|
38
|
+
<td class="tit">Euro</td>
|
39
|
+
<td>84,0000</td>
|
40
|
+
<td>89,0000</td>
|
41
|
+
</tr>
|
42
|
+
<tr>
|
43
|
+
<td class="tit">Real *</td>
|
44
|
+
<td>1250,0000</td>
|
45
|
+
<td>1450,0000</td>
|
46
|
+
</tr>
|
47
|
+
|
48
|
+
</tbody>
|
49
|
+
</table>
|
50
|
+
<a href="#" class="link-cotizacion" data-toggle="modal" data-target="#modalHistorico" id="buttonHistoricoBilletes">Ver histórico</a>
|
51
|
+
<div class="legal">Hora Actualización: 10:40</div>
|
52
|
+
<div class="legal">(*) cotización cada 100 unidades.</div>
|
53
|
+
|
54
|
+
</div>
|
55
|
+
|
56
|
+
<div class="tab-pane fade" id="divisas">
|
57
|
+
<table class="table cotizacion">
|
58
|
+
<thead>
|
59
|
+
<tr>
|
60
|
+
<th class="fechaCot">31/8/2020</th>
|
61
|
+
<th>Compra</th>
|
62
|
+
<th>Venta</th>
|
63
|
+
</tr>
|
64
|
+
</thead>
|
65
|
+
<tbody>
|
66
|
+
|
67
|
+
<tr>
|
68
|
+
<td class="tit">Dolar U.S.A</td>
|
69
|
+
<td>73.9800</td>
|
70
|
+
<td>74.1800</td>
|
71
|
+
</tr>
|
72
|
+
<tr>
|
73
|
+
<td class="tit">Libra Esterlina</td>
|
74
|
+
<td>98.8743</td>
|
75
|
+
<td>99.3641</td>
|
76
|
+
</tr>
|
77
|
+
<tr>
|
78
|
+
<td class="tit">Euro</td>
|
79
|
+
<td>88.2581</td>
|
80
|
+
<td>88.6822</td>
|
81
|
+
</tr>
|
82
|
+
<tr>
|
83
|
+
<td class="tit">Franco Suizos *</td>
|
84
|
+
<td>8188.4905</td>
|
85
|
+
<td>8221.8519</td>
|
86
|
+
</tr>
|
87
|
+
<tr>
|
88
|
+
<td class="tit">YENES *</td>
|
89
|
+
<td>69.8238</td>
|
90
|
+
<td>70.1124</td>
|
91
|
+
</tr>
|
92
|
+
<tr>
|
93
|
+
<td class="tit">Dolares Canadienses *</td>
|
94
|
+
<td>5675.3160</td>
|
95
|
+
<td>5698.6457</td>
|
96
|
+
</tr>
|
97
|
+
<tr>
|
98
|
+
<td class="tit">Coronas Danesas *</td>
|
99
|
+
<td>1184.4394</td>
|
100
|
+
<td>1195.0806</td>
|
101
|
+
</tr>
|
102
|
+
<tr>
|
103
|
+
<td class="tit">Coronas Noruegas *</td>
|
104
|
+
<td>845.1963</td>
|
105
|
+
<td>854.8381</td>
|
106
|
+
</tr>
|
107
|
+
<tr>
|
108
|
+
<td class="tit">Coronas Suecas *</td>
|
109
|
+
<td>853.8596</td>
|
110
|
+
<td>863.8369</td>
|
111
|
+
</tr>
|
112
|
+
|
113
|
+
</tbody>
|
114
|
+
</table>
|
115
|
+
<a href="#" class="link-cotizacion" data-toggle="modal" data-target="#modalHistorico" id="buttonHistoricoMonedas">Ver histórico</a>
|
116
|
+
<div class="legal">(*) cotización cada 100 unidades.</div>
|
117
|
+
<div class="legal leyenda">El tipo de cambio de cierre de divisa es suministrado al público a fines informativos, como referencia de la cotización de la divisa en el mercado mayorista al final de cada día.</div>
|
118
|
+
</div>
|
119
|
+
</div>
|
120
|
+
</div>
|
121
|
+
</div>
|
122
|
+
</body>
|
123
|
+
|
124
|
+
</html>
|
@@ -1,12 +1,10 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
1
|
module Danconia
|
4
2
|
describe Integrations::ActiveRecord, active_record: true do
|
5
3
|
context 'single currency' do
|
6
4
|
it 'setter' do
|
7
|
-
expect(Product.new(price: 1.536).read_attribute
|
8
|
-
expect(Product.new(price: nil).read_attribute
|
9
|
-
expect(Product.new(price: Money(3)).read_attribute
|
5
|
+
expect(Product.new(price: 1.536).read_attribute(:price)).to eq 1.54
|
6
|
+
expect(Product.new(price: nil).read_attribute(:price)).to eq nil
|
7
|
+
expect(Product.new(price: Money(3)).read_attribute(:price)).to eq 3
|
10
8
|
end
|
11
9
|
|
12
10
|
it 'getter' do
|
@@ -34,6 +32,28 @@ module Danconia
|
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
35
|
+
context 'exchange options support' do
|
36
|
+
let(:exchange) do
|
37
|
+
Class.new(Exchange) do
|
38
|
+
def rates rate_type:
|
39
|
+
case rate_type
|
40
|
+
when 'divisa' then {'USDARS' => 7}
|
41
|
+
when 'billete' then {'USDARS' => 8}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end.new
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'allows to specify options that will be pass to the exchange when exchanging to other currencies' do
|
48
|
+
klass = Class.new(ActiveRecord::Base) do
|
49
|
+
self.table_name = 'products'
|
50
|
+
money :price, rate_type: 'divisa'
|
51
|
+
end
|
52
|
+
|
53
|
+
expect(klass.new(price: Money(10, 'USD')).price.exchange_to('ARS', exchange: exchange)).to eq Money(70, 'ARS')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
37
57
|
class Product < ActiveRecord::Base
|
38
58
|
money :price, :tax, :discount, :cost
|
39
59
|
end
|