Class: Currency::Exchange::Rate::Source::Xe

Inherits:
Provider show all
Defined in:
lib/currency/exchange/rate/source/xe.rb

Overview

Connects to xe.com and parses “XE.com Quick Cross Rates” from home page HTML.

Constant Summary collapse

PIVOT_CURRENCY =

Defines the pivot currency for xe.com/.

:USD

Instance Attribute Summary

Attributes inherited from Provider

#date, #uri, #uri_path

Attributes inherited from Base

#pivot_currency, #time_quantitizer, #verbose

Instance Method Summary collapse

Methods inherited from Provider

#available?, #date_DD, #date_MM, #date_YYYY, #get_page_content, #get_rate, #get_uri, #rates

Methods inherited from Base

#__subclass_responsibility, #clear_rate, #convert, #currencies, #get_rate, #get_rate_base, #get_rates, #new_rate, #normalize_time, #rate, #rates, #to_s

Constructor Details

#initialize(*opt) ⇒ Xe

Returns a new instance of Xe.



20
21
22
23
24
25
# File 'lib/currency/exchange/rate/source/xe.rb', line 20

def initialize(*opt)
  self.uri = 'http://xe.com/'
  self.pivot_currency = PIVOT_CURRENCY
  @raw_rates = nil
  super(*opt)
end

Instance Method Details

#clear_ratesObject



34
35
36
37
# File 'lib/currency/exchange/rate/source/xe.rb', line 34

def clear_rates
  @raw_rates = nil
  super
end

#eat_lines_until(rx) ⇒ Object

Raises:



117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/currency/exchange/rate/source/xe.rb', line 117

def eat_lines_until(rx)
  until @lines.empty?
    @line = @lines.shift
    if md = rx.match(@line)
      $stderr.puts "\nMATCHED #{@line.inspect} WITH #{rx.inspect} AT LINES:\n#{@lines[0..4].inspect}" if @verbose
      return md
    end
    yield @line if block_given?
  end

  raise ParserError, [ 'eat_lines_until failed', :rx, rx ]

  false
end

#load_rates(time = nil) ⇒ Object

Return a list of known base rates.



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/currency/exchange/rate/source/xe.rb', line 134

def load_rates(time = nil)
  if time
    $stderr.puts "#{self}: WARNING CANNOT SUPPLY HISTORICAL RATES" unless @time_warning
    @time_warning = true
  end

  rates = raw_rates # Load rates
  rates_pivot = rates[PIVOT_CURRENCY]
  raise ::Currency::Exception::UnknownRate,
  [ 
   "Cannot get base rate #{PIVOT_CURRENCY.inspect}",
   :pivot_currency, PIVOT_CURRENCY,
  ] unless rates_pivot
  
  result = rates_pivot.keys.collect do | c2 | 
    new_rate(PIVOT_CURRENCY, c2, rates_pivot[c2], @rate_timestamp)
  end
  
  result
end

#nameObject

Returns ‘xe.com’.



29
30
31
# File 'lib/currency/exchange/rate/source/xe.rb', line 29

def name
  'xe.com'
end

#parse_page_rates(data = nil) ⇒ Object

Parses xe.com homepage HTML for quick rates of 10 currencies.

Raises:



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/currency/exchange/rate/source/xe.rb', line 53

def parse_page_rates(data = nil)
  data = get_page_content unless data

   doc = Hpricot(data)

   # xe.com no longer gives date/time.
   # Remove usecs.

   time = Time.at(Time.new.to_i).getutc
   @rate_timestamp = time

   # parse out the currency names in the table
   currency = [ ]
   doc.search("table.currency tr:first-child .c1").each do |td|
     currency.push(td.inner_text.strip.upcase.intern)
   end
   $stderr.puts "Found currencies: #{currency.inspect}" if @verbose
   raise ParserError, "Currencies header not found" if currency.empty?

   # Parse out the actual rates, dealing with the explanation gunk about which currency is worth more
   
   parsed_rates = doc.search(".currency tr:nth-child(2) td.c2.cA, .currency tr:nth-child(2) td.cLast").map do |e|
     if (inner = e.search('div.aboveLayer')).size > 0
       inner.inner_text.strip
     else
       e.inner_text.strip
     end
   end

   rate = { }

   parsed_rates.each_with_index do |parsed_rate, i|
     usd_to_cur = parsed_rate.to_f
     cur = currency[i]
     raise ParserError, "Currency not found at column #{i}" unless cur
     next if cur.to_s == PIVOT_CURRENCY.to_s
     (rate[PIVOT_CURRENCY] ||= {})[cur] = usd_to_cur
     (rate[cur] ||= { })[PIVOT_CURRENCY] ||= 1.0 / usd_to_cur
     $stderr.puts "#{cur.inspect} => #{usd_to_cur}" if @verbose      
   end




  raise ::Currency::Exception::UnavailableRates, "No rates found in #{get_uri.inspect}" if rate.keys.empty?



  raise ParserError, 
  [
   "Not all currencies found", 
   :expected_currences, currency,
   :found_currencies, rate.keys,
   :missing_currencies, currency - rate.keys,
  ] if rate.keys.size != currency.size

  @lines = @line = nil

  raise ParserError, "Rate date not found" unless @rate_timestamp

  rate
end

#raw_ratesObject

Returns a cached Hash of rates:

xe.raw_rates[:USD][:CAD] => 1.0134


44
45
46
47
# File 'lib/currency/exchange/rate/source/xe.rb', line 44

def raw_rates
  # Force load of rates
  @raw_rates ||= parse_page_rates
end