Cached Models

CachedModels provides to your models a transparent approach to use ActiveSupport cache mechanism.

Usually, when you decide to use cache for your ActiveRecord results, you have to manually implement complex expiring policies, like the following:

   1  class Author < ActiveRecord::Base
   2    has_many :posts
   3  
   4    after_save :expire_cache
   5  
   6    def cached_posts
   7      Rails.cache.fetch("#{cache_key}/posts") { self.posts }
   8    end
   9  
  10    private
  11      def expire_cache
  12        Rails.cache.delete("#{cache_key}/posts")
  13      end
  14  end
  15  
  16  class Post < ActiveRecord::Base
  17    belongs_to :author
  18  
  19    after_save :expire_cache
  20  
  21    private
  22      def expire_cache
  23        Rails.cache.delete("#{author.cache_key}/posts")
  24      end
  25  end


The problem with this kind of approach is that the code complexity grows on the cached-stuff growing. If I would to add hasmany :recentposts and cache the results, I should write other bureaucracy code, for observing my objects.
Note that the above code is incomplete, because doesn't handles a lot of cased provided by the has_many macro. For instance:

   1  post = author.posts.last
   2  another_author.posts << post


The second instruction should expire caches for both the models, cache values are now inconsistent. This means, of course, write other code and add code complexity.

Solution

It would be nice to DRYup your caching code. Here the cached_models approach:

   1  class Author < ActiveRecord::Base
   2    has_many :posts, :cached => true
   3  end
   4  
   5  class Post < ActiveRecord::Base
   6    belongs_to :author, :cached => true
   7  end


That's all!!.

A more complex example..

   1  class Project < ActiveRecord::Base
   2    has_many :developers, :cached => true
   3  
   4    has_many :tickets, :cached => true
   5    has_many :recent_tickets, :limit => 5,
   6      :order => 'id DESC', :cached => true
   7  end
   8  
   9  class Developer < ActiveRecord::Base
  10    belongs_to :project, :cached => true
  11  end


Example 1

   1  project.developers # Database fetch and automatic cache storing
   2  
   3  developer = project.developers.last
   4  developer.update_attributes :first_name => 'Luca' # Database update and cache expiration for project cache

Example 2

   1  # Fetch associated collection for both the projects
   2  project.developers
   3  project2.developers
   4  
   5  developer = project.developers.last
   6  project2.developers << developer # Database update and cache renewal for both project and project2 caches

Example 3

   1  project.tickets # Database fetch and automatic cache storing
   2  ticket = project.recent_tickets.first
   3  ticket.update_attributes :state => 'solved' # Database update and cache expiration for both tickets and recent_tickets entries

Installation

There are three ways to install CachedModels:

Gemified Rails plugin

   1  #config/environment.rb
   2  Rails::Initializer.run do |config|
   3    config.gem 'cached-models'
   4  end
   1  $ (sudo) rake gems:install
   2  $ rake gems:unpack

Rails plugin

   1  $ ./script/plugin install git://github.com/jodosha/cached-models.git

Standalone

   1  $ (sudo) gem install cached-models
   1  require 'rubygems'
   2  require 'activerecord'
   3  require 'cached-models'
   4  
   5  ActiveRecord::Base.rails_cache = ActiveSupport::Cache.lookup_store(:mem_cache_store, 'localhost')

Issues

The current version works only with the has_many macro.

Make sure to configure your current environment with:

   1  config.cache_classes = true
   2  config.action_controller.perform_caching = true
   3  config.cache_store = :mem_cache_store

Repository

http://github.com/jodosha/cached-models/tree/master

How to contribute

      Check out the code and test it:

         1  $ ./script/plugin install git://github.com/jodosha/cached-models.git
      

      Create a ticket to the Sushistar Lighthouse page
      Create a patch and add as attachment to the ticket.

advertising