# Model Cache instead of Controller Cache

**URL:** <https://www.ruby-forum.com/t/model-cache-instead-of-controller-cache/103611>\
**Category:** Rails\
**Created:** [May 8, 2007, 1:12pm UTC](https://www.ruby-forum.com/t/model-cache-instead-of-controller-cache/103611 "2007-05-08T13:12:07Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![Tobias\_Numiranta](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/4491bb/32.png) [@Tobias\_Numiranta](https://www.ruby-forum.com/u/Tobias_Numiranta)\
**Post date:** [May 8, 2007, 1:12pm UTC](https://www.ruby-forum.com/t/model-cache-instead-of-controller-cache/103611/1 "2007-05-08T13:12:07Z")

</div>

Hi, I’ve made a model cache for rails, to use when you want to cache  
model instance methods, rather than controller calls. Does anything  
similar exist already? Any comments on the implementation?

Since it serializes cached data, it might be good to add the following  
to environment.rb to get YAML-support for the rails class autoloading  
mechanism.

– add to environment.rb

# YAML support for class autoloading

YAML.add\_domain\_type(“ActiveRecord,2007”, “”) do |type, val|  
klass = type.split(’:’).last.constantize  
YAML.object\_maker(klass, val)  
end

class ActiveRecord::Base  
def to\_yaml\_type  
“!ActiveRecord,2007/#{self.class}”  
end  
def to\_yaml\_properties # only store the attributes  
[’@attributes’]  
end  
end

– model\_cache\_helper.rb

# ModelCache to use on an ActiveRecord class to cache method calls.

Example:

# 

# Class X \< ActiveRecord::Base

# def heavy\_calculation(a, b)

# “result #{self.x} #{a} #{b}”

# end

# 

# include ModelCacheHelper

# cache\_method :heavy\_calculation

# end

# 

# The created method X.heavy\_calculation(id, arg1, arg2) will then

check if the

# result is cached, otherwise it will do

X.find(1).heavy\_calculation(arg1, arg2)

# and cache the result. Calling heavy\_calculation on an instance of X

will also

# cache the result.

# 

# The instance method expire\_cache, will expire the cache for that

object. It

# can be used with an optional argument, expire\_cache(method), to only

expire

# the cache for a certain method.

# 

# Non-string result will be serialized with YAML and automatically

deserialized

# when loaded.

module ModelCacheHelper  
def self.included(klass)  
klass.extend ClassMethods  
end

module ClassMethods  
def check\_cache(url, args)  
c = ActionController::Base.new  
return yield unless c.perform\_caching

```
  url += args.map do |a|
    [String,Fixnum,Symbol,TrueClass,FalseClass].member?(a.class) ?

```

a.to\_s : nil  
end.join("-")

```
  unless f = c.read_fragment(url)
    res = yield
    c.write_fragment(url, res.is_a?(String) ? res : res.to_yaml)
    res
  else
    (f[0..3] == "--- ") ? YAML.load(f) : f
  end
end

def cache_method(method)
  controller = self.class_name.underscore.pluralize
  self.class_eval %Q{
    def self.#{method}(id, *args)
      check_cache("#{controller}/\#{id}-#{method}-", args) do
        self.find(id).#{method}_without_cache(*args)
      end
    end

    def #{method}_with_cache(*args)
      self.class.check_cache("#{controller}/\#{id}-#{method}-",

```

args) do  
#{method}\_without\_cache(\*args)  
end  
end  
alias\_method\_chain :#{method}, :cache  
}  
end  
end

def expire\_cache(method = false)  
c = ActionController::Base.new  
return unless c.perform\_caching  
controller = self.class.class\_name.underscore.pluralize  
puts “expire #{controller} - id #{self.id}”  
unless method  
c.expire\_fragment %r{#{controller}/#{self.id}-._}  
else  
c.expire\_fragment %r{#{controller}/#{self.id}-#{method}-._}  
end  
end  
end

---

<div class="post-metadata">

**Author:** ![Tobias\_Numiranta](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/4491bb/32.png) [@Tobias\_Numiranta](https://www.ruby-forum.com/u/Tobias_Numiranta)\
**Post date:** [May 8, 2007, 2:26pm UTC](https://www.ruby-forum.com/t/model-cache-instead-of-controller-cache/103611/2 "2007-05-08T14:26:28Z")

</div>

Tobias Numiranta wrote:

> 

Hi Tobias.

Do you have benchmarks for this that show a significant performance  
improvement over re-calling the DB?

I believe that EDGE Rails (and maybe the current version now) have model  
caching, simply storing DB calls in case the call is made again within  
the same transaction. I believe however that you are keeping your cache  
for multiple transactions.

---

<div class="post-metadata">

**Author:** ![Tobias\_Numiranta](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/4491bb/32.png) [@Tobias\_Numiranta](https://www.ruby-forum.com/u/Tobias_Numiranta)\
**Post date:** [May 11, 2007, 10:46am UTC](https://www.ruby-forum.com/t/model-cache-instead-of-controller-cache/103611/3 "2007-05-11T10:46:51Z")

</div>

Hi. Yes, I get nice performance improvements since some of the model  
methods takes a few seconds with a disabled cache. Seems like a nice  
feature when you have database-intensive method calls.

Yes, EDGE seems to have some model caching, but only for a code-block.  
Instead, these results are cached until you explicitly call  
expire\_cache, just like the cache in a controller.

On May 8, 2:26 pm, Douglas S. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)

---

<div class="post-metadata">

**Author:** ![Tobias\_Numiranta](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/t/4491bb/32.png) [@Tobias\_Numiranta](https://www.ruby-forum.com/u/Tobias_Numiranta)\
**Post date:** [May 11, 2007, 12:34pm UTC](https://www.ruby-forum.com/t/model-cache-instead-of-controller-cache/103611/4 "2007-05-11T12:34:05Z")

</div>

Tobias,

A tiny detail but if you add

```
ActiveRecord::Base.send(:include, ModelCacheHelper)

```

in environment.rb, you’ll no longer have to explicitely include it,  
and you’d just have to write:

```
cache_method :heavy_calculation

```

## Alain R.

[blog.ravet.com](http://blog.ravet.com)
