acvwilson-acvwilson-currency 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/COPYING.txt +339 -0
  2. data/ChangeLog +8 -0
  3. data/LICENSE.txt +65 -0
  4. data/Manifest.txt +58 -0
  5. data/README.txt +51 -0
  6. data/Releases.txt +155 -0
  7. data/TODO.txt +9 -0
  8. data/currency.gemspec +18 -0
  9. data/examples/ex1.rb +13 -0
  10. data/examples/xe1.rb +20 -0
  11. data/lib/currency.rb +143 -0
  12. data/lib/currency/active_record.rb +265 -0
  13. data/lib/currency/config.rb +91 -0
  14. data/lib/currency/core_extensions.rb +48 -0
  15. data/lib/currency/currency.rb +175 -0
  16. data/lib/currency/currency/factory.rb +121 -0
  17. data/lib/currency/currency_version.rb +6 -0
  18. data/lib/currency/exception.rb +119 -0
  19. data/lib/currency/exchange.rb +50 -0
  20. data/lib/currency/exchange/rate.rb +214 -0
  21. data/lib/currency/exchange/rate/deriver.rb +157 -0
  22. data/lib/currency/exchange/rate/source.rb +89 -0
  23. data/lib/currency/exchange/rate/source/base.rb +166 -0
  24. data/lib/currency/exchange/rate/source/failover.rb +63 -0
  25. data/lib/currency/exchange/rate/source/federal_reserve.rb +160 -0
  26. data/lib/currency/exchange/rate/source/historical.rb +79 -0
  27. data/lib/currency/exchange/rate/source/historical/rate.rb +184 -0
  28. data/lib/currency/exchange/rate/source/historical/rate_loader.rb +186 -0
  29. data/lib/currency/exchange/rate/source/historical/writer.rb +220 -0
  30. data/lib/currency/exchange/rate/source/new_york_fed.rb +127 -0
  31. data/lib/currency/exchange/rate/source/provider.rb +120 -0
  32. data/lib/currency/exchange/rate/source/test.rb +50 -0
  33. data/lib/currency/exchange/rate/source/the_financials.rb +191 -0
  34. data/lib/currency/exchange/rate/source/timed_cache.rb +198 -0
  35. data/lib/currency/exchange/rate/source/xe.rb +165 -0
  36. data/lib/currency/exchange/time_quantitizer.rb +111 -0
  37. data/lib/currency/formatter.rb +300 -0
  38. data/lib/currency/macro.rb +321 -0
  39. data/lib/currency/money.rb +296 -0
  40. data/lib/currency/money_helper.rb +13 -0
  41. data/lib/currency/parser.rb +193 -0
  42. data/spec/ar_base_spec.rb +140 -0
  43. data/spec/ar_column_spec.rb +69 -0
  44. data/spec/ar_core_spec.rb +64 -0
  45. data/spec/ar_simple_spec.rb +31 -0
  46. data/spec/config_spec.rb +29 -0
  47. data/spec/federal_reserve_spec.rb +75 -0
  48. data/spec/formatter_spec.rb +72 -0
  49. data/spec/historical_writer_spec.rb +187 -0
  50. data/spec/macro_spec.rb +109 -0
  51. data/spec/money_spec.rb +347 -0
  52. data/spec/new_york_fed_spec.rb +73 -0
  53. data/spec/parser_spec.rb +105 -0
  54. data/spec/spec_helper.rb +25 -0
  55. data/spec/time_quantitizer_spec.rb +115 -0
  56. data/spec/timed_cache_spec.rb +95 -0
  57. data/spec/xe_spec.rb +50 -0
  58. metadata +117 -0
