开发者

rails caching with money gem (eu_central_bank)

I found this add-on for the Money gem which updates from the ECB European Central Bank (updates its rates every 24 hours) but I'm unsure how I should go about caching in my rails app which uses multiple currencies.

http://github.com/RubyMoney/eu_central_bank

eu_bank ||= EuCentralBank.new
eu_bank.update_rates
#Rails.cache.fetch('rates', :expires_in => 24.hours) { eu_bank.update_rates }
rate = eu_bank.exchange_with(Money.new(100, session[:currency]), "USD").to_f

It has a function to write out the rates to some file... but i'm 开发者_Python百科not sure that's what I want either. I'm also using heroku which has a read-only file system.

eu_bank.save_rates("/some/file/location/exchange_rates.xml")

I couldn't find any way to check the age on the object either. I'm just wondering the best option to load it once per 24 hours and persist for my entire Rails app. Any pointers?


This can be done by using Rails low-level caching and a before_filter:

class ApplicationController < ActionController::Base

  before_filter :set_conversion_rates

  def set_conversion_rates

    rates = Rails.cache.fetch "money:eu_central_bank_rates", expires_in: 24.hours do

      Money.default_bank.save_rates_to_s
    end

    Money.default_bank.update_rates_from_s rates

  end
end

This code will download the rates once in 24 hours and save the results to the cache (whatever caching module you use), from where the bank object loads them on every request.


My current solution is not very elegant, but works at the moment - I'm using initializer and update/cache rates on app start.

config/initializers/money.rb

# Money exchange
::Money.default_bank = ::EuCentralBank.new

EU_CENTRAL_BANK_CACHE = '/tmp/eu_bank_exchange_rates.xml'

# Right now we update exchange rates on app restart. App is restarted daily after logs rotation,
# Should be ok for now.

if (!File.exist?(EU_CENTRAL_BANK_CACHE)) || File.mtime(EU_CENTRAL_BANK_CACHE) < 23.hours.ago
  p "Updating money exchange rates"
  ::Money.default_bank.save_rates(EU_CENTRAL_BANK_CACHE)
end

::Money.default_bank.update_rates(EU_CENTRAL_BANK_CACHE)


I just deployed a solution that caches the exchange rates in memcache and updates it every 24 hours. You have to use the latest money gem, commit https://github.com/RubyMoney/eu_central_bank/commit/fc6c4a3164ad47747c8abbf5c21df617d2d9e644 is required. Since I don't want to restart my web processes every 24 hours, I check for new exchange rates in a before_filter (nicer way possible?). The actual Money.default_bank.update_rates call might be moved into a recurring resque job (or whatever).

lib/my_namespace/bank.rb

module MyNamespace
  class Bank
    def self.update_rates_if_changed
      if last_updated.nil? || last_updated + 12.hours <= Time.now
        update_rates
      end
    end

    def self.update_rates
      fetch_rates

      # Take latest available currency rates snapshot
      [0, 1, 2].each do |days|
        date = Date.today - days.days
        next unless Rails.cache.exist?(rate_key(date))

        begin
          rates = Rails.cache.read(rate_key(date))
          Money.default_bank.update_rates_from_s(rates)
        rescue Nokogiri::XML::SyntaxError
          print "error occurred while reading currency rates"
          # our rates seem to be invalid, so clear the cache and retry
          Rails.cache.delete(rate_key(date))
          update_rates
        end

        break
      end
    end

    private
    def self.fetch_rates
      return if Rails.cache.exist?(rate_key)

      print "Updating currency rates ... "
      begin
        Rails.cache.write(rate_key, Money.default_bank.save_rates_to_s)
        puts "finished"
      rescue Exception => ex
        puts "error occurred: #{ex.inspect}"
      end
    end

    def self.rate_key(date = Date.today)
      ["exchange_rates", date.strftime("%Y%m%d")]
    end

    def self.last_updated
      Money.default_bank.last_updated
    end
  end
end

app/controllers/application_controller.rb

class ApplicationController
  before_filter :check_for_currency_updates

  def check_for_currency_updates
    MyNamespace::Bank.update_rates_if_changed
  end
end

config/initializers/money.rb

MyNamespace::Bank.update_rates_if_changed


Since the amount of data is relatively small you can Marshal.dump the eu_bank object, store it in memchache with an expiry date of 24 hours (see expiration in this doc).

And each time you need it you retrieve it from memchache and Marshal.load it.

If the key has expired or vanished from the cache, you fetch it again for real

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