mumboe-currency 0.5

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.
Files changed (37) hide show
  1. data/bin/currency_historical_rate_load +105 -0
  2. data/examples/ex1.rb +13 -0
  3. data/examples/xe1.rb +20 -0
  4. data/lib/currency/active_record.rb +265 -0
  5. data/lib/currency/config.rb +91 -0
  6. data/lib/currency/core_extensions.rb +41 -0
  7. data/lib/currency/currency/factory.rb +228 -0
  8. data/lib/currency/currency.rb +175 -0
  9. data/lib/currency/currency_version.rb +6 -0
  10. data/lib/currency/exception.rb +119 -0
  11. data/lib/currency/exchange/rate/deriver.rb +157 -0
  12. data/lib/currency/exchange/rate/source/base.rb +166 -0
  13. data/lib/currency/exchange/rate/source/failover.rb +63 -0
  14. data/lib/currency/exchange/rate/source/federal_reserve.rb +160 -0
  15. data/lib/currency/exchange/rate/source/historical/rate.rb +184 -0
  16. data/lib/currency/exchange/rate/source/historical/rate_loader.rb +186 -0
  17. data/lib/currency/exchange/rate/source/historical/writer.rb +220 -0
  18. data/lib/currency/exchange/rate/source/historical.rb +79 -0
  19. data/lib/currency/exchange/rate/source/new_york_fed.rb +127 -0
  20. data/lib/currency/exchange/rate/source/provider.rb +120 -0
  21. data/lib/currency/exchange/rate/source/test.rb +50 -0
  22. data/lib/currency/exchange/rate/source/the_financials.rb +191 -0
  23. data/lib/currency/exchange/rate/source/timed_cache.rb +198 -0
  24. data/lib/currency/exchange/rate/source/xe.rb +165 -0
  25. data/lib/currency/exchange/rate/source.rb +89 -0
  26. data/lib/currency/exchange/rate.rb +214 -0
  27. data/lib/currency/exchange/time_quantitizer.rb +111 -0
  28. data/lib/currency/exchange.rb +50 -0
  29. data/lib/currency/formatter.rb +290 -0
  30. data/lib/currency/macro.rb +321 -0
  31. data/lib/currency/money.rb +295 -0
  32. data/lib/currency/money_helper.rb +13 -0
  33. data/lib/currency/parser.rb +151 -0
  34. data/lib/currency.rb +143 -0
  35. data/test/string_test.rb +54 -0
  36. data/test/test_base.rb +44 -0
  37. metadata +90 -0
