money-open-exchange-rates 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 78e5f382a89d63ca2810fc5fe93b373bdfe0dd2b
4
- data.tar.gz: c8262bcd25e9a5295c37db99fe197d9ae23a1e55
3
+ metadata.gz: b5a908e851c07a69ab0aca84da9edb9c1f596112
4
+ data.tar.gz: d63b70c7f7bd0300e44bdee7b723c558e862ef62
5
5
  SHA512:
6
- metadata.gz: ec7b0fca5d35cc3427a434e97ec5a8f7d2c3a2f52bbbc37df7178e4b99c7d7b386a429489ebbd18a1b9142ed31d2fd9492b7f2557330cdc3b4bce77bff36ce7c
7
- data.tar.gz: 7eb57b17c948ecdc2e83b951dfb2c6ce0fae81e242c32129788ead04fa1eb3fe75120c38c44d3f49393c52e2bcca85f4478108be3966ee9910a85df495c9cf68
6
+ metadata.gz: d4dfe2d85a96e772b193cfc8bd2986c2caf8e02e56fa1eb46ce86e9993d47b80d00a26eb243a1d6a3b51de56d8259a8de674217e99e89731e94a8665415c9c95
7
+ data.tar.gz: bd056b885c383fa246593a57685c61a2f33001ad1a120f5415d9d982272a03942af9bb9599976078912a424cf2626e0cb687fc8cce2064a728e175bad4b2ea99
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source "https://rubygems.org"
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
data/README.markdown CHANGED
@@ -16,6 +16,10 @@ moe.update_rates
16
16
  # set the seconds after than the current rates are automatically expired
17
17
  # by default, they never expire
18
18
  moe.ttl_in_seconds = 86400
19
+ # (optional)
20
+ # use https to fetch rates from Open Exchange Rates
21
+ # disabled by default to support free-tier users
22
+ moe.secure_connection = true
19
23
  # Store in cache
20
24
  moe.save_rates
21
25
 
@@ -63,6 +67,7 @@ Dmitry Dedov (2)
63
67
  chatgris (2)
64
68
  Sam Lown (1)
65
69
  Fabio Cantoni (1)
70
+ shpupti (1)
66
71
 
67
72
  ## License
68
73
 
@@ -5,15 +5,16 @@ require 'json'
5
5
 
6
6
  class Money
7
7
  module Bank
8
- class InvalidCache < StandardError ; end
8
+ class InvalidCache < StandardError; end
9
9
 
10
- class NoAppId < StandardError ; end
10
+ class NoAppId < StandardError; end
11
11
 
12
+ # OpenExchangeRatesBank base class
12
13
  class OpenExchangeRatesBank < Money::Bank::VariableExchange
13
-
14
14
  OER_URL = 'http://openexchangerates.org/latest.json'
15
+ SECURE_OER_URL = OER_URL.tr('http:', 'https:')
15
16
 
16
- attr_accessor :cache, :app_id
17
+ attr_accessor :cache, :app_id, :secure_connection
17
18
  attr_reader :doc, :oer_rates, :rates_expiration, :ttl_in_seconds
18
19
 
19
20
  def ttl_in_seconds=(value)
@@ -27,16 +28,14 @@ class Money
27
28
  currency = exchange_rate.first
28
29
  next unless Money::Currency.find(currency)
29
30
  set_rate('USD', currency, rate)
30
- set_rate(currency, 'USD', 1.0/rate)
31
+ set_rate(currency, 'USD', 1.0 / rate)
31
32
  end
32
33
  end
33
34
 
34
35
  def save_rates
35
- raise InvalidCache unless cache
36
+ fail InvalidCache unless cache
36
37
  text = read_from_url
37
- if has_valid_rates?(text)
38
- store_in_cache(text)
39
- end
38
+ store_in_cache(text) if valid_rates?(text)
40
39
  rescue Errno::ENOENT
41
40
  raise InvalidCache
42
41
  end
@@ -47,10 +46,17 @@ class Money
47
46
  end
48
47
 
49
48
  def expire_rates
50
- if ttl_in_seconds && rates_expiration <= Time.now
51
- update_rates
52
- refresh_rates_expiration
53
- end
49
+ return unless ttl_in_seconds
50
+ return if rates_expiration > Time.now
51
+ update_rates
52
+ refresh_rates_expiration
53
+ end
54
+
55
+ def source_url
56
+ fail NoAppId if app_id.nil? || app_id.empty?
57
+ oer_url = OER_URL
58
+ oer_url = SECURE_OER_URL if secure_connection
59
+ "#{oer_url}?app_id=#{app_id}"
54
60
  end
