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 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveCurrency
4
+ class ApplicationRecord < ActiveRecord::Base
5
+ self.abstract_class = true
6
+ end
7
+ end
@@ -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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddRates < ActiveRecord::Migration[5.0]
4
+ def up
5
+ ActiveCurrency::AddRates.new.call
6
+ end
7
+ 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
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveCurrency
4
+ class RateStore < DatabaseStore
5
+ include CacheableStore
6
+
7
+ attr_reader :currencies
8
+
9
+ def initialize(currencies)
10
+ @currencies = currencies
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveCurrency
4
+ VERSION = '0.1.0'
5
+ 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: []