Search code examples
ruby-on-railsrubyactiverecordmemoization

How to memoize hash across requests?


In my Rails application I have a very expensive function that fetches a bunch of conversion rates from an external service once per day:

require 'open-uri'

module Currency

  def self.all
    @all ||= fetch_all
  end

  def self.get_rate(from_curr = "EUR", to_curr = "USD")
    all[from_curr][to_curr]
  end

  private

    def self.fetch_all
      hashes = {}
      CURRENCIES.keys.each do |currency|
        hash = JSON.parse(open(URI("http://api.fixer.io/latest?base=#{currency}")).read)
        hashes[currency] = hash["rates"]
      end
      hashes
    end

end

Is there a way to store the result of this function (a hash) to speed things up? Right now, I am trying to store it in an instance variable @all, which speeds it up a little, however it is not persisted across requests. How can I keep it across requests?


Solution

  • create a file lets say currency_rates.rb in your initializer with the following code:

    require 'open-uri'
    hashes = {}
    CURRENCIES.keys.each do |currency|
      hashes[currency] = JSON.parse(open(URI("http://api.fixer.io/latest?base=#{currency}")).read)["rates"]
    end
    CURRENCY_RATES = hashes
    

    Then write the following rake task which will run daily:

    task update_currency_rates: :environment do
      require 'open-uri'
      hashes = {}
      CURRENCIES.keys.each do |currency|
        hashes[currency] = JSON.parse(open(URI("http://api.fixer.io/latest?base=#{currency}")).read)["rates"]
      end
      Constant.const_set('CURRENCY_RATES', hashes)
    end
    

    The only drawback is that it will run every time you deploy new version of your app/on restart. You can go with it if you are ok with it.

    You can avoid that if you use caching like memcachier or something, then you can do like,

    def currency_rates
      Rails.cache.fetch('currency_rates', expires_in: 24.hours) do
        # write above code in some method and call here which will return hash and thus it will be cached.
      end
    end