active_currency 1.0.0 → 1.2.1
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/README.md +68 -16
- data/app/models/active_currency/rate.rb +10 -6
- data/db/migrate/20180911202100_create_active_currency_rates.rb +5 -1
- data/lib/active_currency.rb +0 -1
- data/lib/active_currency/add_rates.rb +39 -14
- data/lib/active_currency/engine.rb +6 -6
- data/lib/active_currency/migration.rb +2 -2
- data/lib/active_currency/version.rb +1 -1
- metadata +45 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 938c7b875052bf9d26b9ade8d9f1f268a94da7949a1685961a4265b01ed70a1c
|
4
|
+
data.tar.gz: cd167c1b911156c2025d75b29be92cfdfceed15aac7533b736db5bb63a59d3bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51af4e890792d65780f3ce822ca6bef58465b2a34578b7edb0c6776d8d104ba569ef282dd7004715944e7d388b44a3935c5e0659ae87fdbf3f01ce1b36fd1cec
|
7
|
+
data.tar.gz: 2b66ea9433ccf5e35b54e22296b95a765dd9b249930e02c056d49afcc0c9f8a6731e7340db6db606fcbda33be7b4bc5d4f6fac53dc75dbae7e4a1b6991f634cc
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# ActiveCurrency
|
2
2
|
|
3
|
+
[](https://circleci.com/gh/sunny/active_currency)
|
4
|
+
|
3
5
|
Rails plugin to retrieve and store the currency rates daily to integrate
|
4
6
|
with the `money-rails` gem.
|
5
7
|
|
@@ -8,34 +10,35 @@ with the `money-rails` gem.
|
|
8
10
|
Storing the current currency rates in the database using ActiveCurrency
|
9
11
|
provides the following advantages:
|
10
12
|
|
11
|
-
- Lets you
|
12
|
-
|
13
|
+
- Lets you query for the currency rate you actually used in your application at
|
14
|
+
any given time.
|
13
15
|
- Does not need to call an API to get the rates when starting or restarting
|
14
16
|
your web server.
|
15
|
-
-
|
17
|
+
- Does not depend on the file system to store cached rates.
|
18
|
+
- Choose how often you want to update the currency rates (daily for example).
|
16
19
|
- Your users do not suffer the cost of making calls to the bank rates API.
|
17
20
|
- Your app does not go down when the bank rates API does.
|
18
|
-
- When fetching the current rate, it uses your application cache in order not
|
19
|
-
to have to do a database query.
|
20
21
|
|
21
|
-
To fetch the
|
22
|
+
To fetch the *current* rate it uses your application cache instead of making
|
23
|
+
a call to the database.
|
22
24
|
|
23
25
|
## Usage
|
24
26
|
|
25
27
|
Store the current rate regularly by calling in a scheduled job (using something
|
26
|
-
like `sidekiq-scheduler` or `
|
28
|
+
like `sidekiq-scheduler`, `whenever`, or `active_scheduler`) with the currencies
|
29
|
+
you want to store:
|
27
30
|
|
28
31
|
```rb
|
29
32
|
ActiveCurrency::AddRates.call(%w[EUR USD])
|
30
33
|
```
|
31
34
|
|
32
|
-
You can then exchange money by using the Money gem:
|
35
|
+
You can then exchange money by using the Money gem helpers:
|
33
36
|
|
34
37
|
```rb
|
35
38
|
10.to_money('EUR').exchange_to('USD').cents
|
36
39
|
```
|
37
40
|
|
38
|
-
|
41
|
+
If you need to look up the previous currency rates:
|
39
42
|
|
40
43
|
```rb
|
41
44
|
ActiveCurrency::Rate.value_for('EUR', 'USD', 1.month.ago)
|
@@ -61,21 +64,41 @@ MoneyRails.configure do |config|
|
|
61
64
|
end
|
62
65
|
```
|
63
66
|
|
64
|
-
Then call `
|
67
|
+
Then call `bin/rake db:migrate` to create the table that holds
|
65
68
|
the currency rates and fill it for the first time.
|
66
69
|
|
70
|
+
## Fetching rates
|
71
|
+
|
72
|
+
By defaut it uses the [eu_central_bank] to update the currency rates.
|
73
|
+
|
74
|
+
If you prefer another API, you can provide any Money-compatible bank when
|
75
|
+
calling `ActiveCurrency::AddRates`. For example with the
|
76
|
+
[money-open-exchange-rates] gem:
|
77
|
+
|
78
|
+
```rb
|
79
|
+
require 'money/bank/open_exchange_rates_bank'
|
80
|
+
|
81
|
+
bank = Money::Bank::OpenExchangeRatesBank.new(Money::RatesStore::Memory.new)
|
82
|
+
bank.app_id = '…'
|
83
|
+
|
84
|
+
ActiveCurrency::AddRates.call(%w[EUR USD], bank: bank)
|
85
|
+
```
|
86
|
+
|
87
|
+
The first currency you give to `AddRates` is considered the default currency
|
88
|
+
against which other currency rates will be guessed if they are unavailable.
|
89
|
+
|
67
90
|
## Tests
|
68
91
|
|
69
92
|
In your app test suite you may not want to have to fill your database to be
|
70
93
|
able to exchange currencies.
|
71
94
|
|
72
|
-
For that, you
|
95
|
+
For that, you can use a fake rate store in your `rails_helper.rb`:
|
73
96
|
|
74
97
|
```rb
|
75
|
-
|
98
|
+
MoneyRails.configure do |config|
|
76
99
|
rate_store = Money::RatesStore::Memory.new.tap do |store|
|
77
|
-
store.add_rate('USD', 'EUR',
|
78
|
-
store.add_rate('EUR', 'USD', 1.
|
100
|
+
store.add_rate('USD', 'EUR', 1.5)
|
101
|
+
store.add_rate('EUR', 'USD', 1.4)
|
79
102
|
end
|
80
103
|
config.default_bank = Money::Bank::VariableExchange.new(rate_store)
|
81
104
|
end
|
@@ -86,10 +109,38 @@ end
|
|
86
109
|
Please file issues and pull requests
|
87
110
|
[on GitHub](https://github.com/sunny/active_currency).
|
88
111
|
|
89
|
-
|
112
|
+
## Development
|
113
|
+
|
114
|
+
Install:
|
90
115
|
|
91
116
|
```sh
|
92
|
-
BUNDLE_GEMFILE=Gemfile-
|
117
|
+
BUNDLE_GEMFILE=Gemfile-rails6.0 bundle install
|
118
|
+
```
|
119
|
+
|
120
|
+
Launch specs and linters:
|
121
|
+
|
122
|
+
```sh
|
123
|
+
BUNDLE_GEMFILE=Gemfile-rails6.0 bin/rake
|
124
|
+
```
|
125
|
+
|
126
|
+
## Release
|
127
|
+
|
128
|
+
Update `CHANGELOG.md`, update version in `lib/active_currency/version.rb`.
|
129
|
+
|
130
|
+
Then:
|
131
|
+
|
132
|
+
```sh
|
133
|
+
gem install bundler:1.3.0
|
134
|
+
BUNDLE_GEMFILE=Gemfile-rails3.2 bundle update
|
135
|
+
BUNDLE_GEMFILE=Gemfile-rails4.2 bundle update
|
136
|
+
BUNDLE_GEMFILE=Gemfile-rails5.2 bundle update
|
137
|
+
|
138
|
+
gem install bundler:2.1.2
|
139
|
+
BUNDLE_GEMFILE=Gemfile-rails6.0 bundle update
|
140
|
+
|
141
|
+
git add CHANGELOG.md lib/active_currency/version.rb Gemfile-rails*.lock
|
142
|
+
git commit -m v`ruby -r./lib/active_currency/version <<< 'puts ActiveCurrency::VERSION'`
|
143
|
+
bin/rake release
|
93
144
|
```
|
94
145
|
|
95
146
|
## License
|
@@ -98,3 +149,4 @@ The gem is available as open source under the terms of the
|
|
98
149
|
[MIT License](http://opensource.org/licenses/MIT).
|
99
150
|
|
100
151
|
[eu_central_bank]: https://github.com/RubyMoney/eu_central_bank
|
152
|
+
[money-open-exchange-rates]: https://github.com/spk/money-open-exchange-rates
|
@@ -2,18 +2,22 @@
|
|
2
2
|
|
3
3
|
module ActiveCurrency
|
4
4
|
class Rate < ActiveRecord::Base
|
5
|
-
validates :from,
|
6
|
-
|
7
|
-
presence: true
|
5
|
+
validates :from, presence: true
|
6
|
+
validates :to, presence: true
|
8
7
|
validates :value, numericality: { greater_than: 0 }
|
9
8
|
|
10
|
-
scope :before, ->(date) {
|
9
|
+
scope :before, ->(date) {
|
10
|
+
where(arel_table[:created_at].lt(date))
|
11
|
+
}
|
11
12
|
|
12
13
|
def self.value_for(from, to, date = nil)
|
13
|
-
|
14
|
+
from = Money::Currency.new(from)
|
15
|
+
to = Money::Currency.new(to)
|
16
|
+
return 1 if from == to
|
14
17
|
|
18
|
+
scope = date ? before(date) : all_scope
|
15
19
|
scope
|
16
|
-
.where(from: from, to: to)
|
20
|
+
.where(from: from.iso_code, to: to.iso_code)
|
17
21
|
.order(:created_at)
|
18
22
|
.last
|
19
23
|
&.value
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_currency/migration'
|
4
|
+
|
3
5
|
class CreateActiveCurrencyRates < ActiveCurrency::Migration
|
4
6
|
def change
|
5
7
|
create_table :active_currency_rates do |t|
|
@@ -9,6 +11,8 @@ class CreateActiveCurrencyRates < ActiveCurrency::Migration
|
|
9
11
|
t.datetime :created_at
|
10
12
|
end
|
11
13
|
|
12
|
-
add_index :active_currency_rates,
|
14
|
+
add_index :active_currency_rates,
|
15
|
+
%i[from to created_at],
|
16
|
+
name: 'index_active_currency_rates'
|
13
17
|
end
|
14
18
|
end
|
data/lib/active_currency.rb
CHANGED
@@ -3,36 +3,61 @@
|
|
3
3
|
module ActiveCurrency
|
4
4
|
# Store the latest currency rates.
|
5
5
|
class AddRates
|
6
|
-
def initialize(currencies)
|
7
|
-
@currencies = currencies
|
6
|
+
def initialize(currencies, bank: EuCentralBank.new)
|
7
|
+
@currencies = currencies.map(&:to_s).map(&:upcase)
|
8
|
+
@bank = bank
|
8
9
|
end
|
9
10
|
|
10
11
|
def call
|
11
|
-
bank = EuCentralBank.new
|
12
12
|
bank.update_rates
|
13
13
|
|
14
|
-
from
|
15
|
-
currencies.map(&:to_s).each do |to|
|
16
|
-
next if to == from
|
17
|
-
|
18
|
-
rate = bank.get_rate(from, to)
|
14
|
+
rates_hash.each do |(from, to), rate|
|
19
15
|
store.add_rate(from, to, rate)
|
20
|
-
store.add_rate(to, from, 1 / rate)
|
21
16
|
end
|
17
|
+
|
18
|
+
nil
|
22
19
|
end
|
23
20
|
|
24
|
-
def self.call(currencies)
|
25
|
-
new(currencies).call
|
21
|
+
def self.call(currencies, *options)
|
22
|
+
new(currencies, *options).call
|
26
23
|
end
|
27
24
|
|
28
25
|
private
|
29
26
|
|
30
|
-
|
31
|
-
@currencies.map(&:to_s).map(&:upcase)
|
32
|
-
end
|
27
|
+
attr_accessor :bank, :currencies
|
33
28
|
|
34
29
|
def store
|
35
30
|
@store ||= ActiveCurrency::RateStore.new
|
36
31
|
end
|
32
|
+
|
33
|
+
def rates_hash
|
34
|
+
currencies.each_with_object({}) do |from, hash|
|
35
|
+
currencies.each do |to|
|
36
|
+
next if from == to
|
37
|
+
|
38
|
+
hash[[from, to]] = get_rate(hash, from, to)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def get_rate(hash, from, to)
|
44
|
+
rate = bank.get_rate(from, to)
|
45
|
+
return rate if rate
|
46
|
+
|
47
|
+
# Inverse rate (not so good)
|
48
|
+
inverse = hash[[to, from]]
|
49
|
+
return 1.fdiv(inverse) if inverse
|
50
|
+
|
51
|
+
# Rate going through the first currency (desperate)
|
52
|
+
from_main = hash[[from, main_currency]]
|
53
|
+
to_main = hash[[main_currency, to]]
|
54
|
+
return from_main * to_main if from_main && to_main
|
55
|
+
|
56
|
+
raise "Unknown rate between #{from} and #{to}"
|
57
|
+
end
|
58
|
+
|
59
|
+
def main_currency
|
60
|
+
currencies.first
|
61
|
+
end
|
37
62
|
end
|
38
63
|
end
|
@@ -5,13 +5,13 @@ module ActiveCurrency
|
|
5
5
|
isolate_namespace ActiveCurrency
|
6
6
|
|
7
7
|
initializer :append_migrations do |app|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
8
|
+
next if app.root.to_s.match(root.to_s)
|
9
|
+
|
10
|
+
paths = ActiveRecord::Migrator.migrations_paths
|
11
|
+
config.paths['db/migrate'].expanded.each do |path|
|
12
|
+
paths << path
|
14
13
|
end
|
14
|
+
paths.uniq!
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActiveCurrency
|
4
4
|
# Helps support previous version of Rails in migrations.
|
5
|
-
if Rails.version > '
|
6
|
-
class Migration < ActiveRecord::Migration[
|
5
|
+
if Rails.version > '5.0'
|
6
|
+
class Migration < ActiveRecord::Migration[5.0]; end
|
7
7
|
else
|
8
8
|
class Migration < ActiveRecord::Migration; end
|
9
9
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_currency
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sunny Ripert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: rspec-rails
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,6 +94,20 @@ dependencies:
|
|
80
94
|
- - ">="
|
81
95
|
- !ruby/object:Gem::Version
|
82
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec_junit_formatter
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
83
111
|
- !ruby/object:Gem::Dependency
|
84
112
|
name: timecop
|
85
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,6 +150,20 @@ dependencies:
|
|
122
150
|
- - ">="
|
123
151
|
- !ruby/object:Gem::Version
|
124
152
|
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rubocop-rails
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
125
167
|
description: Store your currency.
|
126
168
|
email:
|
127
169
|
- sunny@sunfox.org
|
@@ -161,8 +203,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
161
203
|
- !ruby/object:Gem::Version
|
162
204
|
version: '0'
|
163
205
|
requirements: []
|
164
|
-
|
165
|
-
rubygems_version: 2.5.2.3
|
206
|
+
rubygems_version: 3.0.3
|
166
207
|
signing_key:
|
167
208
|
specification_version: 4
|
168
209
|
summary: Rails plugin to store your currency regularly
|