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:
class Author < ActiveRecord::Base
has_many :posts
after_save :expire_cache
def cached_posts
Rails.cache.fetch("#{cache_key}/posts") { self.posts }
end
private
def expire_cache
Rails.cache.delete("#{cache_key}/posts")
end
end
class Post < ActiveRecord::Base
belongs_to :author
after_save :expire_cache
private
def expire_cache
Rails.cache.delete("#{author.cache_key}/posts")
end
endThe problem with this kind of approach is that the code complexity grows on the cached-stuff growing. If I would to add
has_many :recent_posts 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:post = author.posts.last
another_author.posts << postThe 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:
class Author < ActiveRecord::Base
has_many :posts, :cached => true
end
class Post < ActiveRecord::Base
belongs_to :author, :cached => true
endThat's all!!.
A more complex example..
class Project < ActiveRecord::Base
has_many :developers, :cached => true
has_many :tickets, :cached => true
has_many :recent_tickets, :limit => 5,
:order => 'id DESC', :cached => true
end
class Developer < ActiveRecord::Base
belongs_to :project, :cached => true
endExample 1
project.developers # Database fetch and automatic cache storing
developer = project.developers.last
developer.update_attributes :first_name => 'Luca' # Database update and cache expiration for project cache
Example 2
# Fetch associated collection for both the projects
project.developers
project2.developers
developer = project.developers.last
project2.developers << developer # Database update and cache renewal for both project and project2 caches
Example 3
project.tickets # Database fetch and automatic cache storing
ticket = project.recent_tickets.first
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
#config/environment.rb
Rails::Initializer.run do |config|
config.gem 'cached-models'
end
$ (sudo) rake gems:install
$ rake gems:unpack
Rails plugin
$ ./script/plugin install git://github.com/jodosha/cached-models.git
Standalone
$ (sudo) gem install cached-models
require 'rubygems'
require 'activerecord'
require 'cached-models'
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:
config.cache_classes = true
config.action_controller.perform_caching = true
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:
$ ./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.
