Improve Performance With Record/Resource Caching

For my personal homepage I wanted to load the latest tips from Handy Rails Tips as well as my most recent Tweets on twitter. As these resources are stored on different sites from my homepage, there was a little lag when loading the landing page.

I don’t tweet or post tips every day so it seemed a little unnecessary to have these loaded fresh with every hit to my site. Instead, updating them once a day seemed fine.

Here’s a quick tip for caching ActiveResource resources or any other data that doesn’t necessarily need to be ‘hot off the press’.

Below is the Tip model for my homepage:

class Tip < ActiveResource::Base
  self.site = "http://handyrailstips.com/"

  def self.cached
    return @@cached if @@last_cached == Date.today
    @@last_cached = Date.today
    @@cached      = self.all
  end

end

I wont cover the ins and outs of ActiveResource here, but let’s have a look at the cached method:

the first line will return the value of the class variable @@cached only if the @@last_cached date is today. If not, we update @@last_cached to equal today’s date and then re-set @@cached to equal all tips (only 5 are returned from Handy Rails Tips). All subsequent calls to the cached method on this date will return the contents of @@cached.

Then, in the controller all I had to add was:

class HomepagesController < ApplicationController::Base

  # GET /
  def index
    @tips = Tip.cached
  end

end

Simple!

For the tweets I took pretty much the same approach but did not use ActiveResource.

class Tweet

  require "net/http"

  # a Net::HTTP object to be used for each request
  HTTP_OBJ = Net::HTTP.new("twitter.com")

  def self.cached
    # return the cached tweets if they were cached today
    return @cached_tweets if @last_cached == Date.today

    # else, find the latest tweets and changed the cached date to today
    @last_cached      = Date.today    
    @cached_tweets = find_latest
  end

  def self.find_latest(no = 5)
    response, xml = HTTP_OBJ.request_get "/statuses/user_timeline/gavin_morrice.xml?callback=twitterCallback2&count=#{no}"
    # returns an array of new Tweets with the content of each <text> tag
    Nokogiri( xml ).css("status").map { |status| new status.css("text").text }
  end

  attr_reader :text

  def initialize( text )
    @text = text
  end

end

The twitter API returns quite a lot of XML so I used Nokogiri to pick out what I need, the status tags. Each of these are then parsed and their content loaded into a new Tweet object. Calling @tweet.text returns the text content from this tweet.

Remember – by default, classes are not cached from one request to another in the development or test environments so to check this is working properly you’ll have to set cache_classes to true in development.rb.

# In the development environment your application's code is reloaded on
# every request.  This slows down response time but is perfect for development
# since you don't have to restart the webserver when you make code changes.
config.cache_classes = true

Written by

Photo of Gavin Morrice
Gavin Morrice

Software engineer based in Scotland

Work with me

Need help with some code?

In my free time, I like to help people improve their code and skills on Codementor.
Contact me on Codementor