Class: MoneyOXR::RatesStore

Inherits:
Money::RatesStore::Memory
  • Object
show all
Defined in:
lib/money_oxr/rates_store.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRatesStore

Returns a new instance of RatesStore.



11
12
13
14
15
16
17
18
# File 'lib/money_oxr/rates_store.rb', line 11

def initialize(*)
  super
  @app_id = options[:app_id]
  @source = options[:source] || 'USD'
  @cache_path = options[:cache_path]
  @max_age = options[:max_age]
  @on_api_failure = options[:on_api_failure] || :warn
end

Instance Attribute Details

#app_idObject (readonly)

Returns the value of attribute app_id.



9
10
11
# File 'lib/money_oxr/rates_store.rb', line 9

def app_id
  @app_id
end

#cache_pathObject (readonly)

Returns the value of attribute cache_path.



9
10
11
# File 'lib/money_oxr/rates_store.rb', line 9

def cache_path
  @cache_path
end

#last_updated_atObject (readonly)

Returns the value of attribute last_updated_at.



9
10
11
# File 'lib/money_oxr/rates_store.rb', line 9

def last_updated_at
  @last_updated_at
end

#max_ageObject (readonly)

Returns the value of attribute max_age.



9
10
11
# File 'lib/money_oxr/rates_store.rb', line 9

def max_age
  @max_age
end

#on_api_failureObject (readonly)

Returns the value of attribute on_api_failure.



9
10
11
# File 'lib/money_oxr/rates_store.rb', line 9

def on_api_failure
  @on_api_failure
end

#sourceObject (readonly)

Returns the value of attribute source.



9
10
11
# File 'lib/money_oxr/rates_store.rb', line 9

def source
  @source
end

Instance Method Details

#api_uriObject



84
85
86
# File 'lib/money_oxr/rates_store.rb', line 84

def api_uri
  "https://openexchangerates.org/api/latest.json?base=#{source}&app_id=#{app_id}"
end

#get_json_from_apiObject



76
77
78
79
80
81
82
# File 'lib/money_oxr/rates_store.rb', line 76

def get_json_from_api
  URI.open(api_uri).read
rescue OpenURI::HTTPError, SocketError
  raise unless on_api_failure == :warn
  warn "#{$!.class}: #{$!.message}"
  nil
end

#get_rate(iso_from, iso_to) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/money_oxr/rates_store.rb', line 20

def get_rate(iso_from, iso_to)
  load
  super || begin
    if iso_from == source
      nil
    elsif inverse_rate = super(iso_to, iso_from)
      add_rate(iso_from, iso_to, 1 / inverse_rate)
    elsif iso_to == source
      nil
    else
      rate1 = get_rate(iso_from, source)
      rate2 = get_rate(source, iso_to)
      rate1 && rate2 && add_rate(iso_from, iso_to, rate1 * rate2)
    end
  end
end

#loadObject



43
44
45
46
47
48
49
50
51
# File 'lib/money_oxr/rates_store.rb', line 43

def load
  # Loads data and ensures it is not stale.
  if !loaded? && cache_path && File.exist?(cache_path)
    load_from_cache_path
  end
  if app_id && (!loaded? || stale?)
    load_from_api
  end
end

#load_from_apiObject



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/money_oxr/rates_store.rb', line 59

def load_from_api
  # When loading from the API, set the last_updated_at to now.
  # "timestamp" value in response may be days old (it may not update over
  # the weekend)
  now = Time.now
  json = get_json_from_api
  # Protect against saving or loading nil/bad data from API.
  return unless json && json =~ /rates/
  if cache_path
    write_cache_file(json)
    load_from_cache_path
  else
    load_json(json)
  end
  @last_updated_at = now
end

#load_from_cache_pathObject



88
89
90
# File 'lib/money_oxr/rates_store.rb', line 88

def load_from_cache_path
  load_json(File.read(cache_path))
end

#load_json(text) ⇒ Object



96
97
98
99
100
101
102
103
104
105
# File 'lib/money_oxr/rates_store.rb', line 96

def load_json(text)
  data = parse_json(text)
  transaction do
    @last_updated_at = Time.at(data['timestamp'])
    rates.clear
    data['rates'].each do |iso_to, rate|
      add_rate(source, iso_to, rate)
    end
  end
end

#loaded?Boolean

Returns:

  • (Boolean)


37
38
39
40
41
# File 'lib/money_oxr/rates_store.rb', line 37

def loaded?
  transaction do
    rates.any?
  end
end

#parse_json(text) ⇒ Object



107
108
109
110
111
112
113
114
115
# File 'lib/money_oxr/rates_store.rb', line 107

def parse_json(text)
  # Convert text to strings so that we can use BigDecimal instead of Float
  text = text.gsub(/("[A-Z]{3}": ?)(\d+\.\d+)/, '\\1"\\2"')
  data = JSON.parse(text)
  data['rates'] = data['rates'].each_with_object({}) do |(key, value), rates|
    rates[key] = BigDecimal(value)
  end
  data
end

#stale?Boolean

Returns:

  • (Boolean)


53
54
55
56
57
# File 'lib/money_oxr/rates_store.rb', line 53

def stale?
  return false if !max_age
  return true if last_updated_at.nil?
  last_updated_at + max_age < Time.now
end

#write_cache_file(text) ⇒ Object



92
93
94
# File 'lib/money_oxr/rates_store.rb', line 92

def write_cache_file(text)
  File.open(cache_path, 'w') { |file| file.write text }
end