Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Cache based on arguments AND object state; store in memcached, redis, or in-process. Like alias_method, but it's cache_method! One step beyond memoization.

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
lib
test
.gitignore
Gemfile
README.rdoc
Rakefile
cache_method.gemspec

README.rdoc

cache_method

It's like alias_method, but it's cache_method!

Lets you cache the results of calling methods given their arguments. Like memoization, but stored in Memcached, Redis, etc. so that the cached results can be shared between processes and hosts.

Real-world usage

In production use at impact.brighterplanet.com and data.brighterplanet.com.

Example

require 'cache_method'
class Blog
  attr_reader :name
  attr_reader :url

  def initialize(name, url)
    @name = name
    @url = url
  end

  def get_latest_entries
    # ...
  end
  cache_method :get_latest_entries

  # cache_method defaults to using the #hash method
  # It's a "hash code" (integer! always integer!) representing the internal state of an instance.
  # If you need to customize it, you can define #method_cache_hash.
  # In that case, it's recommended that you construct a String or a Hash and then call #hash on it (because you should return an integer)
  def method_cache_hash
    { :name => name, :url => url }.hash
  end
end

Then you can do

my_blog.get_latest_entries => first time won't be cached
my_blog.get_latest_entries => second time will come from cache

And clear them too

my_blog.clear_method_cache :get_latest_entries

(which doesn't delete the rest of your cache)

Configuration (and supported cache clients)

You need to set where the cache will be stored:

CacheMethod.config.storage = Memcached.new '127.0.0.1:11211'

or

CacheMethod.config.storage = Redis.new

or this might even work…

CacheMethod.config.storage = Rails.cache

See Config for the full list of supported caches.

Defining a #hash method (not the same as #to_hash)

Since we're not pure functional programmers, sometimes cache hits depend on object state in addition to method arguments. To illustrate:

my_blog.get_latest_entries

get_latest_entries doesn't take any arguments, so it must depend on my_blog.url or something. This works because we define:

class Blog
  # [...]
  def method_cache_hash
    { :name => name, :url => url }.hash
  end
  # [...]
end

You should follow Ruby convention and have #hash return a Fixnum.

Ideally, you should try to make a String or a Hash and call the standard #hash on that.

Note: this is NOT the same thing as #to_hash! That returns a Hash. What we want is an integer “hash code.”

Using #method_cache_hash instead of #hash

If you don't want to modify #hash, use #method_cache_hash instead.

Module methods

You can put #cache_method right into your module declarations:

module MyModule
  def my_module_method(args)
    # [...]
  end
  cache_method :my_module_method
end

class Tiger
  extend MyModule
end

class Lion
  extend MyModule
end

Rest assured that Tiger.my_module_method and Lion.my_module_method will be cached correctly and separately. This, on the other hand, won't work:

class Tiger
  extend MyModule
  # wrong - will raise NameError Exception: undefined method `my_module_method' for class `Tiger'
  # cache_method :my_module_method
end

Rationale

  • It should be easy to cache a method using memcached, dalli (if you're on heroku), redis, etc. (that's why I made the cache gem)

  • It should be easy to uncache a method without clearing the whole cache

  • It should be easy to cache instance methods

  • It should be easy to cache methods that depend on object state

Copyright

Copyright 2011 Seamus Abshere

Something went wrong with that request. Please try again.