money-open-exchange-rates 1.4.2 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 86f324ed978cde7c5b05b50a8fe3d1ce4740333874457c8776c9871b3fa9ee31
4
- data.tar.gz: ef5d0c06086d520f7a8816a69007b98d4741fb0063148ee0c66b9ec29a658b6d
3
+ metadata.gz: 3b5f63af5ef2482e223249c50e9972e84c4f9aaa3bcec4b3052089ee3b60a237
4
+ data.tar.gz: aa6a135ef6dea76c5ca26739dcdabfe5ba2eac6bf83eba3c8b060f54c5357dc6
5
5
  SHA512:
6
- metadata.gz: 5361f382ab1462ae3c6b1a18e543c1dbe4d8d31f68278c460092d8fd93b65a9fd9070f290915a498b1b9bb10b2c6c6c6fb3bff356a90f0f0b75b12d296914990
7
- data.tar.gz: d85876e62c4f13ebe24c71a273cb6667e7a331707cd7f48e90393b1d620b51593ff03cc77bc24cac78075fd21983290d99cba5eb62bfdb3c2235d1b790669f8d
6
+ metadata.gz: a78deced5445998e4fd6dd9cfa442753dca96bdbeabae0fbf0611e8c1274542030de8ed5be8f7b20a670693095370cb4a72e9a205ac556c1ce075f987ed128e5
7
+ data.tar.gz: b865cbfd1b40ab43f186aa99e1af7b01314eb90a46cecd586634b2b9e4a83614c99f555bf2aa3c7dd8419aa4faea85429026814f6c5299e657fba015c9d728a8
data/History.md CHANGED
@@ -1,4 +1,20 @@
1
1
 
2
+ 2.0.1 / 2026-01-21
3
+ ==================
4
+
5
+ * Merge pull request #75 from @ps-97 / adarsh/preserve-rates-on-partial-api-response
6
+ * fix: preserve rates for currencies missing from API response
7
+
8
+ 2.0.0 / 2025-12-23
9
+ ==================
10
+
11
+ * BREAKING: Upgrade to money ~> 7.0
12
+ * BREAKING: Require Ruby >= 3.1
13
+ * Update monetize dependency to ~> 2.0
14
+ * Update CI to test Ruby 3.1, 3.2, 3.3, 3.4
15
+ * Remove support for Ruby 2.7, 3.0
16
+ * Update README with money 7.x default currency configuration
17
+
2
18
  1.4.2 / 2023-07-25
3
19
  ==================
4
20
 
data/README.md CHANGED
@@ -45,6 +45,9 @@ gem install money-open-exchange-rates
45
45
  ``` ruby
46
46
  require 'money/bank/open_exchange_rates_bank'
47
47
 
48
+ # Money 7.x requires explicit default currency configuration
49
+ Money.default_currency = Money::Currency.new('USD')
50
+
48
51
  # Memory store per default; for others just pass as argument a class like
49
52
  # explained in https://github.com/RubyMoney/money#exchange-rate-stores
50
53
  oxr = Money::Bank::OpenExchangeRatesBank.new(Money::RatesStore::Memory.new)
@@ -176,6 +179,9 @@ take some time (HTTP call) and can fail.
176
179
  ``` ruby
177
180
  require 'money/bank/open_exchange_rates_bank'
178
181
 
182
+ # Money 7.x requires explicit default currency configuration
183
+ Money.default_currency = Money::Currency.new('USD')
184
+
179
185
  OXR_CACHE_KEY = "#{Rails.env}:money:exchange_rates".freeze
180
186
  # ExchangeRate is an ActiveRecord model
181
187
  # more info at https://github.com/RubyMoney/money#exchange-rate-stores
@@ -187,15 +187,14 @@ class Money
187
187
  # @return [Array] Array of exchange rates
188
188
  def update_rates
189
189
  store.transaction do
190
- clear_rates!
191
- exchange_rates.each do |exchange_rate|
192
- rate = exchange_rate.last
193
- currency = exchange_rate.first
190
+ new_rates = exchange_rates
191
+ new_rates.each do |currency, rate|
194
192
  next unless Money::Currency.find(currency)
195
193
 
196
194
  set_rate(source, currency, rate)
197
195
  set_rate(currency, source, 1.0 / rate)
198
196
  end
197
+ clear_cross_rates_for(new_rates)
199
198
  end
200
199
  end
201
200
 
@@ -270,7 +269,7 @@ class Money
270
269
  str = "#{str}&base=#{source}" unless source == OE_SOURCE
