Simple Currency Conversion Rate API Consumption For Ruby / Rails

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.


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.month}/#{date.day}.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

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:

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
          fragment_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

 

My .bash_profile for git & Rails

August 19th, 2008

I’ve converted all of my projects from svn to git.  If you haven’t done so already, and can do it, I would recommend not waiting anymore.  It does make things better.  

I used to really like the visual representations available on svn (such as tortoise svn) and rarely used the command line for repository related things.  The way git works (in particular, not having to have a special ’svn folder within each folder) makes managing files way easier.  The best way to learn is to check out the git peepcode. I borrowed some of the aliases below from the screencast.

Now that I’m on the command line more, I’ve created a .bash_profile to make my life easier.  It is full of nice goodies that I collected.  I figured that I’d share it with the world in case others find it helpful.  It includes:

  • Updating the OSX Leopard terminal title bar (and potentially the tabs) to include the working directory
  • Updating the command prompt to include the current directory and the current git branch (if you’re in a git repo)
  • Lots of aliases to save myself keystrokes.

# Get the name of the current git branc
function parse_git_branch {
  git branch --no-color 2> /dev/null | sed -e '/^[^*]/d’ -e ’s/* \(.*\)/(\1)/’
}

# Update the command prompt to be <user>:<current_directory>(git_branch) >
# Note that the git branch is given a special color
function set_my_prompt {
  PS1=”\u:\w\e[1;34m\$(parse_git_branch)\e[m > "
}

# Update the title for the terminal window to be the full working dir
function set_term_title
{
    local title="$1"
    if [[ -z "$title" ]]; then
        title=”root”
    fi

    local tmpdir=~/Library/Caches/${FUNCNAME}_temp
    local cmdfile=”$tmpdir/$title”

    # Set window title
    #echo -n -e “\e]0;${title}\a”
    echo -n -e “\e]0;${PWD#*/}\a”

   # Set tab title
   # This works by creating a process with the name of the working dir.  
   # So, the tab name doesn’t stick if you start running a mongrel server :-(
    if [[ -n ${CURRENT_TAB_TITLE_PID:+1} ]]; then
        kill $CURRENT_TAB_TITLE_PID
    fi
    mkdir -p $tmpdir
    ln /bin/sleep “$cmdfile”
    “$cmdfile” 10 &
    CURRENT_TAB_TITLE_PID=$(jobs -x echo %+)
    disown %+
    kill -STOP $CURRENT_TAB_TITLE_PID
    command rm -f “$cmdfile”
}

set_my_prompt
PROMPT_COMMAND=’set_term_title “${PWD##*/}”‘

# Some aliases I find useful
alias gclb=”git checkout -b”
alias gb=”git branch”
alias gba=”git branch -a”
alias gs=”git status”
alias gca=”git commit -a”
alias gcm=”git commit -m”
alias gk=”gitk –all &”
alias ss=”script/server”
alias ssp=”script/server -p”
alias sr=”script/runner”

Big credit goes Christopher Stawarz to the the tab & title thing: l

Startup-Australia.org - Great Resource, Please Contribute

June 12th, 2008

I had begun jotted down a list of resources I’ve found helpful while trying to start a business in Australia. I was going to make a blog post out of it. Yesterday I was introduced to a site that has beat me to the punch, and has made it much more useful then a blog post would have been.

Startup-Australia.org is a wiki that contains a range of resources that entrepreneurs will find useful. It’s new but already contains lots of great info about a range of things including funding, events, and startups.

Instead of finishing my blog post I’m just going to add what I know there instead. I suggest others do the same.

The World Of Press Releases

June 4th, 2008

I’m trying to learn more about promotion and press releases. I came across an excellent list of online PR sites that will allow you to post press releases. The list was created the founder of the smart online shopping site tjoos.com

I’ve been playing around with this list for most of the day and have published press releases in a number of places so far. It’s a very painful process though. There was way too much cutting and pasting today….

Railscasts Does It Again : Site Wide Announcements

May 27th, 2008

A note to all rails developers, new and old.  If you’re not following Ryan Bates’ Railcasts, you should be.

I follow a variey of rails blogs and lean on a number of resources quite regularly, but the Railscasts are consistently the most useful.  There are now over 100 railscast, each one a roughly 5 minute screencast outlining the solution to some problem.

A recent cast showed how to create a site wide announcement that each user could mark as read individually.  This is a great, non-intrusive way to communicate notices with users.

The screencast details how to do it.  I was able to implement this on a site in a very short period of time.  I made some modifications which make it work better within my site.  I do have one suggestion to improve it overall.  To track whether a message had been read/should be shown Ryan uses the session.  Sessions expire in the near future, and if using a db store, should be wiped daily.  If your users don’t visit daily, you will want to create a message that hangs around for a week or 2.  In this case, a session variable won’t work.  Instead, you can store the info in a cookie and set a delayed expire time on it.  (By default, cookies expire with the session in rails).

Before reading how to store this info in a cookie you should watch the screencast.  Once you’ve implemented everything like Ryan’s demo, there are just 3 small changes to use cookies and hence have a longer memory.

1. In your controller, set the cookie:


def hide_announcement
  cookies[:announcement_hide_time] ={ :value => Time.now.to_s , :expires => 2.weeks.from_now }
end

2. In your helper method, read the value from the cookie.


def current_announcements
  @announcements ||= Announcement.current_announcements(cookies[:announcement_hide_time])
end

3. In the announcement controller you need to parse the time since it is stored as a string in the cookie


def self.current_announcements(hide_time)
  with_scope :find => { :conditions => "starts_at <= now() AND ends_at >= now()" } do
    if hide_time
      time = Time.parse(hide_time)
      find(:all, :conditions => ["updated_at >= ?", time])
    else
      find(:all)
    end
  end
end

TechNation.com.au Has Launched

May 22nd, 2008

TechNation, an exciting new blog focused on Australia tech startups, launched this week to coincide with CeBit.  From the first post:

TechNation Australia is a technology news, review and analysis site with a focus on startups and Internet companies in Australia.

It was born out of Open Coffee, a Sydney based bi-weekly meetup of entrepreneurs.  I think the blog is a great idea and hope that it will develop into a useful resource for Aussie entreprenuers.  By continuing to provide great content it can help the startup community flourish and let startups down under gain exposure into other markets.  A commonly perceived challenge among startups in Australia seems to be the geographic isolation.  This does not need to be the case, especially as the world continues to flatten.  If you are part of, or know of startup that you would like to see featured please contact TechNation (see their contact page).  If you are interested in startup news - please subscribe to the feed.

Decisions, Decisions…

May 19th, 2008

Hmm, which flight should I choose?

flight decision

Why So Many Blog Posts?

May 18th, 2008

I’ve been posting a lot of blog entries recently. There are 2 reasons for the increase in frequency. First, it gives me something to do if I’m commuting to the city. Second, and more importantly, it lets me experiment with a variety of things.

My recent posts have been mostly non-technical. I find writing detailed tech posts takes longer.  Instead, as I said, I’m experimenting.  So, what am I experimenting with?

Publicizing.
My new blog posts have been shared via twitter (sorry for any tweet spam), facebook, technorati, readburner, digg, stumbleupon, reddit, rssmeme, and mybloglog. I think the results will be relative to the size of the community I have for each, and since I just signed up recently for most of them I’m not expecting much.  So far reddit is providing the most traffic.

Typo-optimizing.
I’m experimenting with SEO and typos. In a recent post I intentionally misspelled ‘FirendConnect’ twice because I noticed that the google search results for that misspelled term were empty.  I managed to get the #1 spot for that term by doing so.

Diggability
My last post was purely to see how many diggs I could get. There are 2 very common themes among popular digg articles recently: Lists & Anti-Clinton. I figured by writing an anti-Clinton list I might get dugg. I think, however, that you probably need a group of friendly diggers to help get you over an initial threshold.  I only got 5 diggs (nowhere close enough to get dugg).

3 More Reasons Not To Vote For Clinton

May 15th, 2008

As the battle for the democratic nomination continues in the US, here are 3 more reasons not to vote for Hillary Clinton.

1. She’s Losing
2. She’s Out Of Money
3. She Can’t Spell

Sound harsh, trivial, or silly? Let me explain. Each of these reasons are the result of very real and very significant leadership flaws exhibited by Mrs. Clinton.

1. She’s Losing

Senator Clinton is, undoubtedly, an accomplished woman; even if her experience is questioned. She was the clear favorite near the end of 2007. In fact, she held a 23% lead over Obama in the polls. By all accounts the nomination was hers to lose - and lose it she did. If her leadership throughout her campaign has seen her turn from a dominate favorite into an also-ran, just imagine what her leadership for America would do.

2. She’s Out Of Money

The Clinton campaign recently announced it is $20M in debt. The campaign to date has raised $194M, so she has overspent by more than 10.3%. This clearly demonstrates her willingness to be fiscally irresponsible in order to pursue her goals. To demonstrate what this would look like on a national scale, the 2007 tax receipts were $2.568 Trillion and the deficit was $162 Billion; this was only a 6.3% overspend. Let me stress this point: if Clinton ran America like she ran her campaign, the country’s deficit would be significantly higher than it is under the current Bush administration. America can ill-afford more fiscal disasters.

3. She Can’t Spell

Good leaders surround themselves with intelligent and capable people. This recent open letter, from Clinton to Obama, contained several mistakes including the ‘creative’ words “untaken” and “publically“. I can’t fault her for making some typos - I make them all the time. With such an important letter though, one or more of her staff should have checked it. So did Clinton not have her staff read it, or did they just miss the obvious mistakes? I’m not sure which is worse but neither is what you want from a president.

A candidate’s positions on the issues and their leadership abilities are both important to consider. I truly commend Hillary Clinton; She has done well to get to where she is today. She is not, however, a leader that can be trusted as President. A president should be able to win a battle, especially when they start in the lead. A president should be responsible. A president should surround themselves with capable staff. Clinton has demonstrated through her campaign that she does not have these 3 leadership traits.