55
61
 
56
62
  protected
@@ -75,18 +81,13 @@ class Money
75
81
  end
76
82
  end
77
83
 
78
- def source_url
79
- raise NoAppId if app_id.nil? || app_id.empty?
80
- "#{OER_URL}?app_id=#{app_id}"
81
- end
82
-
83
84
  def read_from_url
84
85
  open(source_url).read
85
86
  end
86
87
 
87
- def has_valid_rates?(text)
88
+ def valid_rates?(text)
88
89
  parsed = JSON.parse(text)
89
- parsed && parsed.has_key?('rates')
90
+ parsed && parsed.key?('rates')
90
91
  rescue JSON::ParserError
91
92
  false
92
93
  end
@@ -4,123 +4,125 @@ require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
4
4
 
5
5
  describe Money::Bank::OpenExchangeRatesBank do
6
6
  subject { Money::Bank::OpenExchangeRatesBank.new }
7
-
8
- before do
9
- @cache_path = File.expand_path(File.join(File.dirname(__FILE__), 'latest.json'))
7
+ let(:url) { Money::Bank::OpenExchangeRatesBank::OER_URL }
8
+ let(:secure_url) { Money::Bank::OpenExchangeRatesBank::SECURE_OER_URL }
9
+ let(:temp_cache_path) do
10
+ File.expand_path(File.join(File.dirname(__FILE__), 'tmp.json'))
11
+ end
12
+ let(:cache_path) do
13
+ File.expand_path(File.join(File.dirname(__FILE__), 'latest.json'))
10
14
  end
11
15
 
12
16
  describe 'exchange' do
13
17
  before do
14
18
  subject.app_id = TEST_APP_ID
15
- @temp_cache_path = File.expand_path(File.join(File.dirname(__FILE__), 'tmp.json'))
16
- subject.cache = @temp_cache_path
17
- stub(subject).read_from_url { File.read @cache_path }
19
+ subject.cache = temp_cache_path
20
+ stub(subject).read_from_url { File.read cache_path }
18
21
  subject.save_rates
19
22
  end
20
23
 
21
24
  after do
22
- File.unlink(@temp_cache_path)
25
+ File.unlink(temp_cache_path)
23
26
  end
24
27
 
25
- describe "without rates" do
26
- it "should be able to exchange a money to its own currency even without rates" do
27
- money = Money.new(0, "USD")
28
- subject.exchange_with(money, "USD").must_equal money
28
+ describe 'without rates' do
29
+ it 'able to exchange a money to its own currency even without rates' do
30
+ money = Money.new(0, 'USD')
31
+ subject.exchange_with(money, 'USD').must_equal money
29
32
  end
30
33
 
31
- it "should raise if it can't find an exchange rate" do
32
- money = Money.new(0, "USD")
33
- proc { subject.exchange_with(money, "SSP") }.must_raise Money::Bank::UnknownRate
34
+ it "raise if it can't find an exchange rate" do
35
+ money = Money.new(0, 'USD')
36
+ proc { subject.exchange_with(money, 'SSP') }
37
+ .must_raise Money::Bank::UnknownRate
34
38
  end
35
39
  end
36
40
 
37
- describe "with rates" do
38
- before do
41
+ describe 'with rates' do
42
+ before do
39
43
  subject.update_rates
40
44
  end
41
45
 
42
- it "should be able to exchange money from USD to a known exchange rate" do
43
- money = Money.new(100, "USD")
44
- subject.exchange_with(money, "BBD").must_equal Money.new(200, 'BBD')
46
+ it 'should be able to exchange money from USD to a known exchange rate' do
47
+ money = Money.new(100, 'USD')
48
+ subject.exchange_with(money, 'BBD').must_equal Money.new(200, 'BBD')
45
49
  end
46
50
 
47
- it "should be able to exchange money from a known exchange rate to USD" do
48
- money = Money.new(200, "BBD")
49
- subject.exchange_with(money, "USD").must_equal Money.new(100, 'USD')
51
+ it 'should be able to exchange money from a known exchange rate to USD' do
52
+ money = Money.new(200, 'BBD')
53
+ subject.exchange_with(money, 'USD').must_equal Money.new(100, 'USD')
50
54
  end
51
55
 
52
56
  it "should raise if it can't find an exchange rate" do