271
270
  str = "#{str}&show_alternative=#{show_alternative}"
272
271
  str = "#{str}&prettyprint=#{prettyprint}"
273
- str = "#{str}&symbols=#{symbols.join(',')}" if symbols&.is_a?(Array)
272
+ str = "#{str}&symbols=#{symbols.join(',')}" if symbols.is_a?(Array)
274
273
  str
275
274
  end
276
275
 
@@ -324,9 +323,7 @@ class Money
324
323
  if cache.is_a?(Proc)
325
324
  cache.call(text)
326
325
  elsif cache.is_a?(String) || cache.is_a?(Pathname)
327
- File.open(cache.to_s, 'w') do |f|
328
- f.write(text)
329
- end
326
+ File.write(cache.to_s, text)
330
327
  else
331
328
  raise InvalidCache
332
329
  end
@@ -373,7 +370,7 @@ class Money
373
370
  return false unless text
374
371
 
375
372
  parsed = JSON.parse(text)
376
- parsed&.key?(RATES_KEY) && parsed&.key?(TIMESTAMP_KEY)
373
+ parsed&.key?(RATES_KEY) && parsed.key?(TIMESTAMP_KEY)
377
374
  rescue JSON::ParserError
378
375
  false
379
376
  end
@@ -383,9 +380,7 @@ class Money
383
380
  # @return [Hash] key is country code (ISO 3166-1 alpha-3) value Float
384
381
  def exchange_rates
385
382
  doc = JSON.parse(read_from_cache || read_from_url)
386
- if doc['error'] && ERROR_MAP.key?(doc['message'].to_sym)
387
- raise ERROR_MAP[doc['message'].to_sym]
388
- end
383
+ raise ERROR_MAP[doc['message'].to_sym] if doc['error'] && ERROR_MAP.key?(doc['message'].to_sym)
389
384
 
390
385
  self.rates_timestamp = doc[TIMESTAMP_KEY]
391
386
  @oer_rates = doc[RATES_KEY]
@@ -442,6 +437,20 @@ class Money
442
437
  add_rate(iso_from, iso_to, nil)
443
438
  end
444
439
  end
440
+
441
+ # Clears cross-rates for currencies present in new_rates
442
+ # Cross-rates are calculated on next access via calc_pair_rate_using_base
443
+ # Direct rates (source -> X) are not cleared, just overwritten
444
+ #
445
+ # @param new_rates [Hash] Rates hash from API response
446
+ def clear_cross_rates_for(new_rates)
447
+ store.each_rate do |iso_from, iso_to|
448
+ next if iso_from == source || iso_to == source
449
+ next unless new_rates.key?(iso_from) || new_rates.key?(iso_to)
450
+
451
+ add_rate(iso_from, iso_to, nil)
452
+ end
453
+ end
445
454
  end
446
455
  end
447
456
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  # Module for version constant
4
4
  module OpenExchangeRatesBank
5
- VERSION = '1.4.2'
5
+ VERSION = '2.0.1'
6
6
  end
@@ -0,0 +1,16 @@
1
+ {
2
+ "disclaimer": "Exchange rates are provided for informational purposes only.",
3
+ "license": "Data sourced from various providers.",
4
+ "timestamp": 1414008100,
5
+ "base": "USD",
6
+ "rates": {
7
+ "AED": 3.67304,
8
+ "AUD": 1.139103,
9
+ "BBD": 2,
10
+ "CAD": 1.124161,
11
+ "CHF": 0.951922,
12
+ "GBP": 0.62292,
13
+ "JPY": 107.0718,
14
+ "USD": 1
15
+ }
16
+ }
@@ -2,5 +2,5 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- gem 'money', '~> 6.7'
5
+ gem 'money', '~> 7.0'
6
6
  gem 'money-open-exchange-rates', path: '../../'
@@ -3,10 +3,13 @@
3
3
  require 'json'
4
4
  require 'money/bank/open_exchange_rates_bank'
5
5
 
6
+ # Money 7.x requires explicit default currency configuration
7
+ Money.default_currency = Money::Currency.new('USD')
8
+
6
9
  ERROR_MSG = 'Integration test failed!'
7
10
  cache_path = '/tmp/latest.json'
8
11
  to_currency = 'CAD'
9
- app_id = ENV['OXR_APP_ID']
12
+ app_id = ENV.fetch('OXR_APP_ID', nil)
10
13
 
11
14
  if app_id.nil? || app_id.empty?