@@ -0,0 +1,120 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+ require 'currency/exchange/rate/source'
5
+
6
+
7
+ # Base class for rate data providers.
8
+ # Assumes that rate sources provide more than one rate per query.
9
+ class Currency::Exchange::Rate::Source::Provider < Currency::Exchange::Rate::Source::Base
10
+
11
+ # Error during parsing of rates.
12
+ class ParserError < ::Currency::Exception::RateSourceError; end
13
+
14
+ # The URI used to access the rate source.
15
+ attr_accessor :uri
16
+
17
+ # The URI path relative to uri used to access the rate source.
18
+ attr_accessor :uri_path
19
+
20
+ # The Time used to query the rate source.
21
+ # Typically set by #load_rates.
22
+ attr_accessor :date
23
+
24
+ # The name is the same as its #uri.
25
+ alias :name :uri
26
+
27
+ def initialize(*args)
28
+ super
29
+ @rates = { }
30
+ end
31
+
32
+ # Returns the date to query for rates.
33
+ # Defaults to yesterday.
34
+ def date
35
+ @date || (Time.now - 24 * 60 * 60) # yesterday.
36
+ end
37
+
38
+
39
+ # Returns year of query date.
40
+ def date_YYYY
41
+ '%04d' % date.year
42
+ end
43
+
44
+
45
+ # Return month of query date.
46
+ def date_MM
47
+ '%02d' % date.month
48
+ end
49
+
50
+
51
+ # Returns day of query date.
52
+ def date_DD
53
+ '%02d' % date.day
54
+ end
55
+
56
+
57
+ # Returns the URI string as evaluated with this object.
58
+ def get_uri
59
+ uri = self.uri
60
+ uri = "\"#{uri}\""
61
+ uri = instance_eval(uri)
62
+ $stderr.puts "#{self}: uri = #{uri.inspect}" if @verbose
63
+ uri
64
+ end
65
+
66
+
67
+ # Returns the URI content.
68
+ def get_page_content
69
+ data = open(get_uri) { |data| data.read }
70
+
71
+ data
72
+ end
73
+
74
+
75
+ # Clear cached rates from this source.
76
+ def clear_rates
77
+ @rates.clear
78
+ super
79
+ end
80
+
81
+
82
+ # Returns current base Rates or calls load_rates to load them from the source.
83
+ def rates(time = nil)
84
+ time = time && normalize_time(time)
85
+ @rates["#{time}"] ||= load_rates(time)
86
+ end
87
+
88
+
89
+ # Returns an array of base Rates from the rate source.
90
+ #
91
+ # Subclasses must define this method.
92
+ def load_rates(time = nil)
93
+ raise Currency::Exception::SubclassResponsibility, :load_rates
94
+ end
95
+
96
+
97
+ # Return a matching base rate.
98
+ def get_rate(c1, c2, time)
99
+ rates.each do | rate |
100
+ return rate if
101
+ rate.c1 == c1 &&
102
+ rate.c2 == c2 &&
103
+ (! time || normalize_time(rate.date) == time)
104
+ end
105
+
106
+ nil
107
+ end
108
+
109
+ alias :get_rate_base :get_rate
110
+
111
+
112
+ # Returns true if a rate provider is available.
113
+ def available?(time = nil)
114
+ true
115
+ end
116
+
117
+ end # class
118
+
119
+
120
+
@@ -0,0 +1,50 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+ require 'currency/exchange/rate/source/provider'
5
+
6
+ # This class is a test Rate Source.
7
+ # It can provide only fixed rates between USD, CAD and EUR.
8
+ # Used only for test purposes.
9
+ # DO NOT USE THESE RATES FOR A REAL APPLICATION.
10
+ class Currency::Exchange::Rate::Source::Test < Currency::Exchange::Rate::Source::Provider
11
+ @@instance = nil
12
+
13
+ # Returns a singleton instance.
14
+ def self.instance(*opts)
15
+ @@instance ||= self.new(*opts)
16
+ end
17
+
18
+
19
+ def initialize(*opts)
20
+ self.uri = 'none://localhost/Test'
21
+ super(*opts)
22
+ end
23
+
24
+
25
+ def name
26
+ 'Test'
27
+ end
28
+
29
+ # Test rate from :USD to :CAD.
30
+ def self.USD_CAD; 1.1708; end
31
+
32
+
33
+ # Test rate from :USD to :EUR.
34
+ def self.USD_EUR; 0.7737; end
35
+
36
+
37
+ # Test rate from :USD to :GBP.
38
+ def self.USD_GBP; 0.5098; end
39
+
40
+
41
+ # Returns test Rate for USD to [ CAD, EUR, GBP ].
42
+ def rates
43
+ [ new_rate(:USD, :CAD, self.class.USD_CAD),
44
+ new_rate(:USD, :EUR, self.class.USD_EUR),
45
+ new_rate(:USD, :GBP, self.class.USD_GBP) ]
46
+ end
47
+
48
+ end # class
49
+
50
+
@@ -0,0 +1,191 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+ require 'currency/exchange/rate/source/base'
5
+
6
+ require 'net/http'
7
+ require 'open-uri'
8
+ require 'rexml/document'
9
+
10
+
11
+ # Connects to http://www.thefinancials.com and parses XML.
12
+ #
13
+ # This is for demonstration purposes.
14
+ #
15
+ class Currency::Exchange::Rate::Source::TheFinancials < ::Currency::Exchange::Rate::Source::Provider
16
+ # Defines the pivot currency for http://thefinancials.com/.
17
+ PIVOT_CURRENCY = :USD
18
+
19
+ def initialize(*opt)
20
+ @raw_rates = nil
21
+ self.uri_path = 'syndicated/UNKNOWN/fxrates.xml'
22
+ super(*opt)
23
+ self.uri = "http://www.thefinancials.com/#{self.uri_path}"
24
+ end
25
+
26
+
27
+ # Returns 'thefinancials.com'.
28
+ def name
29
+ 'thefinancials.org'
30
+ end
31
+
32
+
33
+ # def get_page_content
34
+ # test_content
35
+ # end
36
+
37
+
38
+ def clear_rates
39
+ @raw_rates = nil
40
+ super
41
+ end
42
+
43
+
44
+ def raw_rates
45
+ rates
46
+ @raw_rates
47
+ end
48
+
49
+
50
+ # Parses XML for rates.
51
+ def parse_rates(data = nil)
52
+ data = get_page_content unless data
53
+
54
+ rates = [ ]
55
+
56
+ @raw_rates = { }
57
+
58
+ # $stderr.puts "parse_rates: data = #{data}"
59
+
60
+ doc = REXML::Document.new(data).root
61
+ doc.elements.to_a('//record').each do | record |
62
+ c1_c2 = record.elements.to_a('symbol')[0].text
63
+ md = /([A-Z][A-Z][A-Z]).*?([A-Z][A-Z][A-Z])/.match(c1_c2)
64
+ c1, c2 = md[1], md[2]
65
+
66
+ c1 = c1.upcase.intern
67
+ c2 = c2.upcase.intern
68
+
69
+ rate = record.elements.to_a('last')[0].text.to_f
70
+
71
+ date = record.elements.to_a('date')[0].text
72
+ date = Time.parse("#{date} 12:00:00 -05:00") # USA NY => EST
73
+
74
+ rates << new_rate(c1, c2, rate, date)
75
+
76
+ (@raw_rates[c1] ||= { })[c2] ||= rate
77
+ end
78
+
79
+ rates
80
+ end
81
+
82
+
83
+ # Return a list of known base rates.
84
+ def load_rates(time = nil)
85
+ self.date = time
86
+ parse_rates
87
+ end
88
+
89
+
90
+ def test_content
91
+ <<EOF
92
+ <?xml version="1.0" ?>
93
+ <TFCRecords>
94
+ <record>
95
+ <symbol>USD/EUR</symbol>
96
+ <date>10/25/2001</date>
97
+ <last>1.115822</last>
98
+ </record>
99
+ <record>
100
+ <symbol>USD/AUD</symbol>
101
+ <date>10/25/2001</date>
102
+ <last>1.975114</last>
103
+ </record>
104
+ <record>
105
+ <symbol>USD/CAD</symbol>
106
+ <date>10/25/2001</date>
107
+ <last>1.57775</last>
108
+ </record>
109
+ <record>
110
+ <symbol>USD/CNY</symbol>
111
+ <date>10/25/2001</date>
112
+ <last>8.2769</last>
113
+ </record>
114
+ <record>
115
+ <symbol>USD/ESP</symbol>
116
+ <date>10/25/2001</date>
117
+ <last>185.65725</last>
118
+ </record>
119
+ <record>
120
+ <symbol>USD/GBP</symbol>
121
+ <date>10/25/2001</date>
122
+ <last>0.698849867830019</last>
123
+ </record>
124
+ <record>
125
+ <symbol>USD/HKD</symbol>
126
+ <date>10/25/2001</date>
127
+ <last>7.7999</last>
128
+ </record>
129
+ <record>
130
+ <symbol>USD/IDR</symbol>
131
+ <date>10/25/2001</date>
132
+ <last>10265</last>
133
+ </record>
134
+ <record>
135
+ <symbol>USD/INR</symbol>
136
+ <date>10/25/2001</date>
137
+ <last>48.01</last>
138
+ </record>
139
+ <record>
140
+ <symbol>USD/JPY</symbol>
141
+ <date>10/25/2001</date>
142
+ <last>122.68</last>
143
+ </record>
144
+ <record>
145
+ <symbol>USD/KRW</symbol>
146
+ <date>10/25/2001</date>
147
+ <last>1293.5</last>
148
+ </record>
149
+ <record>
150
+ <symbol>USD/MYR</symbol>
151
+ <date>10/25/2001</date>
152
+ <last>3.8</last>
153
+ </record>
154
+ <record>
155
+ <symbol>USD/NZD</symbol>
156
+ <date>10/25/2001</date>
157
+ <last>2.41485</last>
158
+ </record>
159
+ <record>
160
+ <symbol>USD/PHP</symbol>
161
+ <date>10/25/2001</date>
162
+ <last>52.05</last>
163
+ </record>
164
+ <record>
165
+ <symbol>USD/PKR</symbol>
166
+ <date>10/25/2001</date>
167
+ <last>61.6</last>
168
+ </record>
169
+ <record>
170
+ <symbol>USD/SGD</symbol>
171
+ <date>10/25/2001</date>
172
+ <last>1.82615</last>
173
+ </record>
174
+ <record>
175
+ <symbol>USD/THB</symbol>
176
+ <date>10/25/2001</date>
177
+ <last>44.88</last>
178
+ </record>
179
+ <record>
180
+ <symbol>USD/TWD</symbol>
181
+ <date>10/25/2001</date>
182
+ <last>34.54</last>
183
+ </record>
184
+ </TFCRecords>
185
+ EOF
186
+ end
187
+
188
+ end # class
189
+
190
+
191
+
@@ -0,0 +1,198 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+ require 'currency/exchange/rate/source/base'
5
+
6
+ # A timed cache for rate sources.
7
+ #
8
+ # This class should be used at the top-level of a rate source change,
9
+ # to correctly check rate dates.
10
+ #
11
+ class Currency::Exchange::Rate::Source::TimedCache < ::Currency::Exchange::Rate::Source::Base
12
+ # The rate source.
13
+ attr_accessor :source
14
+
15
+ # Defines the number of seconds rates until rates
16
+ # become invalid, causing a request of new rates.
17
+ #
18
+ # Defaults to 600 seconds.
19
+ attr_accessor :time_to_live
20
+
21
+
22
+ # Defines the number of random seconds to add before
23
+ # rates become invalid.
24
+ #
25
+ # Defaults to 30 seconds.
26
+ attr_accessor :time_to_live_fudge
27
+
28
+
29
+ # Returns the time of the last load.
30
+ attr_reader :rate_load_time
31
+
32
+
33
+ # Returns the time of the next load.
34
+ attr_reader :rate_reload_time
35
+
36
+
37
+ # Returns source's name.
38
+ def name
39
+ source.name
40
+ end
41
+
42
+
43
+ def initialize(*opt)
44
+ self.time_to_live = 600
45
+ self.time_to_live_fudge = 30
46
+ @rate_load_time = nil
47
+ @rate_reload_time = nil
48
+ @processing_rates = false
49
+ @cached_rates = { }
50
+ @cached_rates_old = nil
51
+ super(*opt)
52
+ end
53
+
54
+
55
+ # Clears current rates.
56
+ def clear_rates
57
+ @cached_rates = { }
58
+ @source.clear_rates
59
+ super
60
+ end
61
+
62
+
63
+ # Returns true if the cache of Rates
64
+ # is expired.
65
+ def expired?
66
+ if @time_to_live &&
67
+ @rate_reload_time &&
68
+ (Time.now > @rate_reload_time)
69
+
70
+ if @cached_rates
71
+ $stderr.puts "#{self}: rates expired on #{@rate_reload_time}" if @verbose
72
+
73
+ @cached_rates_old = @cached_rates
74
+ end
75
+
76
+ clear_rates
77
+
78
+ true
79
+ else
80
+ false
81
+ end
82
+ end
83
+
84
+
85
+ # Check expired? before returning a Rate.
86
+ def rate(c1, c2, time)
87
+ if expired?
88
+ clear_rates
89
+ end
90
+ super(c1, c2, time)
91
+ end
92
+
93
+
94
+ def get_rate(c1, c2, time)
95
+ # STDERR.puts "get_rate #{c1} #{c2} #{time}"
96
+ rates = load_rates(time)
97
+ # STDERR.puts "rates = #{rates.inspect}"
98
+ rate = rates && (rates.select{|x| x.c1 == c1 && x.c2 == c2}[0])
99
+ # STDERR.puts "rate = #{rate.inspect}"
100
+ rate
101
+ end
102
+
103
+
104
+ # Returns an array of all the cached Rates.
105
+ def rates(time = nil)
106
+ load_rates(time)
107
+ end
108
+
109
+
110
+ # Returns an array of all the cached Rates.
111
+ def load_rates(time = nil)
112
+ # Check expiration.
113
+ expired?
114
+
115
+ # Return rates, if cached.
116
+ return rates if rates = @cached_rates["#{time}"]
117
+
118
+ # Force load of rates.
119
+ rates = @cached_rates["#{time}"] = _load_rates_from_source(time)
120
+
121
+ # Update expiration.
122
+ _calc_rate_reload_time
123
+
124
+ return nil unless rates
125
+
126
+ # Flush old rates.
127
+ @cached_rates_old = nil
128
+
129
+ rates
130
+ end
131
+
132
+
133
+ def time_to_live=(x)
134
+ @time_to_live = x
135
+ _calc_rate_reload_time
136
+ x
137
+ end
138
+
139
+
140
+ def time_to_live_fudge=(x)
141
+ @time_to_live_fudge = x
142
+ _calc_rate_reload_time
143
+ x
144
+ end
145
+
146
+
147
+ def _calc_rate_reload_time
148
+ if @time_to_live && @rate_load_time
149
+ @rate_reload_time = @rate_load_time + (@time_to_live + (@time_to_live_fudge || 0))
150
+ $stderr.puts "#{self}: rates expire on #{@rate_reload_time}" if @verbose
151
+ end
152
+
153
+ end
154
+
155
+
156
+
157
+ def _load_rates_from_source(time = nil) # :nodoc:
158
+ rates = nil
159
+
160
+ begin
161
+ # Do not allow re-entrancy
162
+ raise Currency::Exception::InvalidReentrancy, "Reentry!" if @processing_rates
163
+
164
+ # Begin processing new rate request.
165
+ @processing_rates = true
166
+
167
+ # Clear cached Rates.
168
+ clear_rates
169
+
170
+ # Load rates from the source.
171
+ rates = source.load_rates(time)
172
+
173
+ # Compute new rate timestamp.
174
+ @rate_load_time = Time.now
175
+
176
+ # STDERR.puts "rate_load_time = #{@rate_load_time}"
177
+ ensure
178
+ # End processsing new rate request.
179
+ @processing_rates = false
180
+
181
+ end
182
+
183
+ # STDERR.puts "_load_rates => #{rates.inspect}"
184
+
185
+ rates
186
+ end
187
+
188
+
189
+ # Returns true if the underlying rate provider is available.
190
+ def available?(time = nil)
191
+ source.available?(time)
192
+ end
193
+
194
+
195
+ end # class
196
+
197
+
198
+
@@ -0,0 +1,165 @@
1
+ # Copyright (C) 2006-2007 Kurt Stephens <ruby-currency(at)umleta.com>
2
+ # See LICENSE.txt for details.
3
+
4
+ require 'currency/exchange/rate/source/base'
5
+
6
+ require 'net/http'
7
+ require 'open-uri'
8
+ # Cant use REXML because of missing </form> tags -- 2007/03/11
9
+ # require 'rexml/document'
10
+
11
+ # Connects to http://xe.com and parses "XE.com Quick Cross Rates"
12
+ # from home page HTML.
13
+ #
14
+ class Currency::Exchange::Rate::Source::Xe < ::Currency::Exchange::Rate::Source::Provider
15
+
16
+ # Defines the pivot currency for http://xe.com/.
17
+ PIVOT_CURRENCY = :USD
18
+
19
+ def initialize(*opt)
20
+ self.uri = 'http://xe.com/'
21
+ self.pivot_currency = PIVOT_CURRENCY
22
+ @raw_rates = nil
23
+ super(*opt)
24
+ end
25
+
26
+
27
+ # Returns 'xe.com'.
28
+ def name
29
+ 'xe.com'
30
+ end
31
+
32
+
33
+ def clear_rates
34
+ @raw_rates = nil
35
+ super
36
+ end
37
+
38
+
39
+ # Returns a cached Hash of rates:
40
+ #
41
+ # xe.raw_rates[:USD][:CAD] => 1.0134
42
+ #
43
+ def raw_rates
44
+ # Force load of rates
45
+ @raw_rates ||= parse_page_rates
46
+ end
47
+
48
+
49
+
50
+ # Parses http://xe.com homepage HTML for
51
+ # quick rates of 10 currencies.
52
+ def parse_page_rates(data = nil)
53
+ data = get_page_content unless data
54
+
55
+ @lines = data = data.split(/\n/);
56
+
57
+ # xe.com no longer gives date/time.
58
+ # Remove usecs.
59
+ time = Time.at(Time.new.to_i).getutc
60
+ @rate_timestamp = time
61
+
62
+ eat_lines_until /More currencies\.\.\.<\/a>/i
63
+ eat_lines_until /^\s*<tr>/i
64
+ eat_lines_until /^\s*<tr>/i
65
+
66
+ # Read first table row to get position for each currency
67
+ currency = [ ]
68
+ eat_lines_until /^\s*<\/tr>/i do
69
+ if md = /<td[^>]+?>.*?\/> ([A-Z][A-Z][A-Z])<\/td>/.match(@line)
70
+ cur = md[1].intern
71
+ cur_i = currency.size
72
+ currency.push(cur)
73
+ $stderr.puts "Found currency header: #{cur.inspect} at #{cur_i}" if @verbose
74
+ end
75
+ end
76
+ raise ParserError, "Currencies header not found" if currency.empty?
77
+
78
+
79
+ # Skip until "1 USD ="
80
+ eat_lines_until /^\s*<td[^>]+?> 1&nbsp;+USD&nbsp;=/
81
+
82
+ # Read first row of 1 USD = ...
83
+ rate = { }
84
+ cur_i = -1
85
+ eat_lines_until /^\s*<\/tr>/i do
86
+ # Grok:
87
+ #
88
+ # <td align="center" class="cur2 currencyA">114.676</td>\n
89
+ #
90
+ # AND
91
+ #
92
+ # <td align="center" class="cur2 currencyA"><div id="positionImage">0.9502\n
93
+ #
94
+ if md = /<td[^>]+?>\s*(<div[^>]+?>\s*)?(\d+\.\d+)\s*(<\/td>)?/i.match(@line)
95
+ usd_to_cur = md[2].to_f
96
+ cur_i = cur_i + 1
97
+ cur = currency[cur_i]
98
+ raise ParserError, "Currency not found at column #{cur_i}" unless cur
99
+ next if cur.to_s == PIVOT_CURRENCY.to_s
100
+ (rate[PIVOT_CURRENCY] ||= {})[cur] = usd_to_cur
101
+ (rate[cur] ||= { })[PIVOT_CURRENCY] ||= 1.0 / usd_to_cur
102
+ $stderr.puts "#{cur.inspect} => #{usd_to_cur}" if @verbose
103
+ end
104
+ end
105
+
106
+ raise ::Currency::Exception::UnavailableRates, "No rates found in #{get_uri.inspect}" if rate.keys.empty?
107
+
108
+ raise ParserError,
109
+ [
110
+ "Not all currencies found",
111
+ :expected_currences, currency,
112
+ :found_currencies, rate.keys,
113
+ :missing_currencies, currency - rate.keys,
114
+ ] if rate.keys.size != currency.size
115
+
116
+ @lines = @line = nil
117
+
118
+ raise ParserError, "Rate date not found" unless @rate_timestamp
119
+
120
+ rate
121
+ end
122
+
123
+
124
+ def eat_lines_until(rx)
125
+ until @lines.empty?
126
+ @line = @lines.shift
127
+ if md = rx.match(@line)
128
+ $stderr.puts "\nMATCHED #{@line.inspect} WITH #{rx.inspect} AT LINES:\n#{@lines[0..4].inspect}" if @verbose
129
+ return md
130
+ end
131
+ yield @line if block_given?
132
+ end
133
+
134
+ raise ParserError, [ 'eat_lines_until failed', :rx, rx ]
135
+
136
+ false
137
+ end
138
+
139
+
140
+ # Return a list of known base rates.
141
+ def load_rates(time = nil)
142
+ if time
143
+ $stderr.puts "#{self}: WARNING CANNOT SUPPLY HISTORICAL RATES" unless @time_warning
144
+ @time_warning = true
145
+ end
146
+
147
+ rates = raw_rates # Load rates
148
+ rates_pivot = rates[PIVOT_CURRENCY]
149
+ raise ::Currency::Exception::UnknownRate,
150
+ [
151
+ "Cannot get base rate #{PIVOT_CURRENCY.inspect}",
152
+ :pivot_currency, PIVOT_CURRENCY,
153
+ ] unless rates_pivot
154
+
155
+ result = rates_pivot.keys.collect do | c2 |
156
+ new_rate(PIVOT_CURRENCY, c2, rates_pivot[c2], @rate_timestamp)
157
+ end
158
+
159
+ result
160
+ end
161
+
162
+
163
+ end # class
164
+
165
+