Gravatar

Geoff Evason

Archive for October, 2008

Simple Currency Conversion Rate API Consumption For Ruby / Rails

Monday, October 27th, 2008

I had the need to consume some exchange rate data for an internal project so I began looking for an about. My searching found no api for Google Finance, Yahoo Finance, XE, or Oanda. :-(

Fortunately I found the currency converter from Xavier Media and their simple currency exchange rate xml API, which includes historical data too. :-D

I put together an absolutely minimal lib to get the data I need.  I just needed the AU/US rate.  The xml provides all data as base to EUR, but with some simple math I can find the rate I need with reasonable accuracy.  In this case ‘accuracy’ is based on spot checking it against the yahoo rates.

I thought it worth sharing in case others are looking for something similar.

UPDATE: I changed the xml string below to better handle single digit date months. Xavier needs ‘01′ instead of ‘1′

require "cgi"
require "uri"
require "net/https"
require "rexml/document"

module XavierMedia
  # Returns the exchange rate (AUD/USD) on the given date.
  def self.exchange_rate_on(date)
    url = URI.parse("http://api.finance.xaviermedia.com/api/#{date.year}/#{date.strftime("%m")}/#{date.strftime("%d")}.xml")

    resp = Net::HTTP.get(url)
    xml  = REXML::Document.new(resp)

    us_to_eur = 1.0
    au_to_eur = 1.0
    xml.elements.each("//exchange_rates/fx") { |el|
      if el.elements[1].text == "USD"
        us_to_eur = el.elements[2].text.to_f rescue 1.0
      end
      if el.elements[1].text == "AUD"
        au_to_eur = el.elements[2].text.to_f rescue 1.0
      end
    }

    return us_to_eur/au_to_eur
  end
end

 

Rails Fragment Caching Slowness With Regex Expiry

Sunday, October 26th, 2008

Fragment caching in rails works great.  We use it a lot for MomentVille and easily get most of our responses in under 150ms because it dramatically reduces teh number of queries we need to run for our most common actions.

I did notice some slowness recently though, and was quite confused.  The slowness only occurred on our production servers. Our dev, test, and pre-production servers were all still fast.  I tried using new relic rpm service to help pinpoint the problem, and while it does a great job in helping you track things, it didn’t help me narrow down the problem.

Ultimately I discovered that the problem had to do with how we clear the cache. For some actions we have to expire multiple fragments, so we used regex expiration.  Unfortunately, that is very slow.  It seems that regex expiry compares your regex to each fragment stored, even if you think your regex looks like it’s targeting a directory.

Alexander Dymo had a post about regex expiry of fragment caches in rails that outlined a solution for him.  It helped guide me to a solution that works well for us.  OUr fragment caches are actually structured around the data as opposed to the actions, so related fragments are stored within sub directories.  When we need to clear a bunch at once, we just want to wipe out the whole directory.  So, I created a file called fragment_dir_expiration.rb and put it into my /config/initializers folder.  It looks like this:

# For rails 2.0 and lower
module ActionController
  module Caching
    module Fragments

      #dir is the cache path relative to the cache root
      def expire_fragment_dir(dir, options = nil)
        return unless perform_caching
        self.class.benchmark("Expired fragments in dir : #{dir}") do
          ActionController::Base.cache_store.delete_fragment_dir(dir, options)
        end
      end

      class UnthreadedFileStore

        def delete_fragment_dir(dir, options = nil)
          path = @cache_path + dir
          return unless File.exist?(path) #it's ok to not have the cache dir
          search_dir(path) do |f|
            begin
              File.delete(f)
            rescue SystemCallError => e
              # If there's no cache, then there's nothing to complain about
            end
          end
        end

      end
    end
  end
end

When you want to call this you can call it from an observer with a call like this

class WidgetSweeper < ActionController::Caching::Sweeper
  observe Widget
  def after_save(widget)
    # Expire all the fragments for the updated widget
    expire_fragment_dir("/widget/#{widget.id}/")
  end
end

UPDATE: For rails 2.1 and above use the following:

module ActionController
  module Caching
    module Fragments

      #dir is the cache path relative to the cache root
      def expire_fragment_dir(dir, options = nil)
        return unless perform_caching
        self.class.benchmark("Expired fragments in dir : #{dir}") do
          ActionController::Base.cache_store.delete_fragment_dir(dir, options)
        end
      end

    end
  end
end

module ActiveSupport
  module Cache
    class FileStore

      def delete_fragment_dir(dir, options = nil)
        path = @cache_path + dir
        return unless File.exist?(path) #it's ok to not have the cache dir
        search_dir(path) do |f|
          begin
            File.delete(f)
          rescue SystemCallError => e
            # If there's no cache, then there's nothing to complain about
          end
        end
      end
    end
  end
end