12
15
  puts 'OXR_APP_ID env var not set skipping integration tests'
@@ -29,10 +32,8 @@ begin
29
32
  json_to_currency = JSON.parse(File.read(cache_path))['rates'][to_currency]
30
33
  puts 'JSON to_currency', json_to_currency
31
34
  puts 'Money to_currency', cad_rate
32
- # rubocop:disable Style/AndOr
33
35
  json_to_currency == cad_rate or raise ERROR_MSG
34
- # rubocop:enable Style/AndOr
35
- # rubocop:disable Style/RescueStandardError
36
+ # rubocop:disable Style/RescueStandardError
36
37
  rescue
37
38
  # rubocop:enable Style/RescueStandardError
38
39
  raise ERROR_MSG
@@ -117,7 +117,7 @@ describe Money::Bank::OpenExchangeRatesBank do
117
117
  end
118
118
 
119
119
  it 'should update itself with exchange rates from OpenExchangeRates' do
120
- subject.oer_rates.keys.each do |currency|
120
+ subject.oer_rates.each_key do |currency|
121
121
  next unless Money::Currency.find(currency)
122
122
 
123
123
  _(subject.get_rate('USD', currency)).must_be :>, 0
@@ -316,21 +316,21 @@ describe Money::Bank::OpenExchangeRatesBank do
316
316
  initial_size = File.read(temp_cache_path).size
317
317
  subject.stubs(:api_response).returns ''
318
318
  subject.refresh_rates
319
- _(File.open(temp_cache_path).read.size).must_equal initial_size
319
+ _(File.read(temp_cache_path).size).must_equal initial_size
320
320
  end
321
321
 
322
322
  it 'should not break an existing file if save returns json without rates' do
323
323
  initial_size = File.read(temp_cache_path).size
324
324
  subject.stubs(:api_response).returns '{"error": "An error"}'
325
325
  subject.refresh_rates
326
- _(File.open(temp_cache_path).read.size).must_equal initial_size
326
+ _(File.read(temp_cache_path).size).must_equal initial_size
327
327
  end
328
328
 
329
329
  it 'should not break an existing file if save returns a invalid json' do
330
330
  initial_size = File.read(temp_cache_path).size
331
331
  subject.stubs(:api_response).returns '{invalid_json: "An error"}'
332
332
  subject.refresh_rates
333
- _(File.open(temp_cache_path).read.size).must_equal initial_size
333
+ _(File.read(temp_cache_path).size).must_equal initial_size
334
334
  end
335
335
  end
336
336
 
@@ -533,5 +533,48 @@ describe Money::Bank::OpenExchangeRatesBank do
533
533
  end
534
534
  end
535
535
  end
536
+
537
+ describe 'missing currencies' do
538
+ let(:oer_partial_path) do
539
+ data_file('partial_latest.json')
540
+ end
541
+
542
+ before do
543
+ add_to_webmock(subject)
544
+ # see test/data/latest.json +52
545
+ @latest_usd_eur_rate = 0.79085
546
+ # see test/data/latest.json +33
547
+ @latest_usd_cad_rate = 1.124161
548
+ subject.update_rates
549
+ end
550
+
551
+ it 'should preserve direct rates' do
552
+ _(subject.get_rate('USD', 'EUR')).must_equal @latest_usd_eur_rate
553
+ subject.cache = nil
554
+ stub_request(:get, subject.source_url)
555
+ .to_return(status: 200, body: File.read(oer_partial_path))
556
+ subject.update_rates
557
+ _(subject.get_rate('USD', 'EUR')).must_equal @latest_usd_eur_rate
558
+ end
559
+
560
+ it 'should update rates for currencies in response' do
561
+ _(subject.get_rate('USD', 'CAD')).must_equal @latest_usd_cad_rate
562
+ subject.cache = nil
563
+ stub_request(:get, subject.source_url)
564
+ .to_return(status: 200, body: File.read(oer_partial_path))
565
+ subject.update_rates
566
+ _(subject.get_rate('USD', 'CAD')).must_equal @latest_usd_cad_rate
567
+ end
568
+
569
+ it 'should exchange with preserved rates' do
570
+ money = Money.new(100, 'USD')
571
+ _(subject.exchange_with(money, 'EUR')).must_equal Money.new(79, 'EUR')
572
+ subject.cache = nil
573
+ stub_request(:get, subject.source_url)
574
+ .to_return(status: 200, body: File.read(oer_partial_path))
575
+ subject.update_rates
576
+ _(subject.exchange_with(money, 'EUR')).must_equal Money.new(79, 'EUR')
577
+ end
578
+ end
536
579
  end
