How do I cache a method with Ruby/Rails?

an in-code approach could look something like this:

def get_listings
  @listings ||= get_listings!
end

def get_listings!
  Hpricot.XML(open(xml_feed))
end

which will cache the result on a per-request basis (new controller instance per request), though you may like to look at the 'memoize' helpers as an api option.

If you want to share across requests don't save data on the class objects, as your app will not be threadsafe, unless you're good at concurrent programming & make sure the threads don't interfere with each other's data access to the shared variable.

The "rails way" to cache across requests is the Rails.cache store. Memcached gets used a lot, but you might find the file or memory stores fit your needs. It really depends on how you're deploying and whether you want to prioritise cache hits, response time, storage (RAM), or use a hosted solution e.g. a heroku addon.


As nruth suggests, Rails' built-in cache store is probably what you want.

Try:

def get_listings
  Rails.cache.fetch(:listings) { get_listings! }
end

def get_listings!
  Hpricot.XML(open(xml_feed))
end

fetch() retrieves the cached value for the specified key, or writes the result of the block to the cache if it doesn't exist.

By default, the Rails cache uses file store, but in a production environment, memcached is the preferred option.

See section 2 of http://guides.rubyonrails.org/caching_with_rails.html for more details.


You can use the cache_method gem:

gem install cache_method
require 'cache_method'

In your code:

def get_listings
  Hpricot.XML(open(xml_feed))
end
cache_method :get_listings

You might notice I got rid of get_listings!. If you need a way to refresh the data manually, I suggest:

def refresh
  clear_method_cache :get_listings
end

Here's another tidbit:

def get_listings
  Hpricot.XML(open(xml_feed))
end
cache_method :get_listings, (60*60) # automatically expire cache after an hour