@@ -0,0 +1,127 @@
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.newyorkfed.org/markets/fxrates/FXtoXML.cfm
12
+ # ?FEXdate=2007%2D02%2D14%2000%3A00%3A00%2E0&FEXtime=1200 and parses XML.
13
+ #
14
+ # No rates are available on Saturday and Sunday.
15
+ #
16
+ class Currency::Exchange::Rate::Source::NewYorkFed < ::Currency::Exchange::Rate::Source::Provider
17
+ # Defines the pivot currency for http://xe.com/.
18
+ PIVOT_CURRENCY = :USD
19
+
20
+ def initialize(*opt)
21
+ self.uri = 'http://www.newyorkfed.org/markets/fxrates/FXtoXML.cfm?FEXdate=#{date_YYYY}%2D#{date_MM}%2D#{date_DD}%2000%3A00%3A00%2E0&FEXTIME=1200'
22
+ @raw_rates = nil
23
+ super(*opt)
24
+ end
25
+
26
+
27
+ # Returns 'newyorkfed.org'.
28
+ def name
29
+ 'newyorkfed.org'
30
+ end
31
+
32
+
33
+ # New York Fed rates are not available on Saturday and Sunday.
34
+ def available?(time = nil)
35
+ time ||= Time.now
36
+ ! [0, 6].include?(time.wday) ? true : false
37
+ end
38
+
39
+
40
+ def clear_rates
41
+ @raw_rates = nil
42
+ super
43
+ end
44
+
45
+
46
+ def raw_rates
47
+ rates
48
+ @raw_rates
49
+ end
50
+
51
+
52
+ # The fed swaps rates on some currency pairs!
53
+ # See http://www.newyorkfed.org/markets/fxrates/noon.cfm (LISTS AUD!)
54
+ # http://www.newyorkfed.org/xml/fx.html (DOES NOT LIST AUD!)
55
+ @@swap_units = {
56
+ :AUD => true,
57
+ :EUR => true,
58
+ :NZD => true,
59
+ :GBP => true,
60
+ }
61
+
62
+
63
+ # Parses XML for rates.
64
+ def parse_rates(data = nil)
65
+ data = get_page_content unless data
66
+
67
+ rates = [ ]
68
+
69
+ @raw_rates = { }
70
+
71
+ $stderr.puts "#{self}: parse_rates: data =\n#{data}" if @verbose
72
+
73
+ doc = REXML::Document.new(data).root
74
+ x_series = doc.elements.to_a('//frbny:Series')
75
+ raise ParserError, "no UNIT attribute" unless x_series
76
+ x_series.each do | series |
77
+ c1 = series.attributes['UNIT'] # WHAT TO DO WITH @UNIT_MULT?
78
+ raise ParserError, "no UNIT attribute" unless c1
79
+ c1 = c1.upcase.intern
80
+
81
+ c2 = series.elements.to_a('frbny:Key/frbny:CURR')[0].text
82
+ raise ParserError, "no frbny:CURR element" unless c2
83
+ c2 = c2.upcase.intern
84
+
85
+ rate = series.elements.to_a('frbny:Obs/frbny:OBS_VALUE')[0]
86
+ raise ParserError, 'no frbny:OBS_VALUE' unless rate
87
+ rate = rate.text.to_f
88
+
89
+ date = series.elements.to_a('frbny:Obs/frbny:TIME_PERIOD')[0]
90
+ raise ParserError, 'no frbny:TIME_PERIOD' unless date
91
+ date = date.text
92
+ date = Time.parse("#{date} 12:00:00 -05:00") # USA NY => EST
93
+
94
+ # Handle arbitrary rate reciprocals!
95
+ if @@swap_units[c1] || @@swap_units[c2]
96
+ c1, c2 = c2, c1
97
+ end
98
+
99
+ rates << new_rate(c1, c2, rate, date)
100
+
101
+ (@raw_rates[c1] ||= { })[c2] ||= rate
102
+ (@raw_rates[c2] ||= { })[c1] ||= 1.0 / rate
103
+ end
104
+
105
+ # $stderr.puts "rates = #{rates.inspect}"
106
+ raise ::Currency::Exception::UnavailableRates,
107
+ [
108
+ "No rates found in #{get_uri.inspect}",
109
+ :uri, get_uri,
110
+ ] if rates.empty?
111
+
112
+ rates
113
+ end
114
+
115
+
116
+ # Return a list of known base rates.
117
+ def load_rates(time = nil)
118
+ # $stderr.puts "#{self}: load_rates(#{time})" if @verbose
119
+ self.date = time
120
+ parse_rates
121
+ end
122
+
123
+
124
+ end # class
125
+
126
+
127
+
@@ -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
+