53
- money = Money.new(0, "USD")
54
- proc { subject.exchange_with(money, "SSP") }.must_raise Money::Bank::UnknownRate
57
+ money = Money.new(0, 'USD')
58
+ proc { subject.exchange_with(money, 'SSP') }
59
+ .must_raise Money::Bank::UnknownRate
55
60
  end
56
61
  end
57
-
58
62
  end
59
63
 
60
64
  describe 'update_rates' do
61
65
  before do
62
66
  subject.app_id = TEST_APP_ID
63
- subject.cache = @cache_path
67
+ subject.cache = cache_path
64
68
  subject.update_rates
65
69
  end
66
70
 
67
- it "should update itself with exchange rates from OpenExchangeRates" do
71
+ it 'should update itself with exchange rates from OpenExchangeRates' do
68
72
  subject.oer_rates.keys.each do |currency|
69
73
  next unless Money::Currency.find(currency)
70
- subject.get_rate("USD", currency).must_be :>, 0
74
+ subject.get_rate('USD', currency).must_be :>, 0
71
75
  end
72
76
  end
73
77
 
74
- it "should not return 0 with integer rate" do
78
+ it 'should not return 0 with integer rate' do
75
79
  wtf = {
76
- :priority => 1,
77
- :iso_code => "WTF",
78
- :name => "WTF",
79
- :symbol => "WTF",
80
- :subunit => "Cent",
81
- :subunit_to_unit => 1000,
82
- :separator => ".",
83
- :delimiter => ","
80
+ priority: 1,
81
+ iso_code: 'WTF',
82
+ name: 'WTF',
83
+ symbol: 'WTF',
84
+ subunit: 'Cent',
85
+ subunit_to_unit: 1000,
86
+ separator: '.',
87
+ delimiter: ','
84
88
  }
85
89
  Money::Currency.register(wtf)
86
- subject.add_rate("USD", "WTF", 2)
87
- subject.add_rate("WTF", "USD", 2)
90
+ subject.add_rate('USD', 'WTF', 2)
91
+ subject.add_rate('WTF', 'USD', 2)
88
92
  subject.exchange_with(5000.to_money('WTF'), 'USD').cents.wont_equal 0
89
93
  end
90
94
 
91
95
  # in response to #4
92
- it "should exchange btc" do
96
+ it 'should exchange btc' do
93
97
  btc = {
94
- :priority => 1,
95
- :iso_code => "BTC",
96
- :name => "Bitcoin",
97
- :symbol => "BTC",
98
- :subunit => "Cent",
99
- :subunit_to_unit => 1000,
100
- :separator => ".",
101
- :delimiter => ","
98
+ priority: 1,
99
+ iso_code: 'BTC',
100
+ name: 'Bitcoin',
101
+ symbol: 'BTC',
102
+ subunit: 'Cent',
103
+ subunit_to_unit: 1000,
104
+ separator: '.',
105
+ delimiter: ','
102
106
  }
103
107
  Money::Currency.register(btc)
104
108
  rate = 13.7603
105
- subject.add_rate("USD", "BTC", 1 / 13.7603)
106
- subject.add_rate("BTC", "USD", rate)
107
- subject.exchange_with(100.to_money("BTC"), 'USD').cents.must_equal 137603
109
+ subject.add_rate('USD', 'BTC', 1 / 13.7603)
110
+ subject.add_rate('BTC', 'USD', rate)
111
+ subject.exchange_with(100.to_money('BTC'), 'USD').cents.must_equal 137_603
108
112
  end
109
113
  end
110
114
 
111
115
  describe 'App ID' do
112
-
113
116
  before do
114
- @temp_cache_path = File.expand_path(File.join(File.dirname(__FILE__), 'tmp.json'))
115
- subject.cache = @temp_cache_path
116
- stub(OpenURI::OpenRead).open(Money::Bank::OpenExchangeRatesBank::OER_URL) { File.read @cache_path }
117
+ subject.cache = temp_cache_path
118
+ stub(OpenURI::OpenRead).open(url) { File.read cache_path }
117
119
  end
118
120
 
119
121
  it 'should raise an error if no App ID is set' do
120
122
  proc { subject.save_rates }.must_raise Money::Bank::NoAppId
121
123
  end
122
124
 
123
- #TODO: As App IDs are compulsory soon, need to add more tests handle
125
+ # TODO: As App IDs are compulsory soon, need to add more tests handle
124
126
  # app_id-specific errors from
