active_currency 0.1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +81 -0
- data/Rakefile +37 -0
- data/app/models/active_currency/application_record.rb +7 -0
- data/app/models/active_currency/rate.rb +23 -0
- data/db/migrate/20180911202100_create_active_currency_rates.rb +14 -0
- data/db/migrate/20180914064834_add_rates.rb +7 -0
- data/lib/active_currency.rb +13 -0
- data/lib/active_currency/add_rates.rb +22 -0
- data/lib/active_currency/cacheable_store.rb +27 -0
- data/lib/active_currency/database_store.rb +20 -0
- data/lib/active_currency/engine.rb +17 -0
- data/lib/active_currency/rate_store.rb +13 -0
- data/lib/active_currency/version.rb +5 -0
- metadata +170 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: '079217f3be4857bdc2856432a807341a94eea305'
|
4
|
+
data.tar.gz: defcb92fec6e6124a0a5566c9f9c9040b7efe31a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bddbb54d3fbbae7b918bcb2eb90e024e7413f5656fe887f46ee3577a3af2089d1a1993575aa126bf78f62ec504f36c78ccad684c6899b0a15dedc1c3ffa8d73a
|
7
|
+
data.tar.gz: a0776af8effc2d92c9ff6df9a2e9679bf9cea1ae671002987202abe638c5a1b499a6d7817a6dcea534d16b33b2322efb8d0c1143559585f1c5c7e5964dae0309
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2018 Sunny Ripert
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# ActiveCurrency
|
2
|
+
|
3
|
+
Rails plugin to retrieve and store the currency rates daily to integrate
|
4
|
+
with the `money-rails` gem.
|
5
|
+
|
6
|
+
## Rationale
|
7
|
+
|
8
|
+
Storing the current currency rates in the database using ActiveCurrency
|
9
|
+
provides the following advantages:
|
10
|
+
|
11
|
+
- Lets you find out what the currency rate you used in your application was
|
12
|
+
at any given time.
|
13
|
+
- Does not need to call an API to get the rates when starting or restarting
|
14
|
+
your web server.
|
15
|
+
- Choose how often you want to check for a currency (daily for example).
|
16
|
+
- Your users do not suffer the cost of making calls to the bank rates API.
|
17
|
+
- 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
|
+
## Usage
|
22
|
+
|
23
|
+
Store the current rate regularly by calling in a scheduled job (using something
|
24
|
+
like `sidekiq-scheduler` or `whenever`):
|
25
|
+
|
26
|
+
```rb
|
27
|
+
ActiveCurrency::AddRates.new.call
|
28
|
+
```
|
29
|
+
|
30
|
+
You can then exchange money by using the Money gem:
|
31
|
+
|
32
|
+
```rb
|
33
|
+
10.to_money('EUR').exchange_to('USD').cents
|
34
|
+
```
|
35
|
+
|
36
|
+
Or look up the currency rate:
|
37
|
+
|
38
|
+
```rb
|
39
|
+
ActiveCurrency::Rate.current_value_for('EUR', 'USD', 10.days.ago)
|
40
|
+
# => 1.151
|
41
|
+
ActiveCurrency::Rate.where(from: 'EUR', to: 'USD').pluck(:value)
|
42
|
+
# => [1.162, 1.162, 1.161, 1.61, 1.63, …]
|
43
|
+
```
|
44
|
+
|
45
|
+
## Installation
|
46
|
+
|
47
|
+
Add these lines to your application's `Gemfile`:
|
48
|
+
|
49
|
+
```rb
|
50
|
+
# Store and retrieve the currency from the database.
|
51
|
+
gem 'active_currency',
|
52
|
+
git: 'git@github.com:sunny/active_currency.git'
|
53
|
+
```
|
54
|
+
|
55
|
+
And in `config/initializers/money.rb`:
|
56
|
+
|
57
|
+
```rb
|
58
|
+
MoneyRails.configure do |config|
|
59
|
+
rate_store = ActiveCurrency::RateStore.new(%w[EUR USD])
|
60
|
+
config.default_bank = Money::Bank::VariableExchange.new(rate_store)
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
Then call `bundle exec rake db:migrate` to create the table that holds
|
65
|
+
the currency rates and fill it for the first time.
|
66
|
+
|
67
|
+
## Contributing
|
68
|
+
|
69
|
+
Please file issues and pull requests
|
70
|
+
[on GitHub](https://github.com/sunny/active_currency).
|
71
|
+
|
72
|
+
In developemnt, launch specs and code linter by calling:
|
73
|
+
|
74
|
+
```sh
|
75
|
+
bundle exec rake
|
76
|
+
```
|
77
|
+
|
78
|
+
## License
|
79
|
+
|
80
|
+
The gem is available as open source under the terms of the
|
81
|
+
[MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'bundler/setup'
|
5
|
+
rescue LoadError
|
6
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'rdoc/task'
|
10
|
+
|
11
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
12
|
+
rdoc.rdoc_dir = 'rdoc'
|
13
|
+
rdoc.title = 'ActiveCurrency'
|
14
|
+
rdoc.options << '--line-numbers'
|
15
|
+
rdoc.rdoc_files.include('README.md')
|
16
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
17
|
+
end
|
18
|
+
|
19
|
+
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
20
|
+
load 'rails/tasks/engine.rake'
|
21
|
+
|
22
|
+
load 'rails/tasks/statistics.rake'
|
23
|
+
|
24
|
+
require 'bundler/gem_tasks'
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rspec/core/rake_task'
|
28
|
+
RSpec::Core::RakeTask.new(:spec)
|
29
|
+
rescue LoadError
|
30
|
+
puts 'RSpec load error'
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'rubocop/rake_task'
|
34
|
+
|
35
|
+
RuboCop::RakeTask.new
|
36
|
+
|
37
|
+
task default: %i[spec rubocop]
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveCurrency
|
4
|
+
class Rate < ApplicationRecord
|
5
|
+
validates :from,
|
6
|
+
:to,
|
7
|
+
inclusion: { in: ->(_v) { Money.default_bank.store.currencies } },
|
8
|
+
presence: true
|
9
|
+
validates :value, numericality: { greater_than: 0 }
|
10
|
+
|
11
|
+
scope :before, ->(date) { where(arel_table[:created_at].lt(date)) }
|
12
|
+
|
13
|
+
def self.current_value_for(from, to, date = nil)
|
14
|
+
scope = date ? before(date) : all
|
15
|
+
|
16
|
+
scope
|
17
|
+
.where(from: from, to: to)
|
18
|
+
.order(:created_at)
|
19
|
+
.last
|
20
|
+
&.value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CreateActiveCurrencyRates < ActiveRecord::Migration[5.0]
|
4
|
+
def change
|
5
|
+
create_table :active_currency_rates do |t|
|
6
|
+
t.string :from
|
7
|
+
t.string :to
|
8
|
+
t.float :value
|
9
|
+
t.datetime :created_at
|
10
|
+
end
|
11
|
+
|
12
|
+
add_index :active_currency_rates, %i[from to created_at]
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'money-rails'
|
4
|
+
require 'eu_central_bank'
|
5
|
+
|
6
|
+
require 'active_currency/engine'
|
7
|
+
require 'active_currency/database_store'
|
8
|
+
require 'active_currency/cacheable_store'
|
9
|
+
require 'active_currency/rate_store'
|
10
|
+
require 'active_currency/add_rates'
|
11
|
+
|
12
|
+
module ActiveCurrency
|
13
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveCurrency
|
4
|
+
# Store the latest currency rates.
|
5
|
+
class AddRates
|
6
|
+
def call
|
7
|
+
currencies = Money.default_bank.store.currencies.map(&:to_s) - ['EUR']
|
8
|
+
return if currencies.blank?
|
9
|
+
|
10
|
+
bank = EuCentralBank.new
|
11
|
+
bank.update_rates
|
12
|
+
|
13
|
+
from = 'EUR'
|
14
|
+
currencies.each do |to|
|
15
|
+
rate = bank.get_rate(from, to)
|
16
|
+
|
17
|
+
Money.add_rate(from, to, rate)
|
18
|
+
Money.add_rate(to, from, 1 / rate)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveCurrency
|
4
|
+
# Mixin to add caching capability to a rate store when getting the current
|
5
|
+
# cache.
|
6
|
+
module CacheableStore
|
7
|
+
def get_rate(from, to, date = nil)
|
8
|
+
return super if date
|
9
|
+
|
10
|
+
Rails.cache.fetch(cache_key(from, to)) do
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_rate(from, to, rate, date = nil)
|
16
|
+
super
|
17
|
+
|
18
|
+
Rails.cache.delete(cache_key(from, to))
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def cache_key(from, to)
|
24
|
+
['active_currency_rate', from, to]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveCurrency
|
4
|
+
# Currency Store that uses our ActiveRecord model to save and retrieve a
|
5
|
+
# value.
|
6
|
+
class DatabaseStore
|
7
|
+
def get_rate(from, to, date = nil)
|
8
|
+
ActiveCurrency::Rate.current_value_for(from, to, date)
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_rate(from, to, rate, date = nil)
|
12
|
+
ActiveCurrency::Rate.create!(
|
13
|
+
from: from,
|
14
|
+
to: to,
|
15
|
+
value: rate,
|
16
|
+
created_at: date || Time.zone.now
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveCurrency
|
4
|
+
class Engine < ::Rails::Engine
|
5
|
+
isolate_namespace ActiveCurrency
|
6
|
+
|
7
|
+
initializer :append_migrations do |app|
|
8
|
+
# This prevents migrations from being loaded twice from the inside of the
|
9
|
+
# gem itself (dummy test app)
|
10
|
+
if app.root.to_s !~ /#{root}/
|
11
|
+
config.paths['db/migrate'].expanded.each do |migration_path|
|
12
|
+
app.config.paths['db/migrate'] << migration_path
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_currency
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sunny Ripert
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-09-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '5.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '5.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: money-rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: eu_central_bank
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sqlite3
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-rails
|
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'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: timecop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry
|
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'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: Store your currency.
|
126
|
+
email:
|
127
|
+
- sunny@sunfox.org
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- MIT-LICENSE
|
133
|
+
- README.md
|
134
|
+
- Rakefile
|
135
|
+
- app/models/active_currency/application_record.rb
|
136
|
+
- app/models/active_currency/rate.rb
|
137
|
+
- db/migrate/20180911202100_create_active_currency_rates.rb
|
138
|
+
- db/migrate/20180914064834_add_rates.rb
|
139
|
+
- lib/active_currency.rb
|
140
|
+
- lib/active_currency/add_rates.rb
|
141
|
+
- lib/active_currency/cacheable_store.rb
|
142
|
+
- lib/active_currency/database_store.rb
|
143
|
+
- lib/active_currency/engine.rb
|
144
|
+
- lib/active_currency/rate_store.rb
|
145
|
+
- lib/active_currency/version.rb
|
146
|
+
homepage: https://github.com/sunny/active_currency
|
147
|
+
licenses:
|
148
|
+
- MIT
|
149
|
+
metadata: {}
|
150
|
+
post_install_message:
|
151
|
+
rdoc_options: []
|
152
|
+
require_paths:
|
153
|
+
- lib
|
154
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
159
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - ">="
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
requirements: []
|
165
|
+
rubyforge_project:
|
166
|
+
rubygems_version: 2.6.11
|
167
|
+
signing_key:
|
168
|
+
specification_version: 4
|
169
|
+
summary: Rails plugin to store your currency regularly
|
170
|
+
test_files: []
|