Browse the Ruby on Rails Community.

You are here: Browse Railsplugins Acts As Cached

Acts As Cached


THIS PLUGIN HAS BEEN DEPRECATED

Instead, please try cache_fu:

$ script/plugin install svn://errtheblog.com/svn/plugins/cache_fu

http://errtheblog.com/post/4872


\ acts_as_cached /

A plugin which allows you to cache any Ruby object with memcached.

Mailing List => http://groups.google.com/group/acts_as_cached

err.the_blog.find_by_title(‘Memcaching Rails’) => http://errtheblog.com/post/27

Installation Steps

1. Install memcached.

Download:            http://danga.com/memcached/download.bml
OSX needs libevent:  http://www.monkey.org/provos/libevent/
OSX needs this too:  http://blog.segment7.net/articles/2006/03/02/fast-memcached-on-os-x

2. Install memcached-client.

$ sudo gem install memcached-client

3. Install this plugin.

$ cd RAILS_ROOT
$ ruby script/plugin install svn://errtheblog.com/svn/plugins/acts_as_cached

4. Start memcached

$ memcached -vv

5. Start rails. Enjoy.

topfunky has a great writeup on memcached here: http://nubyonrails.com/articles/2006/08/17/memcached-basics-for-rails

Please read the note below about setting up CACHE and MemCache.new. You don't need to do that with acts_as_cached.

Usage

After installation you will notice memcached.yml in your config/ directory. This file contains information about your memcached servers. The different configuration options are all represented, so have a look. You do not need to setup MemCache.new in your config files anymore. In fact, get rid of that if you added it after following something like the Rails wiki or topfunky’s guide to install memcached.

=== The Basics

In your class definition, include `acts_as_cached’ as you would with any Rails plugin.

class Story < ActiveRecord::Base
  acts_as_cached
end

Your Story class now has a host of new methods. Among them: get_cache

Use get_cache in place of find to retrieve single Story objects from the cache.

Story.get_cache(1) => #<story:0×29761bc>

Use expire_cache to remove an object from the cache.

Story.expire_cache(10002) => “DELETED\r\n”

=== Methods

Class methods: get_cache(id) Checks to see if `id’ is already in the cache. If so, returns it. If not, calls find(id) on itself and caches the result.

expire_cache(id)
  Clears the cache for a given object.
reset_cache(id)
  Re-sets the cache by calling find(id) and then setting the result to the cache without checking
  if the cache already exists.

Instance methods: expire_cache, reset_cache Calling story.expire_cache is the same as Story.expire_cache(story_object.id).

=== Time to Live

Tweak the cache expiration time by setting a ttl method on your class and returning a number of seconds. The default time is 30 minutes.

class Story &lt; ActiveRecord::Base
  acts_as_cached
end
def self.ttl
  20.minutes
end

=== Callbacks

Ensure your cache is always up to date by utilizing Rails’ callbacks:

class Story &lt; ActiveRecord::Base
  acts_as_cached
end
after_save     :expire_cache_by_id
before_destroy :expire_cache_by_id

On save, your Story object’s cache will be expired. Same with destroy.

=== Versioning Your Models

In a production environment, you may have multiple instances of cached Story object. What happens when you update the Story class and want to push out changes? All of your cached objects are now stale and may have unstable methods or attributes. To solve this, version your cached objects:

class Story &lt; ActiveRecord::Base
  acts_as_cached :version => 1
end

When you make a change, simply increment the version. This will ensure only stories of the same version are retrieved from the cache.

=== Conditions (get_cache-only fake with_scope)

As get_cache calls find on your object, it is possible to ensure object consistency by passing an options hash to acts_as_cached.

class Story &lt; ActiveRecord::Base
  acts_as_cached :conditions => ['stories.state = ?', Story::State::Published]
end

Now a call to Story.get_cache(1) will call, behind the scenes, Story.find(1, :conditions => [‘stories.state = ?’, Story::State::Published]) and cache the returned object.

The options hash passed to acts_as_cached will be passed to find() behind the scenes for you. Be careful.

=== Refresh All Cached Objects on Pageload