125
127
  # https://openexchangerates.org/documentation#errors
126
128
  end
@@ -132,7 +134,7 @@ describe Money::Bank::OpenExchangeRatesBank do
132
134
  end
133
135
 
134
136
  it 'should get from url' do
135
- stub(OpenURI::OpenRead).open(Money::Bank::OpenExchangeRatesBank::OER_URL) { File.read @cache_path }
137
+ stub(OpenURI::OpenRead).open(url) { File.read cache_path }
136
138
  subject.update_rates
137
139
  subject.oer_rates.wont_be_empty
138
140
  end
@@ -142,14 +144,34 @@ describe Money::Bank::OpenExchangeRatesBank do
142
144
  end
143
145
  end
144
146
 
147
+ describe 'secure_connection' do
148
+ it "should use the non-secure http url if secure_connection isn't set" do
149
+ subject.secure_connection = nil
150
+ subject.app_id = TEST_APP_ID
151
+ subject.source_url.must_equal "#{url}?app_id=#{TEST_APP_ID}"
152
+ end
153
+
154
+ it 'should use the non-secure http url if secure_connection is false' do
155
+ subject.secure_connection = false
156
+ subject.app_id = TEST_APP_ID
157
+ subject.source_url.must_equal "#{url}?app_id=#{TEST_APP_ID}"
158
+ end
159
+
160
+ it 'should use the secure https url if secure_connection is set to true' do
161
+ subject.secure_connection = true
162
+ subject.app_id = TEST_APP_ID
163
+ subject.source_url.must_equal "#{secure_url}?app_id=#{TEST_APP_ID}"
164
+ end
165
+ end
166
+
145
167
  describe 'no valid file for cache' do
146
168
  before do
147
- subject.cache = "space_dir#{rand(999999999)}/out_space_file.json"
169
+ subject.cache = "space_dir#{rand(999_999_999)}/out_space_file.json"
148
170
  subject.app_id = TEST_APP_ID
149
171
  end
150
172
 
151
173
  it 'should get from url' do
152
- stub(OpenURI::OpenRead).open(Money::Bank::OpenExchangeRatesBank::OER_URL) { File.read @cache_path }
174
+ stub(OpenURI::OpenRead).open(url) { File.read cache_path }
153
175
  subject.update_rates
154
176
  subject.oer_rates.wont_be_empty
155
177
  end
@@ -161,40 +183,38 @@ describe Money::Bank::OpenExchangeRatesBank do
161
183
 
162
184
  describe 'using proc for cache' do
163
185
  before :each do
