Forum: Ruby on Rails Model Cache instead of Controller Cache.

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
B1429052f9d298f0bc0f2c0c6447cbdb?d=identicon&s=25 Tobias Numiranta (Guest)
on 2007-05-08 13:12
(Received via mailing list)
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
B1edc6a4aa373d980b49e600923df552?d=identicon&s=25 Douglas Shearer (dougal)
on 2007-05-08 14:26
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.
B1429052f9d298f0bc0f2c0c6447cbdb?d=identicon&s=25 Tobias Numiranta (Guest)
on 2007-05-11 10:46
(Received via mailing list)
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 Shearer <rails-mailing-l...@andreas-s.net>
Ba3a00606eb530dcab2c4a6a59bf366d?d=identicon&s=25 Alain Ravet (Guest)
on 2007-05-11 12:34
(Received via mailing list)
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 Ravet
----
blog.ravet.com
This topic is locked and can not be replied to.