537
580
  # rubocop:enable Metrics/BlockLength
data/test/test_helper.rb CHANGED
@@ -7,6 +7,7 @@ rescue LoadError
7
7
  warn 'simplecov not loaded'
8
8
  end
9
9
 
10
+ require 'minitest/unit'
10
11
  require 'minitest/autorun'
11
12
  require 'mocha/minitest'
12
13
  require 'webmock/minitest'
@@ -14,6 +15,9 @@ require 'money/bank/open_exchange_rates_bank'
14
15
  require 'monetize'
15
16
  require 'timecop'
16
17
 
18
+ # Money 7.x requires explicit default currency configuration
19
+ Money.default_currency = Money::Currency.new('USD')
20
+
17
21
  TEST_APP_ID = 'TEST_APP_ID'
18
22
 
19
23
  def data_file(file)
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: money-open-exchange-rates
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.2
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Laurent Arnoud
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-07-25 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: money
@@ -16,14 +15,28 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '6.12'
18
+ version: '7.0'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: '6.12'
25
+ version: '7.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: ostruct
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.6'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.6'
27
40
  - !ruby/object:Gem::Dependency
28
41
  name: minitest
29
42
  requirement: !ruby/object:Gem::Requirement
@@ -56,22 +69,16 @@ dependencies:
56
69
  name: monetize
57
70
  requirement: !ruby/object:Gem::Requirement
58
71
  requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: 1.3.1
62
- - - "<"
72
+ - - "~>"
63
73
  - !ruby/object:Gem::Version
64
- version: '2'
74
+ version: '2.0'
65
75
  type: :development
66
76
  prerelease: false
67
77
  version_requirements: !ruby/object:Gem::Requirement
68
78
  requirements:
69
- - - ">="
70
- - !ruby/object:Gem::Version
71
- version: 1.3.1
72
- - - "<"
79
+ - - "~>"
73
80
  - !ruby/object:Gem::Version
74
- version: '2'
81
+ version: '2.0'
75
82
  - !ruby/object:Gem::Dependency
76
83
  name: rake
77
84
  requirement: !ruby/object:Gem::Requirement
@@ -92,14 +99,14 @@ dependencies:
92
99
  requirements:
93
100
  - - "~>"
94
101
  - !ruby/object:Gem::Version
95
- version: 0.76.0
102
+ version: '1.69'
96
103
  type: :development
97
104
  prerelease: false
98
105
  version_requirements: !ruby/object:Gem::Requirement
99
106
  requirements:
100
107
  - - "~>"
101
108
  - !ruby/object:Gem::Version
102
- version: 0.76.0
109
+ version: '1.69'
103
110
  - !ruby/object:Gem::Dependency
104
111
  name: timecop
105
112
  requirement: !ruby/object:Gem::Requirement
@@ -146,6 +153,7 @@ files:
146
153
  - test/data/access_restricted_error.json
147
154
  - test/data/app_id_inactive.json
148
155
  - test/data/latest.json
156
+ - test/data/partial_latest.json
149
157
  - test/integration/Gemfile
150
158
  - test/integration/Gemfile.lock
151
159
  - test/integration/api.rb
@@ -154,8 +162,8 @@ files:
154
162
  homepage: http://github.com/spk/money-open-exchange-rates
155
163
  licenses:
156
164
  - MIT
157
- metadata: {}
158
- post_install_message:
165
+ metadata:
166
+ rubygems_mfa_required: 'true'
159
167
  rdoc_options: []
160
168
  require_paths:
161
169
  - lib
@@ -163,16 +171,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
163
171
  requirements:
164
172
  - - ">="
165
173
  - !ruby/object:Gem::Version
166
- version: '2.6'
174
+ version: '3.1'
167
175
  required_rubygems_version: !ruby/object:Gem::Requirement
168
176
  requirements:
169
177
  - - ">="
170
178
  - !ruby/object:Gem::Version
171
179
  version: '0'
172
180
  requirements: []
173
- rubygems_version: 3.3.15
174
- signing_key:
181
+ rubygems_version: 3.6.7
175
182
  specification_version: 4
176
183
  summary: A gem that calculates the exchange rate using published rates from open-exchange-rates.
177
- test_files:
178
- - test/open_exchange_rates_bank_test.rb
184
+ test_files: []