164
- $global_rates = nil
165
- subject.cache = Proc.new {|v|
186
+ @global_rates = nil
187
+ subject.cache = proc {|v|
166
188
  if v
167
- $global_rates = v
189
+ @global_rates = v
168
190
  else
169
- $global_rates
191
+ @global_rates
170
192
  end
171
193
  }
172
194
  subject.app_id = TEST_APP_ID
173
195
  end
174
196
 
175
197
  it 'should get from url normally' do
176
- stub(subject).source_url() { @cache_path }
198
+ stub(subject).source_url { cache_path }
177
199
  subject.update_rates
178
200
  subject.oer_rates.wont_be_empty
179
201
  end
180
202
 
181
203
  it 'should save from url and get from cache' do
182
- stub(subject).source_url { @cache_path }
204
+ stub(subject).source_url { cache_path }
183
205
  subject.save_rates
184
- $global_rates.wont_be_empty
206
+ @global_rates.wont_be_empty
185
207
  dont_allow(subject).source_url
186
208
  subject.update_rates
187
209
  subject.oer_rates.wont_be_empty
188
210
  end
189
-
190
211
  end
191
212
 
192
213
  describe 'save rates' do
193
214
  before do
194
215
  subject.app_id = TEST_APP_ID
195
- @temp_cache_path = File.expand_path(File.join(File.dirname(__FILE__), 'tmp.json'))
196
- subject.cache = @temp_cache_path
197
- stub(OpenURI::OpenRead).open(Money::Bank::OpenExchangeRatesBank::OER_URL) { File.read @cache_path }
216
+ subject.cache = temp_cache_path
217
+ stub(OpenURI::OpenRead).open(url) { File.read cache_path }
198
218
  subject.save_rates
199
219
  end
200
220
 
@@ -202,33 +222,33 @@ describe Money::Bank::OpenExchangeRatesBank do
202
222
  begin
203
223
  subject.update_rates
204
224
  rescue
205
- assert false, "Should allow updating after saving"
225
+ assert false, 'Should allow updating after saving'
206
226
  end
207
227
  end
208
228
 
209
- it "should not break an existing file if save fails to read" do
210
- initial_size = File.read(@temp_cache_path).size
211
- stub(subject).read_from_url {""}
229
+ it 'should not break an existing file if save fails to read' do
230
+ initial_size = File.read(temp_cache_path).size
231
+ stub(subject).read_from_url { '' }
212
232
  subject.save_rates
213
- File.open(@temp_cache_path).read.size.must_equal initial_size
233
+ File.open(temp_cache_path).read.size.must_equal initial_size
214
234
  end
215
235
 
216
- it "should not break an existing file if save returns json without rates" do
217
- initial_size = File.read(@temp_cache_path).size
218
- stub(subject).read_from_url { %Q({"error": "An error"}) }
236
+ it 'should not break an existing file if save returns json without rates' do
237
+ initial_size = File.read(temp_cache_path).size
238
+ stub(subject).read_from_url { '{"error": "An error"}' }
219
239
  subject.save_rates
220
- File.open(@temp_cache_path).read.size.must_equal initial_size
240
+ File.open(temp_cache_path).read.size.must_equal initial_size
221
241
  end
222
242
 
223
- it "should not break an existing file if save returns a invalid json" do
224
- initial_size = File.read(@temp_cache_path).size
225
- stub(subject).read_from_url { %Q({invalid_json: "An error"}) }
243
+ it 'should not break an existing file if save returns a invalid json' do
244
+ initial_size = File.read(temp_cache_path).size
245
+ stub(subject).read_from_url { '{invalid_json: "An error"}' }
226
246
  subject.save_rates
227
- File.open(@temp_cache_path).read.size.must_equal initial_size
247
+ File.open(temp_cache_path).read.size.must_equal initial_size
228
248
  end
229
249
 
230
250
  after do
231
- File.delete @temp_cache_path
251
+ File.delete temp_cache_path
232
252
  end
233
253
  end
234
254
 
@@ -238,9 +258,10 @@ describe Money::Bank::OpenExchangeRatesBank do
238
258
  subject.ttl_in_seconds = 1000
239
259
  @usd_eur_rate = 0.655
240
260
  subject.add_rate('USD', 'EUR', @usd_eur_rate)
241
- @temp_cache_path = File.expand_path(File.join(File.dirname(__FILE__), 'tmp.json'))
242
- subject.cache = @temp_cache_path
243
- stub(OpenURI::OpenRead).open(Money::Bank::OpenExchangeRatesBank::OER_URL) { File.read @cache_path }
261
+ subject.cache = temp_cache_path
262
+ stub(OpenURI::OpenRead).open(url) do
263
+ File.read cache_path
264
+ end
244
265
  end
245
266
 
246
267
  describe 'when the ttl has expired' do
data/test/test_helper.rb CHANGED
@@ -10,5 +10,6 @@ TEST_APP_ID_PATH = File.join(File.dirname(__FILE__), '..', 'TEST_APP_ID')
10
10
  TEST_APP_ID = ENV['TEST_APP_ID'] || File.read(TEST_APP_ID_PATH)
11
11
 
12
12
  if TEST_APP_ID.nil? || TEST_APP_ID.empty?
13
- raise "Please add a valid app id to file #{TEST_APP_ID_PATH} or to TEST_APP_ID environment"
13
+ fail "Please add a valid app id to file #{TEST_APP_ID_PATH} or to " \
14
+ ' TEST_APP_ID environment'
14
15
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: money-open-exchange-rates
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laurent Arnoud
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-21 00:00:00.000000000 Z
11
+ date: 2015-06-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: money
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1.1'
33
+ version: '1.3'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '1.1'
40
+ version: '1.3'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: json
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +122,20 @@ dependencies:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rubocop
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
125
139
  description: A gem that calculates the exchange rate using published rates from open-exchange-rates.
126
140
  Compatible with the money gem.
127
141
  email: laurent@spkdev.net
@@ -149,7 +163,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
149
163
  requirements:
150
164
  - - ">="
151
165
  - !ruby/object:Gem::Version
152
- version: 1.9.2
166
+ version: 1.9.3
153
167
  required_rubygems_version: !ruby/object:Gem::Requirement
154
168
  requirements:
155
169
  - - ">="