You may want to immediately see all the freshest data on a page load at any time. To help you out, acts_as_cached models have the skip_cache_gets= setter. Try this:

class ApplicationController &lt; ActionController::Base
  before_filter :check_cache_skip
end
def check_cache_skip
  returning true do
    ActiveRecord::Base.skip_cache_gets = params[:skip_cache] ? true : false
  end
end

Now any page loaded with ?skip_cache=1 will pull from your database and skip the cache gets. You may want to add some authentication to this method.

=== Associations

Using the acts_as_cached options hash it is possible to cache associations.

class Story &lt; ActiveRecord::Base
  acts_as_cached :include => [ :pages, :category ]
end

Again, a call to Story.get_cache(1) will now call Story.find(1, :include => [ :pages, :category ]) behind the scenes.

If you are caching associations, you will need to put forth some effort to keep your caches up to date. Consider this:

class Story &lt; ActiveRecord::Base
  acts_as_cached :include => :author
end

When you call Story.get_cache(1), both story 1 AND that story’s associated author will be cached in the same slab as the story itself. If you have 20 stories and 1 author, that author’s object will be cached in 20 places. It can get stale.

One solution:

class Story &lt; ActiveRecord::Base
  acts_as_cached :include => :author
end
def after_save
  expire_cache(id)
  Author.expire_cache(author_id)
end

You will also need:

class Author &lt; ActiveRecord::Base
  acts_as_cached 
end
def after_save
  expire_cache(id)
  stories.each do |story|
    Story.expire_cache(story.id)
  end
end

This can get unmanageable rather quickly.

Another solution is to not cache associated objects but rather their IDs, and to override the accesor methods:

class Story &lt; ActiveRecord::Base
  acts_as_cached :include => :author
end
def author
  Author.get_cache(author_id)
end

This ensures only the freshest cache objects are retrieved at any given point.

=== Caching Ruby Objects

Once you’ve installed the plugin, any Ruby object may implement acts_as_cached.

class Song acts_as_cached end

The Reloadable module is included in the class to ensure fresh development reloading (not compatible with edge, yet).

Your class must implement a ‘find’ method. A slightly more complete example:

class Song
  acts_as_cached
end
def self.find(id)
  "I am song #{id}!" 
end

=== Caching Blocks

If you’d like to get crazy, you can pass a block to get_cache(id). The result of the block will be cached.

@songs = Song.get_cache(:all) do
           Song.find(:all).to_json
         end

=== Caching nil

You can not cache `nil’ as a value. acts_as_cache will convert anything you try to cache as `nil’ into `false’ and warn you. Be alert.

=== Keys

Keys are stored, by default, as app-ENVIRONMENT:CLASS:ID. So in development mode a call of Story.get_cache(5) will look for app-development:Story:5.

If you use the :version option, you’ll get something like app-development:Story:v2:5.

=== Cache Size

The default memcached slab (item) size is one megabyte. It is possible to change this but only by recompiling memcached. It is recommended you be mindful of what you cache. Think! Watch your logs.

=== Other Options

It is possible to use something besides memcached as a cache store. Simply pass in your object to your acts_as_cached call via the :store key.

class Post < ActiveRecord::Base acts_as_cached :store => $alternate_store end

Your cache must respond to two methods: set and get. An example:

class MemCacheMock &lt; Hash
  def set(key, data, expiry = 0)
    self[key] = data.dup rescue data
  end
end
def get(key)
  self[key]
end
class Post &lt; ActiveRecord::Base
  acts_as_cached :store => MemCacheMock.new
end

== Useful Memcached Links

- http://errtheblog.com/post/27
- http://jehiah.com/projects/memcached-win32/
- http://danga.com/memcached/download.bml
- http://www.monkey.org/provos/libevent/
- http://blog.segment7.net/articles/2006/03/02/fast-memcached-on-os-x
- $ sudo gem install memcache-client-stats

/ .by. \

Chris Wanstrath, Tim Myrtle, PJ Hyett => chris[at]ozmm[dot]org

NOTE: This description has been extracted from the Plugin README and so the formatting may need updating to make browser friendly