Skip to content

Instantly share code, notes, and snippets.

@andrewberls
Last active August 29, 2015 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save andrewberls/60cc7cf0a655570012b2 to your computer and use it in GitHub Desktop.
Save andrewberls/60cc7cf0a655570012b2 to your computer and use it in GitHub Desktop.
# lib/cache_proxy.rb
class CacheProxy
class << self
attr_accessor :cache
end
def initialize(obj)
@obj = obj
end
def cache_key(meth)
"#{self.class}:#{@obj.class}:#{@obj.object_id}:#{meth}"
end
def cache_fetch(meth, *args, &block)
key = cache_key(meth)
self.class.cache.cache_fetch(key, expires_in: 1.hour) do
@obj.__send__(meth, *args, &block)
end
end
def method_missing(meth, *args, &block)
if @obj.respond_to?(meth)
cache_fetch(meth, *args, &block)
else
super
end
end
end
module CacheProxyMethods
def cached
CacheProxy.new(self)
end
end
# config/initializers/cache_proxy.rb
require 'cache_proxy'
ActiveSupport.on_load(:redis) do
CacheProxy.cache = $redis
ActiveRecord::Base.send(:include, CacheProxyMethods)
end
# config/initializers/redis.rb
$redis = Redis.new
class << $redis
# Include client helper extensions, which implement cache_fetch among others
include RedisClientExtensions
end
ActiveSupport.run_load_hooks(:redis, self)
module RedisClientExtensions
# Get/set a value cached in a key
#
# key - String key name
# expires_in: Integer TTL of the key, in seconds
# block - Proc to compute the value to be cached on miss
#
# Returns result of evaluating <block>
def cache_fetch(key, expires_in:, &block)
if ret = get(key)
ret
else
val = block.call
set(key, val)
expire(key, expires_in)
val
end
end
end
@andrewberls
Copy link
Author

An experiment to transparently cache methods independent of their implementation. Example:

val = my_object.cached.expensive_computation # Stores value in cache
...
object.cached.expensive_computation # Retrieves from cache - doesn't actually call method!

Calling cached on an object will return a corresponding CacheProxy:

Post.last.cached
#<CacheProxy:0x007f9a04d65420 @obj=#<Post ...>"

Calling methods on this cache proxy will first check the cache for a value, and only call the actual method if a value is missing. Note this is accomplished using the cache_fetch method on the configurable cache backend, which is responsible for implementing this (we've added the cache_fetch method to the Redis client, although any interface that implements cache_fetch will do)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment